Passed
Push — master ( b712c2...804c9e )
by Brent
05:55 queued 02:35
created

ResponsiveFactory::setOptimizerOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace Brendt\Image;
4
5
use Amp\Parallel\Forking\Fork;
6
use AsyncInterop\Promise;
7
use Brendt\Image\Config\DefaultConfigurator;
8
use Brendt\Image\Config\ResponsiveFactoryConfigurator;
9
use Brendt\Image\Exception\FileNotFoundException;
10
use Brendt\Image\Scaler\Scaler;
11
use ImageOptimizer\Optimizer;
12
use ImageOptimizer\OptimizerFactory;
13
use Intervention\Image\Image;
14
use Intervention\Image\ImageManager;
15
use Symfony\Component\Filesystem\Filesystem;
16
use Symfony\Component\Finder\Finder;
17
use Symfony\Component\Finder\SplFileInfo;
18
19
class ResponsiveFactory
20
{
21
22
    /**
23
     * The image driver to use.
24
     * Available drivers: 'gd' and 'imagick'.
25
     *
26
     * @var string
27
     */
28
    protected $driver;
29
30
    /**
31
     * The source path to load images from.
32
     *
33
     * @var string
34
     */
35
    protected $sourcePath;
36
37
    /**
38
     * The public path to save rendered images.
39
     *
40
     * @var string
41
     */
42
    protected $publicPath;
43
44
    /**
45
     * Enabled cache will stop generated images from being overwritten.
46
     *
47
     * @var bool
48
     */
49
    private $enableCache;
50
51
    /**
52
     * Enable optimizers will run several image optimizers on the saved files.
53
     *
54
     * @var bool
55
     */
56
    private $optimize;
57
58
    /**
59
     * @var bool
60
     */
61
    private $async;
62
63
    /**
64
     *
65
     */
66
    private $optimizerOptions = [];
67
68
    /**
69
     * The Intervention image engine.
70
     *
71
     * @var ImageManager
72
     */
73
    protected $engine;
74
75
    /**
76
     * @var Filesystem
77
     */
78
    protected $fs;
79
80
    /**
81
     * @var Scaler
82
     */
83
    protected $scaler;
84
85
    /**
86
     * @var Optimizer
87
     */
88
    protected $optimizer;
89
90
    /**
91
     * @var Promise[]
92
     */
93
    protected $promises;
94
95
    /**
96
     * ResponsiveFactory constructor.
97
     *
98
     * @param ResponsiveFactoryConfigurator $configurator
99
     */
100
    public function __construct(ResponsiveFactoryConfigurator $configurator = null) {
101
        $configurator = $configurator ?? new DefaultConfigurator();
102
        $configurator->configure($this);
103
104
        $this->sourcePath = rtrim($this->sourcePath, '/');
105
        $this->publicPath = rtrim($this->publicPath, '/');
106
107
        $this->engine = new ImageManager([
108
            'driver' => $this->driver,
109
        ]);
110
111
        $this->optimizer = (new OptimizerFactory($this->optimizerOptions))->get();
112
        $this->fs = new Filesystem();
113
114
        if (!$this->fs->exists($this->publicPath)) {
115
            $this->fs->mkdir($this->publicPath);
116
        }
117
    }
118
119
    /**
120
     * @param string $src
121
     *
122
     * @return ResponsiveImage
123
     * @throws FileNotFoundException
124
     */
125
    public function create($src) {
126
        $responsiveImage = new ResponsiveImage($src);
127
        $src = $responsiveImage->src();
128
        $sourceImage = $this->getImageFile($this->sourcePath, $src);
129
130
        if (!$sourceImage) {
131
            throw new FileNotFoundException("{$this->sourcePath}{$src}");
132
        }
133
134
        $extension = $sourceImage->getExtension();
135
        $fileName = str_replace(".{$extension}", '', $sourceImage->getFilename());
136
        $publicImagePath = "{$this->publicPath}/{$src}";
137
138
        $urlParts = explode('/', $src);
139
        array_pop($urlParts);
140
        $urlPath = implode('/', $urlParts);
141
142
        $responsiveImage->setExtension($extension);
143
        $responsiveImage->setFileName($fileName);
144
        $responsiveImage->setUrlPath($urlPath);
145
146
        if ($this->enableCache && $this->fs->exists($publicImagePath)) {
147
            /** @var SplFileInfo[] $cachedFiles */
148
            $cachedFiles = Finder::create()->files()->in("{$this->publicPath}/{$sourceImage->getRelativePath()}")->name("{$fileName}-*.{$extension}");
149
150
            foreach ($cachedFiles as $cachedFile) {
151
                $cachedFilename = $cachedFile->getFilename();
152
                $size = (int) str_replace(".{$extension}", '', str_replace("{$fileName}-", '', $cachedFilename));
153
154
                $responsiveImage->addSource("{$urlPath}/{$cachedFilename}", $size);
155
            }
156
157
            return $responsiveImage;
158
        }
159
160
        if (!$this->enableCache || !$this->fs->exists($publicImagePath)) {
161
            $this->fs->dumpFile($publicImagePath, $sourceImage->getContents());
162
        }
163
164
        $imageObject = $this->engine->make($sourceImage->getPathname());
165
166
        // TODO: This piece of code should be added as a size and not as a "default".
167
        // It's because the WidthScaler skips the default size.
168
        $width = $imageObject->getWidth();
169
        $responsiveImage->addSource($src, $width);
170
        $imageObject->destroy();
171
172
        $this->createScaledImages($sourceImage, $responsiveImage);
173
174
        return $responsiveImage;
175
    }
176
177
    /**
178
     * Create scaled image files and add them as sources to a Responsive Image, based on an array of file sizes:
179
     * [
180
     *      width => height,
181
     *      ...
182
     * ]
183
     *
184
     * @param SplFileInfo     $sourceImage
185
     * @param ResponsiveImage $responsiveImage
186
     *
187
     * @return ResponsiveImage
188
     */
189
    public function createScaledImages(SplFileInfo $sourceImage, ResponsiveImage $responsiveImage) : ResponsiveImage {
190
        $async = $this->async && Fork::supported();
191
        $imageObject = $this->engine->make($sourceImage->getPathname());
192
        $urlPath = $responsiveImage->getUrlPath();
193
        $sizes = $this->scaler->scale($sourceImage, $imageObject);
194
195
        foreach ($sizes as $width => $height) {
196
            $scaledFileSrc = trim("{$urlPath}/{$imageObject->filename}-{$width}.{$imageObject->extension}", '/');
197
            $responsiveImage->addSource($scaledFileSrc, $width);
198
        }
199
200
        if ($async) {
201
            $factory = $this;
202
203
            $fork = Fork::spawn(function () use ($factory, $sourceImage, $responsiveImage) {
204
                $factory->scaleProcess($sourceImage, $responsiveImage);
205
            });
206
207
            $responsiveImage->setPromise($fork->join());
208
        } else {
209
            $this->scaleProcess($sourceImage, $responsiveImage);
210
            $deferred = new \Amp\Deferred();
211
            $deferred->resolve();
212
213
            $responsiveImage->setPromise($deferred->promise());
0 ignored issues
show
Documentation introduced by
$deferred->promise() is of type object<Amp\Deferred>, but the function expects a object<AsyncInterop\Promise>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
214
        }
215
216
        return $responsiveImage;
217
    }
218
219
    /**
220
     * @param SplFileInfo     $sourceImage
221
     * @param ResponsiveImage $responsiveImage
222
     */
223
    public function scaleProcess(SplFileInfo $sourceImage, ResponsiveImage $responsiveImage) {
224
        $urlPath = $responsiveImage->getUrlPath();
225
        $imageObject = $this->engine->make($sourceImage->getPathname());
226
        $sizes = $this->scaler->scale($sourceImage, $imageObject);
227
228
        foreach ($sizes as $width => $height) {
229
            $scaledFileSrc = trim("{$urlPath}/{$imageObject->filename}-{$width}.{$imageObject->extension}", '/');
230
            $scaledFilePath = "{$this->getPublicPath()}/{$scaledFileSrc}";
231
232
            $scaledImage = $imageObject->resize((int) $width, (int) $height)->encode($imageObject->extension);
233
234
            if (!$this->enableCache || !$this->fs->exists($scaledFilePath)) {
235
                $this->fs->dumpFile($scaledFilePath, $scaledImage);
236
            }
237
        }
238
239
        $imageObject->destroy();
240
241
        if ($this->optimize) {
242
            $this->optimizeResponsiveImage($responsiveImage);
243
        }
244
    }
245
246
    /**
247
     * Optimize all sources of a Responsive Image
248
     *
249
     * @param ResponsiveImage $responsiveImage
250
     *
251
     * @return ResponsiveImage
252
     */
253
    public function optimizeResponsiveImage(ResponsiveImage $responsiveImage) : ResponsiveImage {
254
        foreach ($responsiveImage->getSrcset() as $imageFile) {
255
            $this->optimizer->optimize("{$this->publicPath}/{$imageFile}");
256
        }
257
258
        return $responsiveImage;
259
    }
260
261
    /**
262
     * @param string $directory
263
     * @param string $path
264
     *
265
     * @return SplFileInfo
266
     */
267
    private function getImageFile(string $directory, string $path) : SplFileInfo {
268
        $iterator = Finder::create()->files()->in($directory)->path(ltrim($path, '/'))->getIterator();
269
        $iterator->rewind();
270
271
        return $iterator->current();
272
    }
273
274
    /**
275
     * @param string $driver
276
     *
277
     * @return ResponsiveFactory
278
     */
279
    public function setDriver($driver) : ResponsiveFactory {
280
        $this->driver = $driver;
281
282
        return $this;
283
    }
284
285
    /**
286
     * @param string $publicPath
287
     *
288
     * @return ResponsiveFactory
289
     */
290
    public function setPublicPath($publicPath) : ResponsiveFactory {
291
        $this->publicPath = $publicPath;
292
293
        return $this;
294
    }
295
296
    /**
297
     * @param boolean $enableCache
298
     *
299
     * @return ResponsiveFactory
300
     */
301
    public function setEnableCache($enableCache) : ResponsiveFactory {
302
        $this->enableCache = $enableCache;
303
304
        return $this;
305
    }
306
307
    /**
308
     * @param string $sourcePath
309
     *
310
     * @return ResponsiveFactory
311
     */
312
    public function setSourcePath($sourcePath) : ResponsiveFactory {
313
        $this->sourcePath = $sourcePath;
314
315
        return $this;
316
    }
317
318
    /**
319
     * @param Scaler $scaler
320
     *
321
     * @return ResponsiveFactory
322
     */
323
    public function setScaler($scaler) : ResponsiveFactory {
324
        $this->scaler = $scaler;
325
326
        return $this;
327
    }
328
329
    /**
330
     * @param bool $optimize
331
     *
332
     * @return ResponsiveFactory
333
     */
334
    public function setOptimize(bool $optimize) : ResponsiveFactory {
335
        $this->optimize = $optimize;
336
337
        return $this;
338
    }
339
340
    /**
341
     * @param bool $async
342
     *
343
     * @return ResponsiveFactory
344
     */
345
    public function setAsync(bool $async) : ResponsiveFactory {
346
        $this->async = $async;
347
348
        return $this;
349
    }
350
351
    /**
352
     * @return string
353
     */
354
    public function getPublicPath() : string {
355
        return $this->publicPath;
356
    }
357
358
    /**
359
     * @param mixed $optimizerOptions
360
     *
361
     * @return ResponsiveFactory
362
     */
363
    public function setOptimizerOptions($optimizerOptions) : ResponsiveFactory {
364
        $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...
365
366
        return $this;
367
    }
368
369
}
370