Server   B
last analyzed

↳ Parent: Project

Coupling/Cohesion

Components 1
Dependencies 5

Complexity

Total Complexity 54

Size/Duplication

Total Lines 532
Duplicated Lines 0 %

Test Coverage

Coverage 98.24%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 54
c 3
b 0
f 0
lcom 1
cbo 5
dl 0
loc 532
ccs 167
cts 170
cp 0.9824
rs 7.0642

33 Methods

Rating   Name   Duplication   Size   Complexity  
A setSource() 0 4 1
A __construct() 0 6 1
A getSource() 0 4 1
A setSourcePathPrefix() 0 4 1
A getSourcePathPrefix() 0 4 1
A getSourcePath() 0 20 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 setGroupCacheInFolders() 0 4 1
A getGroupCacheInFolders() 0 4 1
A setCacheWithFileExtensions() 0 4 1
A getCacheWithFileExtensions() 0 4 1
B getCachePath() 0 26 6
A cacheFileExists() 0 6 1
A deleteCache() 0 12 2
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 getImageResponse() 0 12 2
A getImageAsBase64() 0 14 2
A outputImage() 0 17 2
B makeImage() 0 57 7

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