Completed
Pull Request — master (#33)
by diego
14:36
created

FocusCropService::getViewHelperImage()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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