Completed
Push — master ( e21f4f...0f6799 )
by Tim
13s
created

FocusCropService::getViewHelperImage()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 18
rs 8.8571
cc 5
eloc 12
nc 5
nop 3
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
     * @var string
40
     */
41
    protected $tempImageFolder;
42
43
    /**
44
     * get the image
45
     *
46
     * @param $src
47
     * @param $image
48
     * @param $treatIdAsReference
49
     *
50
     * @return \TYPO3\CMS\Core\Resource\File|FileInterface|CoreFileReference|\TYPO3\CMS\Core\Resource\Folder
51
     * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
52
     * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
53
     */
54
    public function getViewHelperImage($src, $image, $treatIdAsReference)
55
    {
56
        $resourceFactory = ResourceFactory::getInstance();
57
        if ($image instanceof \TYPO3\CMS\Core\Resource\FileReference) {
58
            return $image;
59
        }
60
        if ($image instanceof FileReference) {
61
            return $image->getOriginalResource();
62
        }
63
        if (!MathUtility::canBeInterpretedAsInteger($src)) {
64
            return $resourceFactory->retrieveFileOrFolderObject($src);
65
        }
66
        if (!$treatIdAsReference) {
67
            return $resourceFactory->getFileObject($src);
68
        }
69
        $image = $resourceFactory->getFileReferenceObject($src);
70
        return $image->getOriginalFile();
71
    }
72
73
    /**
74
     * Helper function for view helpers
75
     *
76
     * @param $src
77
     * @param $image
78
     * @param $treatIdAsReference
79
     * @param $ratio
80
     *
81
     * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
82
     * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
83
     *
84
     * @return string
85
     */
86
    public function getCroppedImageSrcForViewHelper($src, $image, $treatIdAsReference, $ratio)
87
    {
88
        $file = $this->getViewHelperImage($src, $image, $treatIdAsReference);
89
        return $this->getCroppedImageSrcByFile($file, $ratio);
0 ignored issues
show
Bug introduced by
It seems like $file defined by $this->getViewHelperImag...e, $treatIdAsReference) on line 88 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...
90
    }
91
92
    /**
93
     * Get the cropped image
94
     *
95
     * @param string $fileReference
96
     * @param string $ratio
97
     *
98
     * @return string The new filename
99
     */
100
    public function getCroppedImageSrcByFileReference($fileReference, $ratio)
101
    {
102
        if ($fileReference instanceof FileReference) {
103
            $fileReference = $fileReference->getOriginalResource();
104
        }
105
        if ($fileReference instanceof CoreFileReference) {
106
            return $this->getCroppedImageSrcByFile($fileReference->getOriginalFile(), $ratio);
107
        }
108
        throw new \InvalidArgumentException('The given argument is not a valid file reference', 1475144027);
109
    }
110
111
    /**
112
     * Get the cropped image by File Object
113
     *
114
     * @param FileInterface $file
115
     * @param string $ratio
116
     *
117
     * @return string The new filename
118
     */
119
    public function getCroppedImageSrcByFile(FileInterface $file, $ratio)
120
    {
121
        $result = $this->getCroppedImageSrcBySrc(
122
            $file->getForLocalProcessing(false),
123
            $ratio,
124
            $file->getProperty('focus_point_x'),
125
            $file->getProperty('focus_point_y')
126
        );
127
        if ($result === null) {
128
            return $file->getPublicUrl();
129
        }
130
        return $result;
131
    }
132
133
134
    /**
135
     * Get the cropped image by src
136
     *
137
     * @param string $src Relative file name
138
     * @param string $ratio
139
     * @param int $x
140
     * @param int $y
141
     *
142
     * @return string The new filename
143
     */
144
    public function getCroppedImageSrcBySrc($src, $ratio, $x, $y)
