Completed
Push — master ( 76a568...502745 )
by Tim
35:07 queued 20:50
created

FocusCropService::generateTempImageName()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 28
rs 8.8571
cc 3
eloc 20
nc 3
nop 4
1
<?php
2
/**
3
 * Crop images via focus crop
4
 *
5
 * @package Focuspoint\Service
6
 * @author  Tim Lochmüller
7
 */
8
9
namespace HDNET\Focuspoint\Service;
10
11
use HDNET\Focuspoint\Service\WizardHandler\Group;
12
use HDNET\Focuspoint\Utility\GlobalUtility;
13
use TYPO3\CMS\Core\Resource\FileInterface;
14
use TYPO3\CMS\Core\Resource\FileReference as CoreFileReference;
15
use TYPO3\CMS\Core\Resource\ResourceFactory;
16
use TYPO3\CMS\Core\Utility\GeneralUtility;
17
use TYPO3\CMS\Core\Utility\MathUtility;
18
use TYPO3\CMS\Core\Utility\PathUtility;
19
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
20
use TYPO3\CMS\Extbase\Object\ObjectManager;
21
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
22
23
/**
24
 * Crop images via focus crop
25
 *
26
 * @author Tim Lochmüller
27
 */
28
class FocusCropService extends AbstractService
29
{
30
31
    const SIGNAL_tempImageCropped = 'tempImageCropped';
32
33
    /**
34
     * @var Dispatcher
35
     */
36
    protected $signalSlotDispatcher;
37
38
    /**
39
     * get the image
40
     *
41
     * @param $src
42
     * @param $image
43
     * @param $treatIdAsReference
44
     *
45
     * @return \TYPO3\CMS\Core\Resource\File|FileInterface|CoreFileReference|\TYPO3\CMS\Core\Resource\Folder
46
     * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
47
     * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
48
     */
49
    public function getViewHelperImage($src, $image, $treatIdAsReference)
50
    {
51
        $resourceFactory = ResourceFactory::getInstance();
52
        if ($image instanceof FileReference) {
53
            $image = $image->getOriginalResource();
54
        }
55
        if ($image instanceof CoreFileReference) {
56
            return $image->getOriginalFile();
57
        }
58
        if (!MathUtility::canBeInterpretedAsInteger($src)) {
59
            return $resourceFactory->retrieveFileOrFolderObject($src);
60
        }
61
        if (!$treatIdAsReference) {
62
            return $resourceFactory->getFileObject($src);
63
        }
64
        $image = $resourceFactory->getFileReferenceObject($src);
65
        return $image->getOriginalFile();
66
    }
67
68
    /**
69
     * Helper function for view helpers
70
     *
71
     * @param $src
72
     * @param $image
73
     * @param $treatIdAsReference
74
     * @param $ratio
75
     *
76
     * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
77
     * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
78
     *
79
     * @return string
80
     */
81
    public function getCroppedImageSrcForViewHelper($src, $image, $treatIdAsReference, $ratio)
82
    {
83
        $file = $this->getViewHelperImage($src, $image, $treatIdAsReference);
84
        return $this->getCroppedImageSrcByFile($file, $ratio);
0 ignored issues
show
Bug introduced by
It seems like $file defined by $this->getViewHelperImag...e, $treatIdAsReference) on line 83 can also be of type object<TYPO3\CMS\Core\Resource\Folder>; however, HDNET\Focuspoint\Service...CroppedImageSrcByFile() does only seem to accept object<TYPO3\CMS\Core\Resource\FileInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
85
    }
86
87
    /**
88
     * Get the cropped image
89
     *
90
     * @param string $fileReference
91
     * @param string $ratio
92
     *
93
     * @return string The new filename
94
     */
95
    public function getCroppedImageSrcByFileReference($fileReference, $ratio)
96
    {
97
        if ($fileReference instanceof FileReference) {
98
            $fileReference = $fileReference->getOriginalResource();
99
        }
100
        if ($fileReference instanceof CoreFileReference) {
101
            return $this->getCroppedImageSrcByFile($fileReference->getOriginalFile(), $ratio);
102
        }
103
        throw new \InvalidArgumentException('The given argument is not a valid file reference', 1475144027);
104
    }
105
106
    /**
107
     * Get the cropped image by File Object
108
     *
109
     * @param FileInterface $file
110
     * @param string $ratio
111
     *
112
     * @return string The new filename
113
     */
114
    public function getCroppedImageSrcByFile(FileInterface $file, $ratio)
115
    {
116
        $result = $this->getCroppedImageSrcBySrc(
117
            $file->getForLocalProcessing(false),
118
            $ratio,
119
            $file->getProperty('focus_point_x'),
120
            $file->getProperty('focus_point_y')
121
        );
122
        if ($result === null) {
123
            return $file->getPublicUrl();
124
        }
125
        return $result;
126
    }
127
128
129
    /**
130
     * Get the cropped image by src
131
     *
132
     * @param string $src Relative file name
133
     * @param string $ratio
134
     * @param int $x
135
     * @param int $y
136
     *
137
     * @return string The new filename
138
     */
139
    public function getCroppedImageSrcBySrc($src, $ratio, $x, $y)
