Completed
Push — master ( 401a94...5be1e8 )
by Ankit
02:43
created

Server   F

Complexity

Total Complexity 83

Size/Duplication

Total Lines 745
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 745
ccs 253
cts 253
cp 1
rs 1.263
c 0
b 0
f 0
wmc 83

32 Methods

Rating   Name   Duplication   Size   Complexity  
A getMaxUploadSize() 0 3 1
A getServerChecksum() 0 3 1
A __construct() 0 16 1
A getUploadDir() 0 3 1
A setUploadKey() 0 5 1
A setMaxUploadSize() 0 5 1
A setUploadDir() 0 5 1
A getChecksumAlgorithm() 0 11 2
A getUploadKey() 0 15 3
A headers() 0 9 2
A getResponse() 0 3 1
A getRequest() 0 3 1
B handlePost() 0 44 5
A getSupportedHashAlgorithms() 0 14 3
A isExpired() 0 9 4
A getPartialsMeta() 0 11 2
B handleConcatenation() 0 37 3
B handleGet() 0 20 5
A handleHead() 0 15 3
A verifyUploadSize() 0 9 3
B serve() 0 29 4
A getHeadersForHeadRequest() 0 17 4
A getClientChecksum() 0 17 4
C handlePatch() 0 35 8
A verifyChecksum() 0 8 2
A exit() 0 3 1
A buildFile() 0 9 2
B handleExpiration() 0 24 5
A handleOptions() 0 16 2
A handleDelete() 0 20 4
A getPathForPartialUpload() 0 11 2
A __call() 0 3 1

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.

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 TusPhp\Tus;
4
5
use TusPhp\File;
6
use Carbon\Carbon;
7
use TusPhp\Request;
8
use TusPhp\Response;
9
use Ramsey\Uuid\Uuid;
10
use TusPhp\Cache\Cacheable;
11
use TusPhp\Exception\FileException;
12
use TusPhp\Exception\ConnectionException;
13
use TusPhp\Exception\OutOfRangeException;
14
use Illuminate\Http\Request as HttpRequest;
15
use Illuminate\Http\Response as HttpResponse;
16
use Symfony\Component\HttpFoundation\BinaryFileResponse;
17
18
class Server extends AbstractTus
19
{
20
    /** @const string Tus Creation Extension */
21
    const TUS_EXTENSION_CREATION = 'creation';
22
23
    /** @const string Tus Termination Extension */
24
    const TUS_EXTENSION_TERMINATION = 'termination';
25
26
    /** @const string Tus Checksum Extension */
27
    const TUS_EXTENSION_CHECKSUM = 'checksum';
28
29
    /** @const string Tus Expiration Extension */
30
    const TUS_EXTENSION_EXPIRATION = 'expiration';
31
32
    /** @const string Tus Concatenation Extension */
33
    const TUS_EXTENSION_CONCATENATION = 'concatenation';
34
35
    /** @const array All supported tus extensions */
36
    const TUS_EXTENSIONS = [
37
        self::TUS_EXTENSION_CREATION,
38
        self::TUS_EXTENSION_TERMINATION,
39
        self::TUS_EXTENSION_CHECKSUM,
40
        self::TUS_EXTENSION_EXPIRATION,
41
        self::TUS_EXTENSION_CONCATENATION,
42
    ];
43
44
    /** @const int 460 Checksum Mismatch */
45
    const HTTP_CHECKSUM_MISMATCH = 460;
46
47
    /** @const string Default checksum algorithm */
48
    const DEFAULT_CHECKSUM_ALGORITHM = 'sha256';
49
50
    /** @const int 24 hours access control max age header */
51
    const HEADER_ACCESS_CONTROL_MAX_AGE = 86400;
52
53
    /** @var Request */
54
    protected $request;
55
56
    /** @var Response */
57
    protected $response;
58
59
    /** @var string */
60
    protected $uploadDir;
61
62
    /** @var string */
63
    protected $uploadKey;
64
65
    /** @var array */
66
    protected $globalHeaders;
67
68
    /**
69
     * @var int Max upload size in bytes
70
     *          Default 0, no restriction.
71
     */
72
    protected $maxUploadSize = 0;
73
74
    /**
75
     * TusServer constructor.
76
     *
77
     * @param Cacheable|string $cacheAdapter
78
     */
79 3
    public function __construct($cacheAdapter = 'file')
80
    {
81 3
        $this->request   = new Request;
82 3
        $this->response  = new Response;
83 3
        $this->uploadDir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'uploads';
84
85 3
        $this->globalHeaders = [
86 3
            'Access-Control-Allow-Origin' => $this->request->header('Origin'),
87 3
            'Access-Control-Allow-Methods' => implode(',', $this->getRequest()->allowedHttpVerbs()),
88 3
            'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type, Content-Length, Upload-Key, Upload-Checksum, Upload-Length, Upload-Offset, Tus-Version, Tus-Resumable, Upload-Metadata',
89 3
            'Access-Control-Expose-Headers' => 'Upload-Key, Upload-Checksum, Upload-Length, Upload-Offset, Upload-Metadata, Tus-Version, Tus-Resumable, Tus-Extension, Location',
90 3
            'Access-Control-Max-Age' => self::HEADER_ACCESS_CONTROL_MAX_AGE,
91 3
            'X-Content-Type-Options' => 'nosniff',
92
        ];
93
94 3
        $this->setCache($cacheAdapter);
95 3
    }
96
97
    /**
98
     * Set upload dir.
99
     *
100
     * @param string $path
101
     *
102
     * @return Server
103
     */
104 2
    public function setUploadDir(string $path) : self
105
    {
106 2
        $this->uploadDir = $path;
107
108 2
        return $this;
109
    }
110
111
    /**
112
     * Get upload dir.
113
     *
114
     * @return string
115
     */
116 1
    public function getUploadDir() : string
117
    {
118 1
        return $this->uploadDir;
119
    }
120
121
    /**
122
     * Get request.
123
     *
124
     * @return Request
125
     */
126 1
    public function getRequest() : Request
127
    {
128 1
        return $this->request;
129
    }
130
131
    /**
132
     * Get request.
133
     *
134
     * @return Response
135
     */
136 1
    public function getResponse() : Response
137
    {
138 1
        return $this->response;
139
    }
140
141
    /**
142
     * Get file checksum.
143
     *
144
     * @param string $filePath
145
     *
146
     * @return string
147
     */
148 1
    public function getServerChecksum(string $filePath) : string
149
    {
150 1
        return hash_file($this->getChecksumAlgorithm(), $filePath);
151
    }
152
153
    /**
154
     * Get checksum algorithm.
155
     *
156
     * @return string|null
157
     */
158 1
    public function getChecksumAlgorithm()
159
    {
160 1
        $checksumHeader = $this->getRequest()->header('Upload-Checksum');
161
162 1
        if (empty($checksumHeader)) {
163 1
            return self::DEFAULT_CHECKSUM_ALGORITHM;
164
        }
165
166 1
        list($checksumAlgorithm) = explode(' ', $checksumHeader);
167
168 1
        return $checksumAlgorithm;
169
    }
170
171
    /**
172
     * Set upload key.
173
     *
174
     * @param string $key
175
     *
176
     * @return Server
177
     */
178 1
    public function setUploadKey(string $key) : self
179
    {
180 1
        $this->uploadKey = $key;
181
182 1
        return $this;
183
    }
184
185
    /**
186
     * Get upload key from header.
187
     *
188
     * @return string|HttpResponse
189
     */
190 4
    public function getUploadKey()
191
    {
192 4
        if ( ! empty($this->uploadKey)) {
193 1
            return $this->uploadKey;
194
        }
195
196 3
        $key = $this->getRequest()->header('Upload-Key') ?? Uuid::uuid4()->toString();
197
198 3
        if (empty($key)) {
199 1
            return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
200
        }
201
202 2
        $this->uploadKey = $key;
203
204 2
        return $this->uploadKey;
205
    }
206
207
    /**
208
     * Set max upload size.
209
     *
210
     * @param int $uploadSize
211
     *
212
     * @return Server
213
     */
214 2
    public function setMaxUploadSize(int $uploadSize) : self
215
    {
216 2
        $this->maxUploadSize = $uploadSize;
217
218 2
        return $this;
219
    }
220
221
    /**
222
     * Get max upload size.
223
     *
224
     * @return int
225
     */
226 1
    public function getMaxUploadSize() : int
227
    {
228 1
        return $this->maxUploadSize;
229
    }
230
231
    /**
232
     * Set or get global headers.
233
     *
234
     * @param array $headers
235
     *
236
     * @return Server|array
237
     */
238 1
    public function headers(array $headers = [])
239
    {
240 1
        if (empty($headers)) {
241 1
            return $this->globalHeaders;
242
        }
243
244 1
        $this->globalHeaders = $headers + $this->globalHeaders;
245
246 1
        return $this;
247
    }
248
249
    /**
250
     * Handle all HTTP request.
251
     *
252
     * @return null|HttpResponse
253
     */
254 3
    public function serve()
255
    {
256 3
        $requestMethod = $this->getRequest()->method();
257 3
        $globalHeaders = $this->headers();
258
259 3
        if (HttpRequest::METHOD_OPTIONS !== $requestMethod) {
260 3
            $globalHeaders += ['Tus-Resumable' => self::TUS_PROTOCOL_VERSION];
261
        }
262
263
        // Allow overriding the HTTP method. The reason for this is
264
        // that some libraries/environments do not support PATCH and
265
        // DELETE requests, e.g. Flash in a browser and parts of Java
266 3
        $newMethod = $this->getRequest()->header('X-HTTP-Method-Override');
267
268 3
        if ( ! empty($newMethod)) {
269 1
            $requestMethod = $newMethod;
270
        }
271
272 3
        if ( ! in_array($requestMethod, $this->getRequest()->allowedHttpVerbs())) {
273 1
            return $this->response->send(null, HttpResponse::HTTP_METHOD_NOT_ALLOWED);
274
        }
275
276 2
        $this->getResponse()->setHeaders($globalHeaders);
277
278 2
        $method = 'handle' . ucfirst(strtolower($requestMethod));
279
280 2
        $this->{$method}();
281
282 2
        $this->exit();
283 2
    }
284
285
    /**
286
     * Exit from current php process.
287
     *
288
     * @codeCoverageIgnore
289
     */
290
    protected function exit()
291
    {
292
        exit(0);
293
    }
294
295
    /**
296
     * Handle OPTIONS request.
297
     *
298
     * @return HttpResponse
299
     */
300 2
    protected function handleOptions() : HttpResponse
301
    {
302
        $headers = [
303 2
            'Allow' => implode(',', $this->request->allowedHttpVerbs()),
304 2
            'Tus-Version' => self::TUS_PROTOCOL_VERSION,
305 2
            'Tus-Extension' => implode(',', self::TUS_EXTENSIONS),
306 2
            'Tus-Checksum-Algorithm' => $this->getSupportedHashAlgorithms(),
307
        ];
308
309 2
        $maxUploadSize = $this->getMaxUploadSize();
310
311 2
        if ($maxUploadSize > 0) {
312 1
            $headers['Tus-Max-Size'] = $maxUploadSize;
313
        }
314
315 2
        return $this->response->send(null, HttpResponse::HTTP_OK, $headers);
316
    }
317
318
    /**
319
     * Handle HEAD request.
320
     *
321
     * @return HttpResponse
322
     */
323 5
    protected function handleHead() : HttpResponse
324
    {
325 5
        $key = $this->request->key();
326
327 5
        if ( ! $fileMeta = $this->cache->get($key)) {
328 1
            return $this->response->send(null, HttpResponse::HTTP_NOT_FOUND);
329
        }
330
331 4
        $offset = $fileMeta['offset'] ?? false;
332
333 4
        if (false === $offset) {
334 1
            return $this->response->send(null, HttpResponse::HTTP_GONE);
335
        }
336
337 3
        return $this->response->send(null, HttpResponse::HTTP_OK, $this->getHeadersForHeadRequest($fileMeta));
338
    }
339
340
    /**
341
     * Handle POST request.
342
     *
343
     * @return HttpResponse
344
     */
345 5
    protected function handlePost() : HttpResponse
346
    {
347 5
        $fileName   = $this->getRequest()->extractFileName();
348 5
        $uploadType = self::UPLOAD_TYPE_NORMAL;
349
350 5
        if (empty($fileName)) {
351 1
            return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
352
        }
353
354 4
        if ( ! $this->verifyUploadSize()) {
355 1
            return $this->response->send(null, HttpResponse::HTTP_REQUEST_ENTITY_TOO_LARGE);
356
        }
357
358 3
        $uploadKey = $this->getUploadKey();
359 3
        $filePath  = $this->uploadDir . DIRECTORY_SEPARATOR . $fileName;
360
361 3
        if ($this->getRequest()->isFinal()) {
362 1
            return $this->handleConcatenation($fileName, $filePath);
363
        }
364
365 2
        if ($this->getRequest()->isPartial()) {
366 1
            $filePath   = $this->getPathForPartialUpload($uploadKey) . $fileName;
367 1
            $uploadType = self::UPLOAD_TYPE_PARTIAL;
368
        }
369
370 2
        $checksum = $this->getClientChecksum();
371 2
        $location = $this->getRequest()->url() . $this->getApiPath() . '/' . $uploadKey;
372
373 2
        $file = $this->buildFile([
374 2
            'name' => $fileName,
375 2
            'offset' => 0,
376 2
            'size' => $this->getRequest()->header('Upload-Length'),
377 2
            'file_path' => $filePath,
378 2
            'location' => $location,
379 2
        ])->setChecksum($checksum);
380
381 2
        $this->cache->set($uploadKey, $file->details() + ['upload_type' => $uploadType]);
382
383 2
        return $this->response->send(
384 2
            ['data' => ['checksum' => $checksum]],
385 2
            HttpResponse::HTTP_CREATED,
386
            [
387 2
                'Location' => $location,
388 2
                'Upload-Expires' => $this->cache->get($uploadKey)['expires_at'],
389
            ]
390
        );
391
    }
392
393
    /**
394
     * Handle file concatenation.
395
     *
396
     * @param string $fileName
397
     * @param string $filePath
398
     *
399
     * @return HttpResponse
400
     */
401 2
    protected function handleConcatenation(string $fileName, string $filePath) : HttpResponse
402
    {
403 2
        $partials  = $this->getRequest()->extractPartials();
404 2
        $uploadKey = $this->getUploadKey();
405 2
        $files     = $this->getPartialsMeta($partials);
406 2
        $filePaths = array_column($files, 'file_path');
407 2
        $location  = $this->getRequest()->url() . $this->getApiPath() . '/' . $uploadKey;
408
409 2
        $file = $this->buildFile([
410 2
            'name' => $fileName,
411 2
            'offset' => 0,
412 2
            'size' => 0,
413 2
            'file_path' => $filePath,
414 2
            'location' => $location,
415 2
        ])->setFilePath($filePath);
416
417 2
        $file->setOffset($file->merge($files));
418
419
        // Verify checksum.
420 2
        $checksum = $this->getServerChecksum($filePath);
421
422 2
        if ($checksum !== $this->getClientChecksum()) {
423 1
            return $this->response->send(null, self::HTTP_CHECKSUM_MISMATCH);
424
        }
425
426 1
        $this->cache->set($uploadKey, $file->details() + ['upload_type' => self::UPLOAD_TYPE_FINAL]);
427
428
        // Cleanup.
429 1
        if ($file->delete($filePaths, true)) {
430 1
            $this->cache->deleteAll($partials);
431
        }
432
433 1
        return $this->response->send(
434 1
            ['data' => ['checksum' => $checksum]],
435 1
            HttpResponse::HTTP_CREATED,
436
            [
437 1
                'Location' => $location,
438
            ]
439
        );
440
    }
441
442
    /**
443
     * Handle PATCH request.
444
     *
445
     * @return HttpResponse
446
     */
447 7
    protected function handlePatch() : HttpResponse
448
    {
449 7
        $uploadKey = $this->request->key();
450
451 7
        if ( ! $meta = $this->cache->get($uploadKey)) {
452 1
            return $this->response->send(null, HttpResponse::HTTP_GONE);
453
        }
454
455 6
        if (self::UPLOAD_TYPE_FINAL === $meta['upload_type']) {
456 1
            return $this->response->send(null, HttpResponse::HTTP_FORBIDDEN);
457
        }
458
459 5
        $file     = $this->buildFile($meta);
460 5
        $checksum = $meta['checksum'];
461
462
        try {
463 5
            $fileSize = $file->getFileSize();
464 5
            $offset   = $file->setKey($uploadKey)->setChecksum($checksum)->upload($fileSize);
465
466
            // If upload is done, verify checksum.
467 2
            if ($offset === $fileSize && ! $this->verifyChecksum($checksum, $meta['file_path'])) {
468 2
                return $this->response->send(null, self::HTTP_CHECKSUM_MISMATCH);
469
            }
470 3
        } catch (FileException $e) {
471 1
            return $this->response->send($e->getMessage(), HttpResponse::HTTP_UNPROCESSABLE_ENTITY);
472 2
        } catch (OutOfRangeException $e) {
473 1
            return $this->response->send(null, HttpResponse::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE);
474 1
        } catch (ConnectionException $e) {
475 1
            return $this->response->send(null, HttpResponse::HTTP_CONTINUE);
476
        }
477
478 1
        return $this->response->send(null, HttpResponse::HTTP_NO_CONTENT, [
479 1
            'Content-Type' => 'application/offset+octet-stream',
480 1
            'Upload-Expires' => $this->cache->get($uploadKey)['expires_at'],
481 1
            'Upload-Offset' => $offset,
482
        ]);
483
    }
484
485
    /**
486
     * Handle GET request.
487
     *
488
     * @return BinaryFileResponse|HttpResponse
489
     */
490 4
    protected function handleGet()
491
    {
492 4
        $key = $this->request->key();
493
494 4
        if (empty($key)) {
495 1
            return $this->response->send('400 bad request.', HttpResponse::HTTP_BAD_REQUEST);
496
        }
497
498 3
        if ( ! $fileMeta = $this->cache->get($key)) {
499 1
            return $this->response->send('404 upload not found.', HttpResponse::HTTP_NOT_FOUND);
500
        }
501
502 2
        $resource = $fileMeta['file_path'] ?? null;
503 2
        $fileName = $fileMeta['name'] ?? null;
504
505 2
        if ( ! $resource || ! file_exists($resource)) {
506 1
            return $this->response->send('404 upload not found.', HttpResponse::HTTP_NOT_FOUND);
507
        }
508
509 1
        return $this->response->download($resource, $fileName);
510
    }
511
512
    /**
513
     * Handle DELETE request.
514
     *
515
     * @return HttpResponse
516
     */
517 3
    protected function handleDelete() : HttpResponse
518
    {
519 3
        $key      = $this->request->key();
520 3
        $fileMeta = $this->cache->get($key);
521 3
        $resource = $fileMeta['file_path'] ?? null;
522
523 3
        if ( ! $resource) {
524 1
            return $this->response->send(null, HttpResponse::HTTP_NOT_FOUND);
525
        }
526
527 2
        $isDeleted = $this->cache->delete($key);
528
529 2
        if ( ! $isDeleted || ! file_exists($resource)) {
530 1
            return $this->response->send(null, HttpResponse::HTTP_GONE);
531
        }
532
533 1
        unlink($resource);
534
535 1
        return $this->response->send(null, HttpResponse::HTTP_NO_CONTENT, [
536 1
            'Tus-Extension' => self::TUS_EXTENSION_TERMINATION,
537
        ]);
538
    }
539
540
    /**
541
     * Get required headers for head request.
542
     *
543
     * @param array $fileMeta
544
     *
545
     * @return array
546
     */
547 4
    protected function getHeadersForHeadRequest(array $fileMeta) : array
548
    {
549
        $headers = [
550 4
            'Upload-Length' => (int) $fileMeta['size'],
551 4
            'Upload-Offset' => (int) $fileMeta['offset'],
552 4
            'Cache-Control' => 'no-store',
553
        ];
554
555 4
        if (self::UPLOAD_TYPE_FINAL === $fileMeta['upload_type'] && $fileMeta['size'] !== $fileMeta['offset']) {
556 2
            unset($headers['Upload-Offset']);
557
        }
558
559 4
        if (self::UPLOAD_TYPE_NORMAL !== $fileMeta['upload_type']) {
560 3
            $headers += ['Upload-Concat' => $fileMeta['upload_type']];
561
        }
562
563 4
        return $headers;
564
    }
565
566
    /**
567
     * Build file object.
568
     *
569
     * @param array $meta
570
     *
571
     * @return File
572
     */
573 1
    protected function buildFile(array $meta) : File
574
    {
575 1
        $file = new File($meta['name'], $this->cache);
576
577 1
        if (array_key_exists('offset', $meta)) {
578 1
            $file->setMeta($meta['offset'], $meta['size'], $meta['file_path'], $meta['location']);
579
        }
580
581 1
        return $file;
582
    }
583
584
    /**
585
     * Get list of supported hash algorithms.
586
     *
587
     * @return string
588
     */
589 1
    protected function getSupportedHashAlgorithms() : string
590
    {
591 1
        $supportedAlgorithms = hash_algos();
592
593 1
        $algorithms = [];
594 1
        foreach ($supportedAlgorithms as $hashAlgo) {
595 1
            if (false !== strpos($hashAlgo, ',')) {
596 1
                $algorithms[] = "'{$hashAlgo}'";
597
            } else {
598 1
                $algorithms[] = $hashAlgo;
599
            }
600
        }
601
602 1
        return implode(',', $algorithms);
603
    }
604
605
    /**
606
     * Verify and get upload checksum from header.
607
     *
608
     * @return string|HttpResponse
609
     */
610 4
    protected function getClientChecksum()
611
    {
612 4
        $checksumHeader = $this->getRequest()->header('Upload-Checksum');
613
614 4
        if (empty($checksumHeader)) {
615 1
            return '';
616
        }
617
618 3
        list($checksumAlgorithm, $checksum) = explode(' ', $checksumHeader);
619
620 3
        $checksum = base64_decode($checksum);
621
622 3
        if ( ! in_array($checksumAlgorithm, hash_algos()) || false === $checksum) {
623 2
            return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
624
        }
625
626 1
        return $checksum;
627
    }
628
629
    /**
630
     * Get expired but incomplete uploads.
631
     *
632
     * @param array|null $contents
633
     *
634
     * @return bool
635
     */
636 3
    protected function isExpired($contents) : bool
637
    {
638 3
        $isExpired = empty($contents['expires_at']) || Carbon::parse($contents['expires_at'])->lt(Carbon::now());
639
640 3
        if ($isExpired && $contents['offset'] !== $contents['size']) {
641 3
            return true;
642
        }
643
644 2
        return false;
645
    }
646
647
    /**
648
     * Get path for partial upload.
649
     *
650
     * @param string $key
651
     *
652
     * @return string
653
     */
654 1
    protected function getPathForPartialUpload(string $key) : string
655
    {
656 1
        list($actualKey) = explode(self::PARTIAL_UPLOAD_NAME_SEPARATOR, $key);
657
658 1
        $path = $this->uploadDir . DIRECTORY_SEPARATOR . $actualKey . DIRECTORY_SEPARATOR;
659
660 1
        if ( ! file_exists($path)) {
661 1
            mkdir($path);
662
        }
663
664 1
        return $path;
665
    }
666
667
    /**
668
     * Get metadata of partials.
669
     *
670
     * @param array $partials
671
     *
672
     * @return array
673
     */
674 3
    protected function getPartialsMeta(array $partials) : array
675
    {
676 3
        $files = [];
677
678 3
        foreach ($partials as $partial) {
679 3
            $fileMeta = $this->getCache()->get($partial);
680
681 3
            $files[] = $fileMeta;
682
        }
683
684 3
        return $files;
685
    }
686
687
    /**
688
     * Delete expired resources.
689
     *
690
     * @return array
691
     */
692 2
    public function handleExpiration() : array
693
    {
694 2
        $deleted   = [];
695 2
        $cacheKeys = $this->cache->keys();
696
697 2
        foreach ($cacheKeys as $key) {
698 2
            $fileMeta = $this->cache->get($key, true);
699
700 2
            if ( ! $this->isExpired($fileMeta)) {
701 1
                continue;
702
            }
703
704 2
            if ( ! $this->cache->delete($key)) {
705 1
                continue;
706
            }
707
708 1
            if (is_writable($fileMeta['file_path'])) {
709 1
                unlink($fileMeta['file_path']);
710
            }
711
712 1
            $deleted[] = $fileMeta;
713
        }
714
715 2
        return $deleted;
716
    }
717
718
    /**
719
     * Verify max upload size.
720
     *
721
     * @return bool
722
     */
723 1
    protected function verifyUploadSize() : bool
724
    {
725 1
        $maxUploadSize = $this->getMaxUploadSize();
726
727 1
        if ($maxUploadSize > 0 && $this->getRequest()->header('Upload-Length') > $maxUploadSize) {
728 1
            return false;
729
        }
730
731 1
        return true;
732
    }
733
734
    /**
735
     * Verify checksum if available.
736
     *
737
     * @param string $checksum
738
     * @param string $filePath
739
     *
740
     * @return bool
741
     */
742 1
    protected function verifyChecksum(string $checksum, string $filePath) : bool
743
    {
744
        // Skip if checksum is empty
745 1
        if (empty($checksum)) {
746 1
            return true;
747
        }
748
749 1
        return $checksum === $this->getServerChecksum($filePath);
750
    }
751
752
    /**
753
     * No other methods are allowed.
754
     *
755
     * @param string $method
756
     * @param array  $params
757
     *
758
     * @return HttpResponse|BinaryFileResponse
759
     */
760 1
    public function __call(string $method, array $params)
761
    {
762 1
        return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
763
    }
764
}
765