145
    {
146
        $absoluteImageName = GeneralUtility::getFileAbsFileName($src);
147
        if (!is_file($absoluteImageName)) {
148
            return null;
149
        }
150
        $docRoot = rtrim(GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT'), '/') . '/';
151
        $relativeSrc = str_replace($docRoot, '', $absoluteImageName);
152
        $focusPointX = MathUtility::forceIntegerInRange((int)$x, -100, 100, 0);
153
        $focusPointY = MathUtility::forceIntegerInRange((int)$y, -100, 100, 0);
154
155
        if ($focusPointX === 0 && $focusPointY === 0) {
156
            $connection = GlobalUtility::getDatabaseConnection();
157
            $row = $connection->exec_SELECTgetSingleRow(
158
                'uid,focus_point_x,focus_point_y',
159
                Group::TABLE,
160
                'relative_file_path = ' . $connection->fullQuoteStr($relativeSrc, Group::TABLE)
161
            );
162
            if ($row) {
163
                $focusPointX = MathUtility::forceIntegerInRange((int)$row['focus_point_x'], -100, 100, 0);
164
                $focusPointY = MathUtility::forceIntegerInRange((int)$row['focus_point_y'], -100, 100, 0);
165
            }
166
        }
167
168
        $tempImageFolder = $this->getTempImageFolder();
169
        $tempImageName = $this->generateTempImageName($absoluteImageName, $ratio, $focusPointX, $focusPointY);
170
        $tempImageName = $tempImageFolder . $tempImageName;
171
172
        $absoluteTempImageName = GeneralUtility::getFileAbsFileName($tempImageName);
173
174
        if (is_file($absoluteTempImageName)) {
175
            return $tempImageName;
176
        }
177
178
        $absoluteTempImageFolder = GeneralUtility::getFileAbsFileName($tempImageFolder);
179
        if (!is_dir($absoluteTempImageFolder)) {
180
            GeneralUtility::mkdir_deep($absoluteTempImageFolder);
181
        }
182
183
        $imageSizeInformation = getimagesize($absoluteImageName);
184
        $width = $imageSizeInformation[0];
185
        $height = $imageSizeInformation[1];
186
187
        // dimensions
188
        /** @var DimensionService $dimensionService */
189
        $dimensionService = GeneralUtility::makeInstance(DimensionService::class);
190
        list($focusWidth, $focusHeight) = $dimensionService->getFocusWidthAndHeight($width, $height, $ratio);
191
        $cropMode = $dimensionService->getCropMode($width, $height, $ratio);
192
        list($sourceX, $sourceY) = $dimensionService->calculateSourcePosition(
193
            $cropMode,
194
            $width,
195
            $height,
196
            $focusWidth,
197
            $focusHeight,
198
            $focusPointX,
199
            $focusPointY
200
        );
201
202
        $cropService = GeneralUtility::makeInstance(CropService::class);
203
        $cropService->createImage(
204
            $absoluteImageName,
205
            $focusWidth,
206
            $focusHeight,
207
            $sourceX,
208
            $sourceY,
209
            $absoluteTempImageName
210
        );
211
212
        $this->emitTempImageCropped($tempImageName);
213
214
        return $tempImageName;
215
    }
216
217
    /**
218
     * Emit tempImageCropped signal
219
     *
220
     * @param string $tempImageName
221
     */
222
    protected function emitTempImageCropped($tempImageName)
223
    {
224
        $this->getSignalSlotDispatcher()->dispatch(__CLASS__, self::SIGNAL_tempImageCropped, [$tempImageName]);
225
    }
226
227
    /**
228
     * Get the SignalSlot dispatcher.
229
     *
230
     * @return Dispatcher
231
     */
232
    protected function getSignalSlotDispatcher()
233
    {
234
        if (!isset($this->signalSlotDispatcher)) {
235
            $this->signalSlotDispatcher = $this->getObjectManager()->get(Dispatcher::class);
236
        }
237
        return $this->signalSlotDispatcher;
238
    }
239
240
    /**
241
     * Gets the ObjectManager.
242
     *
243
     * @return ObjectManager
244
     */
245
    protected function getObjectManager()
246
    {
247
        return GeneralUtility::makeInstance(ObjectManager::class);
248
    }
249
250
    /**
251
     *
252
     * @param $absoluteImageName
253
     * @param $ratio
254
     * @param $focusPointX
255
     * @param $focusPointY
256
     * @return array
257
     */
258
    protected function generateTempImageName($absoluteImageName, $ratio, $focusPointX, $focusPointY)
259
    {
260
        $name = '';
261
262
        list($name) = $this->getSignalSlotDispatcher()->dispatch(__CLASS__, __FUNCTION__, [
263
            $name,
264
            $absoluteImageName,
265
            $ratio,
266
            $focusPointX,
267
            $focusPointY,
268
        ]);
269
270
        if ($name) {
271
            return $name;
272
        }
273
274
        $hash = function_exists('sha1_file') ? sha1_file($absoluteImageName) : md5_file($absoluteImageName);
275
        $name = $hash . '-fp-' . preg_replace(
276
                '/[^0-9a-z-]/',
277
                '-',
278
                $ratio
279
            ) . '-' . $focusPointX . '-' . $focusPointY . '.' . PathUtility::pathinfo(
280
                $absoluteImageName,
281
                PATHINFO_EXTENSION
282
            );
283
        $name = preg_replace('/--+/', '-', $name);
284
        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...
285
    }
286
287
    /**
288
     * Return the folder for generated images
289
     *
290
     * @return string Path relative to PATH_site
291
     */
292
    protected function getTempImageFolder()
293
    {
294
        if ($this->tempImageFolder === null) {
295
            $extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['focuspoint']);
296
            if (isset($extConf['tempImageFolder'])) {
297
                $this->tempImageFolder = $extConf['tempImageFolder'];
298
            } else {
299
                $this->tempImageFolder = 'typo3temp/focuscrop/';
300
            }
301
        }
302
303
        return $this->tempImageFolder;
304
    }
305
}
306