Completed
Push — master ( 339e22...8a2797 )
by Jonathan
02:46
created

Server   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 470
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 98.03%

Importance

Changes 26
Bugs 5 Features 12
Metric Value
wmc 45
c 26
b 5
f 12
lcom 1
cbo 5
dl 0
loc 470
ccs 149
cts 152
cp 0.9803
rs 8.3673

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A setSource() 0 4 1
A getSource() 0 4 1
A setSourcePathPrefix() 0 4 1
A getSourcePathPrefix() 0 4 1
A getSourcePath() 0 18 4
A sourceFileExists() 0 4 1
A setBaseUrl() 0 4 1
A getBaseUrl() 0 4 1
A setCache() 0 4 1
A getCache() 0 4 1
A setCachePathPrefix() 0 4 1
A getCachePathPrefix() 0 4 1
A cacheFileExists() 0 6 1
A deleteCache() 0 6 1
A setApi() 0 4 1
A getApi() 0 4 1
A setDefaults() 0 4 1
A getDefaults() 0 4 1
A setPresets() 0 4 1
A getPresets() 0 4 1
A getAllParams() 0 14 4
A setResponseFactory() 0 4 1
A getResponseFactory() 0 4 1
A outputImage() 0 15 1
B makeImage() 0 57 7
A getCachePath() 0 22 3
A getImageResponse() 0 12 2
A getImageAsBase64() 0 14 2

How to fix   Complexity   

Complex Class

