Passed
Push — master ( f99852...46c8e4 )
by Brent
04:34 queued 02:08
created

ResponsiveFactory   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 11
dl 0
loc 277
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 2
C create() 0 52 8
A createScaledImages() 0 14 2
A optimizeResponsiveImage() 0 7 2
A saveImageFile() 0 5 3
A getImageFile() 0 6 1
A setDriver() 0 5 1
A setPublicPath() 0 5 1
A setEnableCache() 0 5 1
A setSourcePath() 0 5 1
A setScaler() 0 5 1
A setOptimize() 0 5 1
1
<?php
2
3
namespace Brendt\Image;
4
5
use Brendt\Image\Config\ResponsiveFactoryConfigurator;
6
use Brendt\Image\Exception\FileNotFoundException;
7
use Brendt\Image\Scaler\Scaler;
8
use ImageOptimizer\Optimizer;
9
use ImageOptimizer\OptimizerFactory;
10
use Intervention\Image\Image;
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
     * The Intervention image engine.
57
     *
58
     * @var ImageManager
59
     */
60
    protected $engine;
61
62
    /**
63
     * @var Filesystem
64
     */
65
    protected $fs;
66
67
    /**
68
     * @var Scaler
69
     */
70
    protected $scaler;
71
72
    /**
73
     * @var Optimizer
74
     */
75
    protected $optimizer;
76
77
    /**
78
     * ResponsiveFactory constructor.
79
     *
80
     * @param ResponsiveFactoryConfigurator $configurator
81
     */
82
    public function __construct(ResponsiveFactoryConfigurator $configurator) {
83
        $configurator->configure($this);
84
85
        $this->sourcePath = rtrim($this->sourcePath, '/');
86
        $this->publicPath = rtrim($this->publicPath, '/');
87
88
        $this->engine = new ImageManager([
89
            'driver' => $this->driver,
90
        ]);
91
        $this->optimizer = (new OptimizerFactory())->get();
92
        $this->fs = new Filesystem();
93
94
        if (!$this->fs->exists($this->publicPath)) {
95
            $this->fs->mkdir($this->publicPath);
96
        }
97
    }
98
99
    /**
100
     * @param string $src
101
     *
102
     * @return ResponsiveImage
103
     * @throws FileNotFoundException
104
     */
105
    public function create($src) : ResponsiveImage {
106
        $responsiveImage = new ResponsiveImage($src);
107
        $src = $responsiveImage->src();
108
        $sourceImage = $this->getImageFile($this->sourcePath, $src);
109
110
        if (!$sourceImage) {
111
            throw new FileNotFoundException("{$this->sourcePath}{$src}");
112
        }
113
114
        $extension = $sourceImage->getExtension();
115
        $fileName = str_replace(".{$extension}", '', $sourceImage->getFilename());
116
        $publicImagePath = "{$this->publicPath}/{$src}";
117
118
        $urlParts = explode('/', $src);
119
        array_pop($urlParts);
120
        $urlPath = implode('/', $urlParts);
121
122
        $responsiveImage->setExtension($extension);
123
        $responsiveImage->setFileName($fileName);
124
        $responsiveImage->setUrlPath($urlPath);
125
126
        if ($this->enableCache && $this->fs->exists($publicImagePath)) {
127
            /** @var SplFileInfo[] $cachedFiles */
128
            $cachedFiles = Finder::create()->files()->in($sourceImage->getRelativePath())->name("{$fileName}-*.{$extension}");
129
130
            foreach ($cachedFiles as $cachedFile) {
131
                $cachedFilename = $cachedFile->getFilename();
132
                $size = (int) str_replace(".{$extension}", '', str_replace("{$fileName}-", '', $cachedFilename));
133
134
                $responsiveImage->addSource("{$urlPath}/{$cachedFilename}", $size);
135
            }
136
137
            return $responsiveImage;
138
        }
139
140
        if (!$this->enableCache || !$this->fs->exists($publicImagePath)) {
141
            $this->fs->dumpFile($publicImagePath, $sourceImage->getContents());
142
        }
143
144
        $imageObject = $this->engine->make($sourceImage->getPathname());
145
        $width = $imageObject->getWidth();
146
        $responsiveImage->addSource($src, $width);
147
148
        $sizes = $this->scaler->scale($sourceImage, $imageObject);
149
        $this->createScaledImages($sizes, $imageObject, $responsiveImage);
150
151
        if ($this->optimize) {
152
            $this->optimizeResponsiveImage($responsiveImage);
153
        }
154
155
        return $responsiveImage;
156
    }
