ImageHandler::handle()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 49
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 27
c 1
b 0
f 0
dl 0
loc 49
ccs 0
cts 29
cp 0
rs 8.5546
cc 7
nc 7
nop 1
crap 56
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
     * Serve an image from disk, with optional dynamic resizing.
24
     */
25
    public function handle(ServerRequestInterface $request): ResponseInterface
26
    {
27
        $id = $request->getAttribute('id');
28
29
        $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

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

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