Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.48% covered (success)
90.48%
19 / 21
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Assets
90.48% covered (success)
90.48%
19 / 21
66.67% covered (warning)
66.67%
2 / 3
9.07
0.00% covered (danger)
0.00%
0 / 1
 asset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 build
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 serve
89.47% covered (warning)
89.47%
17 / 19
0.00% covered (danger)
0.00%
0 / 1
7.06
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Controller\Panel;
6
7use Celemas\Core\Exception\HttpNotFound;
8use Celemas\Core\Factory\Factory;
9use Celemas\Core\Request;
10use Celemas\Core\Response;
11use Cosray\Exception\RuntimeException;
12use Cosray\Util\Path;
13
14final class Assets extends Panel
15{
16    public function asset(Request $request, Factory $factory, string $slug): Response
17    {
18        return $this->serve($request, $factory, $this->panelDir, $slug);
19    }
20
21    public function build(Request $request, Factory $factory, string $slug): Response
22    {
23        return $this->serve($request, $factory, $this->publicPanelBuildDir(), $slug);
24    }
25
26    private function serve(Request $request, Factory $factory, string $root, string $slug): Response
27    {
28        try {
29            $file = Path::inside($root, $slug, checkIsFile: true);
30        } catch (RuntimeException $e) {
31            throw new HttpNotFound($request, previous: $e);
32        }
33
34        $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
35
36        if (!in_array($ext, ['css', 'js', 'svg'], true)) {
37            throw new HttpNotFound($request);
38        }
39
40        $etag = md5_file($file);
41        $lastModified = filemtime($file);
42
43        if ($etag === false || $lastModified === false) {
44            throw new HttpNotFound($request);
45        }
46
47        $etag = '"' . $etag . '"';
48        $response = Response::create($factory)
49            ->header('Cache-Control', 'private, max-age=3600')
50            ->header('ETag', $etag)
51            ->header('Last-Modified', gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');
52        $ifNoneMatch = array_map('trim', explode(',', $request->header('If-None-Match')));
53
54        // Return 304 when the client already has this asset revision cached.
55        if (in_array('*', $ifNoneMatch, true) || in_array($etag, $ifNoneMatch, true)) {
56            return $response->status(304);
57        }
58
59        return $response->file($file);
60    }
61}