ImageHandler   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 9
eloc 35
c 2
b 0
f 0
dl 0
loc 80
ccs 0
cts 39
cp 0
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A constructRedirectURI() 0 14 1
B handle() 0 49 7
A __construct() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Handler;
6
7
use Application\Repository\CardRepository;
8
use Application\Service\ImageResizer;
9
use Ecodev\Felix\Handler\AbstractHandler;
10
use Laminas\Diactoros\Response;
11
use Laminas\Diactoros\Response\RedirectResponse;
12
use Psr\Http\Message\ResponseInterface;
13
use Psr\Http\Message\ServerRequestInterface;
14
15
class ImageHandler extends AbstractHandler
16
{
17
    public function __construct(
18
        private readonly CardRepository $cardRepository,
19
        private readonly ImageResizer $imageResizer,
20
    ) {
21
    }
22
23
    /**
24
     * Serve an image from disk, with optional dynamic resizing.
25
     */
26
    public function handle(ServerRequestInterface $request): ResponseInterface
27
    {
28
        $id = $request->getAttribute('id');
29
30
        $card = $this->cardRepository->findOneById($id);
0 ignored issues
show
Bug introduced by
The method findOneById() does not exist on Application\Repository\CardRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

30
        /** @scrutinizer ignore-call */ 
31
        $card = $this->cardRepository->findOneById($id);
Loading history...
31
        if (!$card) {
32
            return $this->createError("Card $id not found in database");
33
        }
34
35
        $path = $card->getPath();
36
        if (!is_readable($path)) {
37
            return $this->createError("Image for card $id not found on disk, or not readable");
38
        }
39
40
        $maxHeight = (int) $request->getAttribute('maxHeight');
41
        if ($maxHeight) {
42
            $accept = $request->getHeaderLine('accept');
43
            $useWebp = str_contains($accept, 'image/webp');
44
45
            $resizeNeeded = $this->imageResizer->isResizeNeeded($card, $maxHeight, $useWebp);
46
            $resizeSpecified = array_key_exists('resize', $request->getQueryParams());
47
48
            if ($resizeNeeded && !$resizeSpecified) {
49
                // If resize is needed, user must specify a specific query params.
50
                // This allow to configure server load balance, but also work
51
                // without any special configuration if not needed.
52
                // Resizing operation is ressource intensive and could lead to block
53
                // the server if not properly configured.
54
                return new RedirectResponse($this->constructRedirectURI(), 302);
55
            }
56
57
            $path = $this->imageResizer->resize($card, $maxHeight, $useWebp);
58
        }
59
60
        $queryParams = $request->getQueryParams();
61
62
        $resource = fopen($path, 'rb');
63
        $type = mime_content_type($path);
64
        $extension = pathinfo($path, PATHINFO_EXTENSION);
65
        $filename = $id . '.' . $extension;
0 ignored issues
show
Bug introduced by
Are you sure $extension of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

65
        $filename = $id . '.' . /** @scrutinizer ignore-type */ $extension;
Loading history...
66
        $disposition = isset($queryParams['inline']) ? 'inline' : 'attachment';
67
68
        $response = new Response($resource, 200, [
69
            'content-type' => $type,
70
            'content-disposition' => $disposition . '; filename=' . $filename,
71
            'cache-control' => 'max-age=' . (24 * 60 * 60), // 24 hours cache
72
        ]);
73
74
        return $response;
75
    }
76
77
    /**
78
     * Construct a new URI for the current request with the resize query
79
     * parameter set to true.
80
     */
81
    protected function constructRedirectURI(): string
82
    {
83
        $currentUrl = $_SERVER['REQUEST_URI'];
84
        $queryString = $_SERVER['QUERY_STRING'];
85
86
        // Build the new query string
87
        parse_str($queryString, $queryArray);
88
        $queryArray['resize'] = 'true';
89
        $newQueryString = http_build_query($queryArray);
90
91
        // Reconstruct the URL with the new query string
92
        $baseUrl = strtok($currentUrl, '?');
93
94
        return $baseUrl . '?' . $newQueryString;
95
    }
96
}
97