157
158
    /**
159
     * Create scaled image files and add them as sources to a Responsive Image, based on an array of file sizes:
160
     * [
161
     *      width => height,
162
     *      ...
163
     * ]
164
     *
165
     * @param array           $sizes
166
     * @param Image           $imageObject
167
     * @param ResponsiveImage $responsiveImage
168
     *
169
     * @return ResponsiveImage
170
     */
171
    private function createScaledImages(array $sizes, Image $imageObject, ResponsiveImage $responsiveImage) : ResponsiveImage {
172
        $urlPath = $responsiveImage->getUrlPath();
173
174
        foreach ($sizes as $width => $height) {
175
            $scaledFileSrc = "{$urlPath}/{$imageObject->filename}-{$width}.{$imageObject->extension}";
176
            $scaledFilePath = "{$this->publicPath}/{$scaledFileSrc}";
177
            $scaledImage = $imageObject->resize((int) $width, (int) $height)->encode($imageObject->extension);
178
179
            $this->saveImageFile($scaledFilePath, $scaledImage);
180
            $responsiveImage->addSource($scaledFileSrc, $width);
181
        }
182
183
        return $responsiveImage;
184
    }
185
186
    /**
187
     * Optimize all sources of a Responsive Image
188
     *
189
     * @param ResponsiveImage $responsiveImage
190
     *
191
     * @return ResponsiveImage
192
     */
193
    private function optimizeResponsiveImage(ResponsiveImage $responsiveImage) : ResponsiveImage {
194
        foreach ($responsiveImage->getSrcset() as $imageFile) {
195
            $this->optimizer->optimize("{$this->publicPath}/{$imageFile}");
196
        }
197
198
        return $responsiveImage;
199
    }
200
201
    /**
202
     * Save the image file contents to a path
203
     *
204
     * @param string $path
205
     * @param string $image
206
     */
207
    private function saveImageFile(string $path, string $image) {
208
        if (!$this->enableCache || !$this->fs->exists($path)) {
209
            $this->fs->dumpFile($path, $image);
210
        }
211
    }
212
213
    /**
214
     * @param string $directory
215
     * @param string $path
216
     *
217
     * @return SplFileInfo
218
     */
219
    private function getImageFile(string $directory, string $path) : SplFileInfo {
220
        $iterator = Finder::create()->files()->in($directory)->path(ltrim($path, '/'))->getIterator();
221
        $iterator->rewind();
222
223
        return $iterator->current();
224
    }
225
226
    /**
227
     * @param string $driver
228
     *
229
     * @return ResponsiveFactory
230
     */
231
    public function setDriver($driver) : ResponsiveFactory {
232
        $this->driver = $driver;
233
234
        return $this;
235
    }
236
237
    /**
238
     * @param string $publicPath
239
     *
240
     * @return ResponsiveFactory
241
     */
242
    public function setPublicPath($publicPath) : ResponsiveFactory {
243
        $this->publicPath = $publicPath;
244
245
        return $this;
246
    }
247
248
    /**
249
     * @param boolean $enableCache
250
     *
251
     * @return ResponsiveFactory
252
     */
253
    public function setEnableCache($enableCache) : ResponsiveFactory {
254
        $this->enableCache = $enableCache;
255
256
        return $this;
257
    }
258
259
    /**
260
     * @param string $sourcePath
261
     *
262
     * @return ResponsiveFactory
263
     */
264
    public function setSourcePath($sourcePath) : ResponsiveFactory {
265
        $this->sourcePath = $sourcePath;
266
267
        return $this;
268
    }
269
270
    /**
271
     * @param Scaler $scaler
272
     *
273
     * @return ResponsiveFactory
274
     */
275
    public function setScaler($scaler) : ResponsiveFactory {
276
        $this->scaler = $scaler;
277
278
        return $this;
279
    }
280
281
    /**
282
     * @param bool $optimize
283
     *
284
     * @return ResponsiveFactory
285
     */
286
    public function setOptimize(bool $optimize) : ResponsiveFactory {
287
        $this->optimize = $optimize;
288
289
        return $this;
290
    }
291
292
}
293