Completed
Pull Request — master (#301)
by
unknown
14:09
created

Server   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 548
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 5
dl 0
loc 548
ccs 0
cts 240
cp 0
rs 4.5599
c 0
b 0
f 0

33 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 20 4
A setCache() 0 4 1
A getCache() 0 4 1
A sourceFileExists() 0 8 2
A setBaseUrl() 0 4 1
A getBaseUrl() 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 28 7
A cacheFileExists() 0 10 2
A deleteCache() 0 18 3
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 23 3
B makeImage() 0 51 6

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