Passed
Push — master ( 7d5600...3f0926 )
by Rob
01:54
created

Image::generateFileName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 15
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 21
rs 9.7666
1
<?php
2
3
namespace Rvdlee\ZfImageResizer\Model;
4
5
use Rvdlee\ZfImageResizer\Exception\InvalidArgumentException;
6
use SplFileInfo;
7
8
class Image
9
{
10
    const ONLY_RESIZE_MODUS     = 'resize';
11
    const CROP_AND_RESIZE_MODUS = 'crop-and-resize';
12
    const ONLY_CROP_MODUS       = 'crop';
13
    const MODUS                 = [
14
        self::ONLY_RESIZE_MODUS,
15
        self::CROP_AND_RESIZE_MODUS,
16
        self::ONLY_CROP_MODUS,
17
    ];
18
19
    const MANUAL_CROP       = 'manual';
20
    const UPPER_LEFT_CROP   = 'upper-left';
21
    const UPPER_MIDDLE_CROP = 'upper-middle';
22
    const UPPER_RIGHT_CROP  = 'upper-right';
23
    const MIDDLE_LEFT_CROP  = 'middle-left';
24
    const CENTERED_CROP     = 'centered';
25
    const MIDDLE_RIGHT_CROP = 'middle-right';
26
    const BOTTOM_LEFT       = 'bottom-left';
27
    const BOTTOM_MIDDLE     = 'bottom-middle';
28
    const BOTTOM_RIGHT      = 'bottom-right';
29
    const CROP_MODUS        = [
30
        self::MANUAL_CROP,
31
        self::UPPER_LEFT_CROP,
32
        self::UPPER_MIDDLE_CROP,
33
        self::UPPER_RIGHT_CROP,
34
        self::MIDDLE_LEFT_CROP,
35
        self::CENTERED_CROP,
36
        self::MIDDLE_RIGHT_CROP,
37
        self::BOTTOM_LEFT,
38
        self::BOTTOM_MIDDLE,
39
        self::BOTTOM_RIGHT,
40
    ];
41
42
    /**
43
     * @var string
44
     */
45
    protected $path;
46
47
    /**
48
     * @var string
49
     */
50
    protected $targetPath;
51
52
    /**
53
     * @var SplFileInfo
54
     */
55
    protected $splFileInfo;
56
57
    /**
58
     * @var int
59
     */
60
    protected $originalWidth;
61
62
    /**
63
     * @var int
64
     */
65
    protected $originalHeight;
66
67
    /**
68
     * @var int
69
     */
70
    protected $targetWidth = 0;
71
72
    /**
73
     * @var int
74
     */
75
    protected $targetHeight = 0;
76
77
    /**
78
     * @var int
79
     */
80
    protected $cropPositionX = 0;
81
82
    /**
83
     * @var int
84
     */
85
    protected $cropPositionY = 0;
86
87
    /**
88
     * Image constructor.
89
     *
90
     * @param string $path
91
     * @param int    $targetWidth
92
     * @param int    $targetHeight
93
     * @param string $cropMode
94
     * @param int    $x
95
     * @param int    $y
96
     * @throws InvalidArgumentException
97
     */
98
    public function __construct(string $path, int $targetWidth, int $targetHeight, string $cropMode = self::CENTERED_CROP, int $x = 0, int $y = 0)
99
    {
100
        // Check if given path exists
101
        if (!file_exists($path)) {
102
            throw new InvalidArgumentException(sprintf('The provided file(%s) does not exist.', $path));
103
        }
104
105
        // Get the original image dimensions and calculate ratio
106
        list($width, $height) = getimagesize($path);
107
        $ratio = ($width < $height ? $width : $height) / ($width > $height ? $width : $height);
108
109
        // If either of these is not set, scale to ratio
110
        if ($targetWidth === 0 || $targetHeight === 0) {
111
            if ($targetWidth === 0) {
112
                $targetWidth = $width * $ratio;
113
            }
114
115
            if ($targetHeight === 0) {
116
                $targetHeight = $height * $ratio;
117
            }
118
        }
119
120
        // Prep this model
121
        $this->setPath($path)
122
             ->setSplFileInfo(new SplFileInfo($path))
123
             ->setOriginalWidth($width)
124
             ->setOriginalHeight($height)
125
             ->setTargetWidth($targetWidth)
126
             ->setTargetHeight($targetHeight)
127
             ->calculateCropPositions($cropMode, $x, $y)
128
             ->setTargetPath($this->generateFileName());
129
    }
130
131
    /**
132
     * Generate a new unique name based on all the image parameters
133
     *
134
     * @return mixed
135
     */
136
    public function generateFileName()
137
    {
138
        // Create a new unique name based on the image parameters
139
        return str_replace(
140
            $this->getSplFileInfo()->getFilename(),
141
            sprintf(
142
                '%s.%s',
143
                hash(
144
                    'sha256',
145
                    sprintf(
146
                        '%s%d%d%d%d',
147
                        explode('.', $this->getSplFileInfo()->getFilename())[0],
148
                        $this->getTargetWidth(),
149
                        $this->getTargetHeight(),
150
                        $this->getCropPositionX(),
151
                        $this->getCropPositionY()
152
                    )
153
                ),
154
                $this->getSplFileInfo()->getExtension()
155
            ),
156
            $this->getPath()
157
        );
158
    }
159
160
161
    /**
162
     * @param string $cropMode
163
     * @param int    $cropPositionX
164
     * @param int    $cropPositionY
165
     * @return Image
166
     * @throws InvalidArgumentException
167
     */
168
    public function calculateCropPositions(
169
        string $cropMode = self::CENTERED_CROP,
170
        int $cropPositionX = 0,
171
        int $cropPositionY = 0
172
    ): Image {
173
        // Check if we've recieved a valid crop modus
174
        if (!in_array($cropMode, self::CROP_MODUS)) {
175
            throw new InvalidArgumentException(
176
                sprintf(
177
                    'Crop mode \'%s\' is not a valid mode. Choose one of the following %s',
178
                    $cropMode,
179
                    implode(self::CROP_MODUS)
180
                )
181
            );
182
        }
183
184
        // If we want to manually control the crop just set it and return this (fluent interface)
185
        if ($cropMode === self::MANUAL_CROP) {
186
            return $this->setCropPositionX($cropPositionX)->setCropPositionY($cropPositionY);
187
        }
188
189
        // These need to be set with actual values
190
        if ($this->getTargetWidth() === 0 || $this->getTargetHeight() === 0) {
191
            throw new InvalidArgumentException('TargetWidth and TargetHeight cannot be 0.');
192
        }
193
194
        // Do some automatic (lazy) crop calculations
195
        switch ($cropMode) {
196
            case self::UPPER_LEFT_CROP:
197
                $cropPositionX = 0;
198
                $cropPositionY = 0;
199
            break;
200
            case self::UPPER_MIDDLE_CROP:
201
                $cropPositionX = ($this->getOriginalWidth() / 2) - ($this->getTargetWidth() / 2);
202
                $cropPositionY = 0;
203
            break;
204
            case self::UPPER_RIGHT_CROP:
205
                $cropPositionX = $this->getOriginalWidth() - $this->getTargetWidth();
206
                $cropPositionY = 0;
207
            break;
208
            case self::MIDDLE_LEFT_CROP:
209
                $cropPositionX = 0;
210
                $cropPositionY = ($this->getOriginalHeight() / 2) - ($this->getTargetHeight() / 2);
211
            break;
212
            case self::CENTERED_CROP:
213
                $cropPositionX = ($this->getOriginalWidth() / 2) - ($this->getTargetWidth() / 2);
214
                $cropPositionY = ($this->getOriginalHeight() / 2) - ($this->getTargetHeight() / 2);
215
            break;
216
            case self::MIDDLE_RIGHT_CROP:
217
                $cropPositionX = $this->getOriginalWidth() - $this->getTargetWidth();
218
                $cropPositionY = ($this->getOriginalHeight() / 2) - ($this->getTargetHeight() / 2);
219
            break;
220
            case self::BOTTOM_LEFT:
221
                $cropPositionX = 0;
222
                $cropPositionY = $this->getOriginalHeight() - $this->getTargetHeight();
223
            break;
224
            case self::BOTTOM_MIDDLE:
225
                $cropPositionX = ($this->getOriginalWidth() / 2) - ($this->getTargetWidth() / 2);
226
                $cropPositionY = $this->getOriginalHeight() - $this->getTargetHeight();
227
            break;
228
            case self::BOTTOM_RIGHT:
229
                $cropPositionX = $this->getOriginalWidth() - $this->getTargetWidth();
230
                $cropPositionY = $this->getOriginalHeight() - $this->getTargetHeight();
231
            break;
232
        }
233
234
        // Set the calculated crop positions and return this (fluent interface)
235
        return $this->setCropPositionX($cropPositionX)->setCropPositionY($cropPositionY);
236
    }
237
238
    /**
239
     * @return string
240
     */
241
    public function getPath(): string
242
    {
243
        return $this->path;
244
    }
245
246
    /**
247
     * @param string $path
248
     *
249
     * @return Image
250
     */
251
    public function setPath(string $path): Image
252
    {
253
        $this->path = $path;
254
255
        return $this;
256
    }
257
258
    /**
259
     * @return string
260
     */
261
    public function getTargetPath(): string
262
    {
263
        return $this->targetPath;
264
    }
265
266
    /**
267
     * @param string $targetPath
268
     * @return Image
269
     */
270
    public function setTargetPath(string $targetPath): Image
271
    {
272
        $this->targetPath = $targetPath;
273
        return $this;
274
    }
275
276
    /**
277
     * @return SplFileInfo
278
     */
279
    public function getSplFileInfo(): SplFileInfo
280
    {
281
        return $this->splFileInfo;
282
    }
283
284
    /**
285
     * @param SplFileInfo $splFileInfo
286
     *
287
     * @return Image
288
     */
289
    public function setSplFileInfo(SplFileInfo $splFileInfo): Image
290
    {
291
        $this->splFileInfo = $splFileInfo;
292
293
        return $this;
294
    }
295
296
    /**
297
     * @return int
298
     */
299
    public function getTargetWidth(): int
300
    {
301
        return $this->targetWidth;
302
    }
303
304
    /**
305
     * @param int $targetWidth
306
     * @return Image
307
     */
308
    public function setTargetWidth(int $targetWidth): Image
309
    {
310
        $this->targetWidth = $targetWidth;
311
        return $this;
312
    }
313
314
    /**
315
     * @return int
316
     */
317
    public function getOriginalWidth(): int
318
    {
319
        return $this->originalWidth;
320
    }
321
322
    /**
323
     * @param int $originalWidth
324
     * @return Image
325
     */
326
    public function setOriginalWidth(int $originalWidth): Image
327
    {
328
        $this->originalWidth = $originalWidth;
329
        return $this;
330
    }
331
332
    /**
333
     * @return int
334
     */
335
    public function getOriginalHeight(): int
336
    {
337
        return $this->originalHeight;
338
    }
339
340
    /**
341
     * @param int $originalHeight
342
     * @return Image
343
     */
344
    public function setOriginalHeight(int $originalHeight): Image
345
    {
346
        $this->originalHeight = $originalHeight;
347
        return $this;
348
    }
349
350
    /**
351
     * @return int
352
     */
353
    public function getTargetHeight(): int
354
    {
355
        return $this->targetHeight;
356
    }
357
358
    /**
359
     * @param int $targetHeight
360
     * @return Image
361
     */
362
    public function setTargetHeight(int $targetHeight): Image
363
    {
364
        $this->targetHeight = $targetHeight;
365
        return $this;
366
    }
367
368
    /**
369
     * @return int
370
     */
371
    public function getCropPositionX(): int
372
    {
373
        return $this->cropPositionX;
374
    }
375
376
    /**
377
     * @param int $cropPositionX
378
     * @return Image
379
     */
380
    public function setCropPositionX(int $cropPositionX): Image
381
    {
382
        $this->cropPositionX = $cropPositionX;
383
        return $this;
384
    }
385
386
    /**
387
     * @return int
388
     */
389
    public function getCropPositionY(): int
390
    {
391
        return $this->cropPositionY;
392
    }
393
394
    /**
395
     * @param int $cropPositionY
396
     * @return Image
397
     */
398
    public function setCropPositionY(int $cropPositionY): Image
399
    {
400
        $this->cropPositionY = $cropPositionY;
401
        return $this;
402
    }
403
}