140
    {
141
        $absoluteImageName = GeneralUtility::getFileAbsFileName($src);
142
        if (!is_file($absoluteImageName)) {
143
            return null;
144
        }
145
        $docRoot = rtrim(GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT'), '/') . '/';
146
        $relativeSrc = str_replace($docRoot, '', $absoluteImageName);
147
        $focusPointX = MathUtility::forceIntegerInRange((int)$x, -100, 100, 0);
148
        $focusPointY = MathUtility::forceIntegerInRange((int)$y, -100, 100, 0);
149
150
        if ($focusPointX === 0 && $focusPointY === 0) {
151
            $connection = GlobalUtility::getDatabaseConnection();
152
            $row = $connection->exec_SELECTgetSingleRow(
153
                'uid,focus_point_x,focus_point_y',
154
                Group::TABLE,
155
                'relative_file_path = ' . $connection->fullQuoteStr($relativeSrc, Group::TABLE)
156
            );
157
            if ($row) {
158
                $focusPointX = MathUtility::forceIntegerInRange((int)$row['focus_point_x'], -100, 100, 0);
159
                $focusPointY = MathUtility::forceIntegerInRange((int)$row['focus_point_y'], -100, 100, 0);
160
            }
161
        }
162
163
        $tempImageFolder = 'typo3temp/focuscrop/';
164
        $tempImageName = $this->generateTempImageName($absoluteImageName, $ratio, $focusPointX, $focusPointY);
165
        $tempImageName = $tempImageFolder . $tempImageName;
166
167
        $absoluteTempImageName = GeneralUtility::getFileAbsFileName($tempImageName);
168
169
        if (is_file($absoluteTempImageName)) {
170
            return $tempImageName;
171
        }
172
173
        $absoluteTempImageFolder = GeneralUtility::getFileAbsFileName($tempImageFolder);
174
        if (!is_dir($absoluteTempImageFolder)) {
175
            GeneralUtility::mkdir_deep($absoluteTempImageFolder);
176
        }
177
178
        $imageSizeInformation = getimagesize($absoluteImageName);
179
        $width = $imageSizeInformation[0];
180
        $height = $imageSizeInformation[1];
181
182
        // dimensions
183
        /** @var DimensionService $dimensionService */
184
        $dimensionService = GeneralUtility::makeInstance(DimensionService::class);
185
        list($focusWidth, $focusHeight) = $dimensionService->getFocusWidthAndHeight($width, $height, $ratio);
186
        $cropMode = $dimensionService->getCropMode($width, $height, $ratio);
187
        list($sourceX, $sourceY) = $dimensionService->calculateSourcePosition(
188
            $cropMode,
189
            $width,
190
            $height,
191
            $focusWidth,
192
            $focusHeight,
193
            $focusPointX,
194
            $focusPointY
195
        );
196
197
        $cropService = GeneralUtility::makeInstance(CropService::class);
198
        $cropService->createImage(
199
            $absoluteImageName,
200
            $focusWidth,
201
            $focusHeight,
202
            $sourceX,
203
            $sourceY,
204
            $absoluteTempImageName
205
        );
206
207
        $this->emitTempImageCropped($tempImageName);
208
209
        return $tempImageName;
210
    }
211
212
    /**
213
     * Emit tempImageCropped signal
214
     *
215
     * @param string $tempImageName
216
     */
217
    protected function emitTempImageCropped($tempImageName)
218
    {
219
        $this->getSignalSlotDispatcher()->dispatch(__CLASS__, self::SIGNAL_tempImageCropped, [$tempImageName]);
220
    }
221
222
    /**
223
     * Get the SignalSlot dispatcher.
224
     *
225
     * @return Dispatcher
226
     */
227
    protected function getSignalSlotDispatcher()
228
    {
229
        if (!isset($this->signalSlotDispatcher)) {
230
            $this->signalSlotDispatcher = $this->getObjectManager()->get(Dispatcher::class);
231
        }
232
        return $this->signalSlotDispatcher;
233
    }
234
235
    /**
236
     * Gets the ObjectManager.
237
     *
238
     * @return ObjectManager
239
     */
240
    protected function getObjectManager()
241
    {
242
        return GeneralUtility::makeInstance(ObjectManager::class);
243
    }
244
245
    /**
246
     *
247
     * @param $absoluteImageName
248
     * @param $ratio
249
     * @param $focusPointX
250
     * @param $focusPointY
251
     * @return array
252
     */
253
    protected function generateTempImageName($absoluteImageName, $ratio, $focusPointX, $focusPointY)
254
    {
255
        $name = '';
256
257
        list($name) = $this->getSignalSlotDispatcher()->dispatch(__CLASS__, __FUNCTION__, [
258
            $name,
259
            $absoluteImageName,
260
            $ratio,
261
            $focusPointX,
262
            $focusPointY,
263
        ]);
264
265
        if ($name) {
266
            return $name;
267
        }
268
269
        $hash = function_exists('sha1_file') ? sha1_file($absoluteImageName) : md5_file($absoluteImageName);
270
        $name = $hash . '-fp-' . preg_replace(
271
                '/[^0-9a-z-]/',
272
                '-',
273
                $ratio
274
            ) . '-' . $focusPointX . '-' . $focusPointY . '.' . PathUtility::pathinfo(
275
                $absoluteImageName,
276
                PATHINFO_EXTENSION
277
            );
278
        $name = preg_replace('/--+/', '-', $name);
279
        return $name;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $name; (string) is incompatible with the return type documented by HDNET\Focuspoint\Service...::generateTempImageName of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
280
    }
281
}
282