Passed
Push — master ( 26a73e...e9f82c )
by Brent
03:12
created

ResponsiveFactory::create()   D

Complexity

Conditions 9
Paths 18

Size

Total Lines 44
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 4.909
c 0
b 0
f 0
cc 9
eloc 26
nc 18
nop 1
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
        $sourceFilename = $this->rebase ? pathinfo($src, PATHINFO_BASENAME) : $src;
121
122
        $sourceImage = $this->getImageFile($this->sourcePath, $sourceFilename);
123
124
        if (!$sourceImage) {
125
            throw new FileNotFoundException("{$this->sourcePath}/{$sourceFilename}");
126
        }
127
128
        $publicDirectory = $this->rebase ? trim(pathinfo($src, PATHINFO_DIRNAME), '/') : $sourceImage->getRelativePath();
129
130
        $extension = pathinfo($sourceImage->getExtension(), PATHINFO_EXTENSION);
131
        $imageFilename = pathinfo($sourceImage->getFilename(), PATHINFO_FILENAME);
132
        $publicImagePath = "{$this->publicPath}{$src}";
133
        $urlPath = '/' . trim(pathinfo($src, PATHINFO_DIRNAME), '/');
134
135
        $responsiveImage->setExtension($sourceImage->getExtension());
136
        $responsiveImage->setFileName($imageFilename);
137
        $responsiveImage->setUrlPath($urlPath);
138
139
        if ($this->enableCache && $this->fs->exists($publicImagePath)) {
140
            /** @var SplFileInfo[] $cachedFiles */
141
            $cachedFiles = Finder::create()->files()->in("{$this->publicPath}/{$publicDirectory}")->name("{$imageFilename}-*.{$extension}");
142
143
            foreach ($cachedFiles as $cachedFile) {
144
                $cachedFilename = $cachedFile->getFilename();
145
                $size = (int) str_replace(".{$extension}", '', str_replace("{$imageFilename}-", '', $cachedFilename));
146
147
                $responsiveImage->addSource("{$urlPath}/{$cachedFilename}", $size);
148
            }
149
150
            return $responsiveImage;
151
        }
152
153
        if (!$this->enableCache || !$this->fs->exists($publicImagePath)) {
154
            $this->fs->dumpFile($publicImagePath, $sourceImage->getContents());
155
        }
156
157
        $this->createScaledImages($sourceImage, $responsiveImage);
158
        
159
        return $responsiveImage;
160
    }
161
162
    /**
163
     * Create scaled image files and add them as sources to a Responsive Image, based on an array of file sizes:
164
     * [
165
     *      width => height,
166
     *      ...
167
     * ]
168
     *
169
     * @param SplFileInfo     $sourceImage
170
     * @param ResponsiveImage $responsiveImage
171
     *
172
     * @return ResponsiveImage
173
     */
174
    public function createScaledImages(SplFileInfo $sourceImage, ResponsiveImage $responsiveImage) : ResponsiveImage {
175
        $imageObject = $this->engine->make($sourceImage->getPathname());
176
        $urlPath = $responsiveImage->getUrlPath();
177
        $sizes = $this->scaler->scale($sourceImage, $imageObject);
178
179
        foreach ($sizes as $width => $height) {
180
            $scaledFileSrc = trim("{$urlPath}/{$imageObject->filename}-{$width}.{$imageObject->extension}", '/');
181
            $scaledFilePath = "{$this->getPublicPath()}/{$scaledFileSrc}";
182
            $responsiveImage->addSource($scaledFileSrc, $width);
183
184
            $scaledImage = $imageObject->resize((int) $width, (int) $height)->encode($imageObject->extension);
185
186
            if (!$this->enableCache || !$this->fs->exists($scaledFilePath)) {
187
                $this->fs->dumpFile($scaledFilePath, $scaledImage);
188
            }
189
        }
190
191
        $imageObject->destroy();
192
193
        if ($this->optimize) {
194
            $this->optimizeResponsiveImage($responsiveImage);
195
        }
196
197
        return $responsiveImage;
198
    }
199
200
    /**
201
     * Optimize all sources of a Responsive Image
202
     *
203
     * @param ResponsiveImage $responsiveImage
204
     *
205
     * @return ResponsiveImage
206
     */
207
    private function optimizeResponsiveImage(ResponsiveImage $responsiveImage) : ResponsiveImage {
208
        foreach ($responsiveImage->getSrcset() as $imageFile) {
209
            $this->optimizer->optimize("{$this->publicPath}/{$imageFile}");
210
        }
211
212
        return $responsiveImage;
213
    }
214
215
    /**
216
     * @param string $directory
217
     * @param string $path
218
     *
219
     * @return SplFileInfo
220
     */
221
    private function getImageFile(string $directory, string $path) : ?SplFileInfo {
222
        $iterator = Finder::create()->files()->in($directory)->path(ltrim($path, '/'))->getIterator();
223
        $iterator->rewind();
224
225
        return $iterator->current();
226
    }
227
228
    /**
229
     * @param string $driver
230
     *
231
     * @return ResponsiveFactory
232
     */
233
    public function setDriver($driver) : ResponsiveFactory {
234
        $this->driver = $driver;
235
236
        return $this;
237
    }
238
239
    /**
240
     * @param string $publicPath
241
     *
242
     * @return ResponsiveFactory
243
     */
244
    public function setPublicPath($publicPath) : ResponsiveFactory {
245
        $this->publicPath = $publicPath;
246
247
        return $this;
248
    }
249
250
    /**
251
     * @param boolean $enableCache
252
     *
253
     * @return ResponsiveFactory
254
     */
255
    public function setEnableCache($enableCache) : ResponsiveFactory {
256
        $this->enableCache = $enableCache;
257
258
        return $this;
259
    }
260
261
    /**
262
     * @param string $sourcePath
263
     *
264
     * @return ResponsiveFactory
265
     */
266
    public function setSourcePath($sourcePath) : ResponsiveFactory {
267
        $this->sourcePath = $sourcePath;
268
269
        return $this;
270
    }
271
272
    /**
273
     * @param Scaler $scaler
274
     *
275
     * @return ResponsiveFactory
276
     */
277
    public function setScaler($scaler) : ResponsiveFactory {
278
        $this->scaler = $scaler;
279
280
        return $this;
281
    }
282
283
    /**
284
     * @param bool $optimize
285
     *
286
     * @return ResponsiveFactory
287
     */
288
    public function setOptimize(bool $optimize) : ResponsiveFactory {
289
        $this->optimize = $optimize;
290
291
        return $this;
292
    }
293
294
    /**
295
     * @return string
296
     */
297
    public function getPublicPath() : string {
298
        return $this->publicPath;
299
    }
300
301
    /**
302
     * @param mixed $optimizerOptions
303
     *
304
     * @return ResponsiveFactory
305
     */
306
    public function setOptimizerOptions($optimizerOptions) : ResponsiveFactory {
307
        $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...
308
309
        return $this;
310
    }
311
312
    /**
313
     * @param bool $rebase
314
     *
315
     * @return ResponsiveFactory
316
     */
317
    public function setRebase(bool $rebase) : ResponsiveFactory {
318
        $this->rebase = $rebase;
319
320
        return $this;
321
    }
322
323
}
324