Completed
Push — master ( 59b76b...dd409d )
by Tim
30:43 queued 15:38
created

FocusCropService::getTempImageFolder()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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