Test Failed
Push — master ( 96789e...6d2c50 )
by Brent
02:42
created

ResponsiveFactory::createScaledImages()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.439
c 0
b 0
f 0
cc 5
eloc 15
nc 6
nop 2
1
<?php
2
3
namespace Brendt\Image;
4
5
use Brendt\Image\Config\DefaultConfigurator;
6
use Brendt\Image\Config\ResponsiveFactoryConfigurator;
7
use Brendt\Image\Exception\FileNotFoundException;
8
use Brendt\Image\Scaler\Scaler;
9
use ImageOptimizer\Optimizer;
10
use ImageOptimizer\OptimizerFactory;
11
use Intervention\Image\ImageManager;
12
use Symfony\Component\Filesystem\Filesystem;
13
use Symfony\Component\Finder\Finder;
14
use Symfony\Component\Finder\SplFileInfo;
15
16
class ResponsiveFactory
17
{
18
19
    /**
20
     * The image driver to use.
21
     * Available drivers: 'gd' and 'imagick'.
22
     *
23
     * @var string
24
     */
25
    protected $driver;
26
27
    /**
28
     * The source path to load images from.
29
     *
30
     * @var string
31
     */
32
    protected $sourcePath;
33
34
    /**
35
     * The public path to save rendered images.
36
     *
37
     * @var string
38
     */
39
    protected $publicPath;
40
41
    /**
42
     * Enabled cache will stop generated images from being overwritten.
43
     *
44
     * @var bool
45
     */
46
    private $enableCache;
47
48
    /**
49
     * Enable optimizers will run several image optimizers on the saved files.
50
     *
51
     * @var bool
52
     */
53
    private $optimize;
54
55
    /**
56
     * @var bool
57
     */
58
    private $rebase;
59
60
    /**
61
     * @var array
62
     */
63
    private $optimizerOptions = [];
64
65
    /**
66
     * The Intervention image engine.
67
     *
68
     * @var ImageManager
69
     */
70
    protected $engine;
71
72
    /**
73
     * @var Filesystem
74
     */
75
    protected $fs;
76
77
    /**
78
     * @var Scaler
79
     */
80
    protected $scaler;
81
82
    /**
83
     * @var Optimizer
84
     */
85
    protected $optimizer;
86
87
    /**
88
     * ResponsiveFactory constructor.
89
     *
90
     * @param ResponsiveFactoryConfigurator $configurator
91
     */
92
    public function __construct(ResponsiveFactoryConfigurator $configurator = null) {
93
        $configurator = $configurator ?? new DefaultConfigurator();
94
        $configurator->configure($this);
95
96
        $this->sourcePath = rtrim($this->sourcePath, '/');
97
        $this->publicPath = rtrim($this->publicPath, '/');
98
99
        $this->engine = new ImageManager([
100
            'driver' => $this->driver,
101
        ]);
102
103
        $this->optimizer = (new OptimizerFactory($this->optimizerOptions))->get();
104
        $this->fs = new Filesystem();
105
106
        if (!$this->fs->exists($this->publicPath)) {
107
            $this->fs->mkdir($this->publicPath);
108
        }
109
    }
110
111
    /**
112
     * @param string $src
113
     *
114
     * @return ResponsiveImage
115
     * @throws FileNotFoundException
116
     */
117
    public function create($src) {
118
        $responsiveImage = new ResponsiveImage($src);
119
        $src = $responsiveImage->src();
120
        $sourcePath = $src;
121
        if ($this->rebase) {
122
            $sourcePath = pathinfo($src, PATHINFO_BASENAME);
123
        }
124
        
125
        $sourceImage = $this->getImageFile($this->sourcePath, $sourcePath);
126
127
        if (!$sourceImage) {
128
            throw new FileNotFoundException("{$this->sourcePath}/{$sourcePath}");
129
        }
130
131
        $extension = pathinfo($sourceImage->getExtension(), PATHINFO_EXTENSION);
132
        $fileName = pathinfo($sourceImage->getFilename(), PATHINFO_FILENAME);
133
        $publicImagePath = "{$this->publicPath}/{$src}";
134
        $urlPath = '/' . trim(pathinfo($src, PATHINFO_DIRNAME), '/');
135
136
        $responsiveImage->setExtension($sourceImage->getExtension());
137
        $responsiveImage->setFileName($fileName);
138
        $responsiveImage->setUrlPath($urlPath);
139
140
        if ($this->enableCache && $this->fs->exists($publicImagePath)) {
141
            /** @var SplFileInfo[] $cachedFiles */
142
            $cachedFiles = Finder::create()->files()->in("{$this->publicPath}/{$sourceImage->getRelativePath()}")->name("{$fileName}-*.{$extension}");
143
144
            foreach ($cachedFiles as $cachedFile) {
145
                $cachedFilename = $cachedFile->getFilename();
146
                $size = (int) str_replace(".{$extension}", '', str_replace("{$fileName}-", '', $cachedFilename));
147
148
                $responsiveImage->addSource("{$urlPath}/{$cachedFilename}", $size);
149
            }
150
151
            return $responsiveImage;
152
        }
153
154
        if (!$this->enableCache || !$this->fs->exists($publicImagePath)) {
155
            $this->fs->dumpFile($publicImagePath, $sourceImage->getContents());
156
        }
157
158
        $imageObject = $this->engine->make($sourceImage->getPathname());
159
160
        // TODO: This piece of code should be added as a size and not as a "default".
161
        // It's because the WidthScaler skips the default size.
162
        $width = $imageObject->getWidth();
163
        $responsiveImage->addSource($src, $width);
164
        $imageObject->destroy();
165
166
        $this->createScaledImages($sourceImage, $responsiveImage);
167
        
168
        return $responsiveImage;
169
    }
170
171
    /**
172
     * Create scaled image files and add them as sources to a Responsive Image, based on an array of file sizes:
173
     * [
174
     *      width => height,
175
     *      ...
176
     * ]
177
     *
178
     * @param SplFileInfo     $sourceImage
179
     * @param ResponsiveImage $responsiveImage
180
     *
181
     * @return ResponsiveImage
182
     */
183
    public function createScaledImages(SplFileInfo $sourceImage, ResponsiveImage $responsiveImage) : ResponsiveImage {
184
        $imageObject = $this->engine->make($sourceImage->getPathname());
185
        $urlPath = $responsiveImage->getUrlPath();
186
        $sizes = $this->scaler->scale($sourceImage, $imageObject);
187
188
        foreach ($sizes as $width => $height) {
189
            $scaledFileSrc = trim("{$urlPath}/{$imageObject->filename}-{$width}.{$imageObject->extension}", '/');
190
            $scaledFilePath = "{$this->getPublicPath()}/{$scaledFileSrc}";
191
            $responsiveImage->addSource($scaledFileSrc, $width);
192
193
            $scaledImage = $imageObject->resize((int) $width, (int) $height)->encode($imageObject->extension);
194
195
            if (!$this->enableCache || !$this->fs->exists($scaledFilePath)) {
196
                $this->fs->dumpFile($scaledFilePath, $scaledImage);
197
            }
198
        }
199
200
        $imageObject->destroy();
201
202
        if ($this->optimize) {
203
            $this->optimizeResponsiveImage($responsiveImage);
204
        }
205
206
        return $responsiveImage;
207
    }
208
209
    /**
210
     * Optimize all sources of a Responsive Image
211
     *
212
     * @param ResponsiveImage $responsiveImage
213
     *
214
     * @return ResponsiveImage
215
     */
216
    private function optimizeResponsiveImage(ResponsiveImage $responsiveImage) : ResponsiveImage {
217
        foreach ($responsiveImage->getSrcset() as $imageFile) {
218
            $this->optimizer->optimize("{$this->publicPath}/{$imageFile}");
219
        }
220
221
        return $responsiveImage;
222
    }
223
224
    /**
225
     * @param string $directory
226
     * @param string $path
227
     *
228
     * @return SplFileInfo
229
     */
230
    private function getImageFile(string $directory, string $path) : ?SplFileInfo {
231
        $iterator = Finder::create()->files()->in($directory)->path(ltrim($path, '/'))->getIterator();
232
        $iterator->rewind();
233
234
        return $iterator->current();
235
    }
236
237
    /**
238
     * @param string $driver
239
     *
240
     * @return ResponsiveFactory
241
     */
242
    public function setDriver($driver) : ResponsiveFactory {
243
        $this->driver = $driver;
244
245
        return $this;
246
    }
247
248
    /**
249
     * @param string $publicPath
250
     *
251
     * @return ResponsiveFactory
252
     */
253
    public function setPublicPath($publicPath) : ResponsiveFactory {
254
        $this->publicPath = $publicPath;
255
256
        return $this;
257
    }
258
259
    /**
260
     * @param boolean $enableCache
261
     *
262
     * @return ResponsiveFactory
263
     */
264
    public function setEnableCache($enableCache) : ResponsiveFactory {
265
        $this->enableCache = $enableCache;
266
267
        return $this;
268
    }
269
270
    /**
271
     * @param string $sourcePath
272
     *
273
     * @return ResponsiveFactory
274
     */
275
    public function setSourcePath($sourcePath) : ResponsiveFactory {
276
        $this->sourcePath = $sourcePath;
277
278
        return $this;
279
    }
280
281
    /**
282
     * @param Scaler $scaler
283
     *
284
     * @return ResponsiveFactory
285
     */
286
    public function setScaler($scaler) : ResponsiveFactory {
287
        $this->scaler = $scaler;
288
289
        return $this;
290
    }
291
292
    /**
293
     * @param bool $optimize
294
     *
295
     * @return ResponsiveFactory
296
     */
297
    public function setOptimize(bool $optimize) : ResponsiveFactory {
298
        $this->optimize = $optimize;
299
300
        return $this;
301
    }
302
303
    /**
304
     * @return string
305
     */
306
    public function getPublicPath() : string {
307
        return $this->publicPath;
308
    }
309
310
    /**
311
     * @param mixed $optimizerOptions
312
     *
313
     * @return ResponsiveFactory
314
     */
315
    public function setOptimizerOptions($optimizerOptions) : ResponsiveFactory {
316
        $this->optimizerOptions = $optimizerOptions;
0 ignored issues
show
Documentation Bug introduced by
It seems like $optimizerOptions of type * is incompatible with the declared type array of property $optimizerOptions.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
317
318
        return $this;
319
    }
320
321
    /**
322
     * @param bool $rebase
323
     *
324
     * @return ResponsiveFactory
325
     */
326
    public function setRebase(bool $rebase) : ResponsiveFactory {
327
        $this->rebase = $rebase;
328
329
        return $this;
330
    }
331
332
}
333