Complex classes like Server often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Server, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace League\Glide;
4
5
use InvalidArgumentException;
6
use League\Flysystem\FileExistsException;
7
use League\Flysystem\FilesystemInterface;
8
use League\Glide\Api\ApiInterface;
9
use League\Glide\Filesystem\FileNotFoundException;
10
use League\Glide\Filesystem\FilesystemException;
11
use League\Glide\Responses\ResponseFactoryInterface;
12
13
class Server
14
{
15
    /**
16
     * Source file system.
17
     * @var FilesystemInterface
18
     */
19
    protected $source;
20
21
    /**
22
     * Source path prefix.
23
     * @var string
24
     */
25
    protected $sourcePathPrefix;
26
27
    /**
28
     * Cache file system.
29
     * @var FilesystemInterface
30
     */
31
    protected $cache;
32
33
    /**
34
     * Cache path prefix.
35
     * @var string
36
     */
37
    protected $cachePathPrefix;
38
39
    /**
40
     * Image manipulation API.
41
     * @var ApiInterface
42
     */
43
    protected $api;
44
45
    /**
46
     * Response factory.
47
     * @var ResponseFactoryInterface|null
48
     */
49
    protected $responseFactory;
50
51
    /**
52
     * Base URL.
53
     * @var string
54
     */
55
    protected $baseUrl;
56
57
    /**
58
     * Default image manipulations.
59
     * @var array
60
     */
61
    protected $defaults = [];
62
63
    /**
64
     * Preset image manipulations.
65
     * @var array
66
     */
67
    protected $presets = [];
68
69
    /**
70
     * Create Server instance.
71
     * @param FilesystemInterface $source Source file system.
72
     * @param FilesystemInterface $cache  Cache file system.
73
     * @param ApiInterface        $api    Image manipulation API.
74
     */
75 132
    public function __construct(FilesystemInterface $source, FilesystemInterface $cache, ApiInterface $api)
76
    {
77 132
        $this->setSource($source);
78 132
        $this->setCache($cache);
79 132
        $this->setApi($api);
80 132
    }
81
82
    /**
83
     * Set source file system.
84
     * @param FilesystemInterface $source Source file system.
85
     */
86 132
    public function setSource(FilesystemInterface $source)
87
    {
88 132
        $this->source = $source;
89 132
    }
90
91
    /**
92
     * Get source file system.
93
     * @return FilesystemInterface Source file system.
94
     */
95 6
    public function getSource()
96
    {
97 6
        return $this->source;
98
    }
99
100
    /**
101
     * Set source path prefix.
102
     * @param string $sourcePathPrefix Source path prefix.
103
     */
104 15
    public function setSourcePathPrefix($sourcePathPrefix)
105
    {
106 15
        $this->sourcePathPrefix = trim($sourcePathPrefix, '/');
107 15
    }
108
109
    /**
110
     * Get source path prefix.
111
     * @return string Source path prefix.
112
     */
113 6
    public function getSourcePathPrefix()
114
    {
115 6
        return $this->sourcePathPrefix;
116
    }
117
118
    /**
119
     * Get source path.
120
     * @param  string                $path Image path.
121
     * @return string                The source path.
122
     * @throws FileNotFoundException
123
     */
124 63
    public function getSourcePath($path)
125
    {
126 63
        $path = trim($path, '/');
127
128 63
        if (substr($path, 0, strlen($this->baseUrl)) === $this->baseUrl) {
129 3
            $path = trim(substr($path, strlen($this->baseUrl)), '/');
130 2
        }
131
132 63
        if ($path === '') {
133 3
            throw new FileNotFoundException('Image path missing.');
134
        }
135
136 60
        if ($this->sourcePathPrefix) {
137 6
            $path = $this->sourcePathPrefix.'/'.$path;
138 4
        }
139
140 60
        return rawurldecode($path);
141
    }
142
143
    /**
144
     * Check if a source file exists.
145
     * @param  string $path Image path.
146
     * @return bool   Whether the source file exists.
147
     */
148 18
    public function sourceFileExists($path)
149
    {
150 18
        return $this->source->has($this->getSourcePath($path));
151
    }
152
153
    /**
154
     * Set base URL.
155
     * @param string $baseUrl Base URL.
156
     */
157 12
    public function setBaseUrl($baseUrl)
158
    {
159 12
        $this->baseUrl = trim($baseUrl, '/');
160 12
    }
161
162
    /**
163
     * Get base URL.
164
     * @return string Base URL.
165
     */
166 6
    public function getBaseUrl()
167
    {
168 6
        return $this->baseUrl;
169
    }
170
171
    /**
172
     * Set cache file system.
173
     * @param FilesystemInterface $cache Cache file system.
174
     */
175 132
    public function setCache(FilesystemInterface $cache)
176
    {
177 132
        $this->cache = $cache;
178 132
    }
179
180
    /**
181
     * Get cache file system.
182
     * @return FilesystemInterface Cache file system.
183
     */
184 6
    public function getCache()
185
    {
186 6
        return $this->cache;
187
    }
188
189
    /**
190
     * Set cache path prefix.
191
     * @param string $cachePathPrefix Cache path prefix.
192
     */
193 12
    public function setCachePathPrefix($cachePathPrefix)
194
    {
195 12
        $this->cachePathPrefix = trim($cachePathPrefix, '/');
196 12
    }
197
198
    /**
199
     * Get cache path prefix.
200
     * @return string Cache path prefix.
201
     */
202 6
    public function getCachePathPrefix()
203
    {
204 6
        return $this->cachePathPrefix;
205
    }
206
207
    /**
208
     * Get cache path.
209
     * @param  string $path   Image path.
210
     * @param  array  $params Image manipulation params.
211
     * @return string Cache path.
212
     */
213 45
    public function getCachePath($path, array $params = [])
214
    {
215 45
        $sourcePath = $this->getSourcePath($path);
216
217 45
        if ($this->sourcePathPrefix) {
218 3
            $sourcePath = substr($sourcePath, strlen($this->sourcePathPrefix) + 1);
219 2
        }
220
221 45
        $params = $this->getAllParams($params);
222 45
        unset($params['s'], $params['p']);
223 45
        ksort($params);
224
225 45
        $md5 = md5($sourcePath.'?'.http_build_query($params));
226
227 45
        $path = $sourcePath.'/'.$md5;
228
229 45
        if ($this->cachePathPrefix) {
230 3
            $path = $this->cachePathPrefix.'/'.$path;
231 2
        }
232
233 45
        return $path;
234
    }
235
236
    /**
237
     * Check if a cache file exists.
238
     * @param  string $path   Image path.
239
     * @param  array  $params Image manipulation params.
240
     * @return bool   Whether the cache file exists.
241
     */
242 33
    public function cacheFileExists($path, array $params)
243
    {
244 33
        return $this->cache->has(
245 33
            $this->getCachePath($path, $params)
246 22
        );
247
    }
248
249
    /**
250
     * Delete cached manipulations for an image.
251
     * @param  string $path Image path.
252
     * @return bool   Whether the delete succeeded.
253
     */
254 3
    public function deleteCache($path)
255
    {
256 3
        return $this->cache->deleteDir(
257 3
            dirname($this->getCachePath($path))
258 2
        );
259
    }
260
261
    /**
262
     * Set image manipulation API.
263
     * @param ApiInterface $api Image manipulation API.
264
     */
265 132
    public function setApi(ApiInterface $api)
266
    {
267 132
        $this->api = $api;
268 132
    }
269
270
    /**
271
     * Get image manipulation API.
272
     * @return ApiInterface Image manipulation API.
273
     */
274 6
    public function getApi()
275
    {
276 6
        return $this->api;
277
    }
278
279
    /**
280
     * Set default image manipulations.
281
     * @param array $defaults Default image manipulations.
282
     */
283 15
    public function setDefaults(array $defaults)
284
    {
285 15
        $this->defaults = $defaults;
286 15
    }
287
288
    /**
289
     * Get default image manipulations.
290
     * @return array Default image manipulations.
291
     */
292 6
    public function getDefaults()
293
    {
294 6
        return $this->defaults;
295
    }
296
297
    /**
298
     * Set preset image manipulations.
299
     * @param array $presets Preset image manipulations.
300
     */
301 15
    public function setPresets(array $presets)
302
    {
303 15
        $this->presets = $presets;
304 15
    }
305
306
    /**
307
     * Get preset image manipulations.
308
     * @return array Preset image manipulations.
309
     */
310 6
    public function getPresets()
311
    {
312 6
        return $this->presets;
313
    }
314
315
    /**
316
     * Get all image manipulations params, including defaults and presets.
317
     * @param  array $params Image manipulation params.
318
     * @return array All image manipulation params.
319
     */
320 48
    public function getAllParams(array $params)
321
    {
322 48
        $all = $this->defaults;
323
324 48
        if (isset($params['p'])) {
325 3
            foreach (explode(',', $params['p']) as $preset) {
326 3
                if (isset($this->presets[$preset])) {
327 3
                    $all = array_merge($all, $this->presets[$preset]);
328 2
                }
329 2
            }
330 2
        }
331
332 48
        return array_merge($all, $params);
333
    }
334
335
    /**
336
     * Set response factory.
337
     * @param ResponseFactoryInterface|null $responseFactory Response factory.
338
     */
339 15
    public function setResponseFactory(ResponseFactoryInterface $responseFactory = null)
340
    {
341 15
        $this->responseFactory = $responseFactory;
342 15
    }
343
344
    /**
345
     * Get response factory.
346
     * @return ResponseFactoryInterface Response factory.
347
     */
348 6
    public function getResponseFactory()
349
    {
350 6
        return $this->responseFactory;
351
    }
352
353
    /**
354
     * Generate and return image response.
355
     * @param  string                   $path   Image path.
356
     * @param  array                    $params Image manipulation params.
357
     * @return mixed                    Image response.
358
     * @throws InvalidArgumentException
359
     */
360 6
    public function getImageResponse($path, array $params)
361
    {
362 6
        if (is_null($this->responseFactory)) {
363 3
            throw new InvalidArgumentException(
364 1
                'Unable to get image response, no response factory defined.'
365 2
            );
366
        }
367
368 3
        $path = $this->makeImage($path, $params);
369
370 3
        return $this->responseFactory->create($this->cache, $path);
371
    }
372
373
    /**
374
     * Generate and return Base64 encoded image.
375
     * @param  string              $path   Image path.
376
     * @param  array               $params Image manipulation params.
377
     * @return string              Base64 encoded image.
378
     * @throws FilesystemException
379
     */
380 6
    public function getImageAsBase64($path, array $params)
381
    {
382 6
        $path = $this->makeImage($path, $params);
383
384 6
        $source = $this->cache->read($path);
385
386 6
        if ($source === false) {
387 3
            throw new FilesystemException(
388 3
                'Could not read the image `'.$path.'`.'
389 2
            );
390
        }
391
392 3
        return 'data:'.$this->cache->getMimetype($path).';base64,'.base64_encode($source);
393
    }
394
395
    /**
396
     * Generate and output image.
397
     * @param  string                   $path   Image path.
398
     * @param  array                    $params Image manipulation params.
399
     * @throws InvalidArgumentException
400
     */
401 3
    public function outputImage($path, array $params)
402
    {
403 3
        $path = $this->makeImage($path, $params);
404
405 3
        header('Content-Type:'.$this->cache->getMimetype($path));
406 3
        header('Content-Length:'.$this->cache->getSize($path));
407 3
        header('Cache-Control:'.'max-age=31536000, public');
408 3
        header('Expires:'.date_create('+1 years')->format('D, d M Y H:i:s').' GMT');
409
410 3
        $stream = $this->cache->readStream($path);
411
412 3
        rewind($stream);
413 3
        fpassthru($stream);
414 3
        fclose($stream);
415 3
    }
416
417
    /**
418
     * Generate manipulated image.
419
     * @param  string                $path   Image path.
420
     * @param  array                 $params Image manipulation params.
421
     * @return string                Cache path.
422
     * @throws FileNotFoundException
423
     * @throws FilesystemException
424
     */
425 30
    public function makeImage($path, array $params)
426
    {
427 30
        $sourcePath = $this->getSourcePath($path);
428 30
        $cachedPath = $this->getCachePath($path, $params);
429
430 30
        if ($this->cacheFileExists($path, $params) === true) {
431 15
            return $cachedPath;
432
        }
433
434 15
        if ($this->sourceFileExists($path) === false) {
435 3
            throw new FileNotFoundException(
436 3
                'Could not find the image `'.$sourcePath.'`.'
437 2
            );
438
        }
439
440 12
        $source = $this->source->read(
441
            $sourcePath
442 8
        );
443
444 12
        if ($source === false) {
445 3
            throw new FilesystemException(
446 3
                'Could not read the image `'.$sourcePath.'`.'
447 2
            );
448
        }
449
450
        // We need to write the image to the local disk before
451
        // doing any manipulations. This is because EXIF data
452
        // can only be read from an actual file.
453 9
        $tmp = tempnam(sys_get_temp_dir(), 'Glide');
454
455 9
        if (file_put_contents($tmp, $source) === false) {
456
            throw new FilesystemException(
457
                'Unable to write temp file for `'.$sourcePath.'`.'
458
            );
459
        }
460
461
        try {
462 9
            $write = $this->cache->write(
463 6
                $cachedPath,
464 9
                $this->api->run($tmp, $this->getAllParams($params))
465 6
            );
466
467 6
            if ($write === false) {
468 3
                throw new FilesystemException(
469 4
                    'Could not write the image `'.$cachedPath.'`.'
470 2
                );
471
            }
472 8
        } catch (FileExistsException $exception) {
473
            // This edge case occurs when the target already exists
474
            // because it's currently be written to disk in another
475
            // request. It's best to just fail silently.
476
        }
477
478 6
        unlink($tmp);
479
480 6
        return $cachedPath;
481
    }
482
}
483