Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
80.00% covered (success)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
AssetVersionTwigExtension
80.00% covered (success)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 2
5.20
0.00% covered (danger)
0.00%
0 / 1
 asset
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 versionedPath
88.89% covered (success)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
4.02
1<?php
2
3/**
4 * Twig extension to append a cache-busting version to built asset URLs
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ\Template
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2026-07-14
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Twig\Extensions;
21
22use Twig\Attribute\AsTwigFunction;
23use Twig\Extension\AbstractExtension;
24
25/**
26 * Class AssetVersionTwigExtension
27 *
28 * Appends the asset file's modification time as a ?v= query parameter, so
29 * browsers re-fetch scripts and stylesheets after an upgrade instead of
30 * serving stale cached copies, while unchanged files stay cacheable.
31 */
32class AssetVersionTwigExtension extends AbstractExtension
33{
34    #[AsTwigFunction(name: 'asset')]
35    public static function asset(string $path): string
36    {
37        return self::versionedPath($path, (string) PMF_ROOT_DIR);
38    }
39
40    public static function versionedPath(string $path, string $rootDir): string
41    {
42        if (str_contains($path, '..')) {
43            return $path;
44        }
45
46        $file = rtrim(string: $rootDir, characters: '/') . '/' . ltrim(string: $path, characters: '/');
47        if (!is_file($file)) {
48            return $path;
49        }
50
51        $modificationTime = filemtime($file);
52        if ($modificationTime === false) {
53            return $path;
54        }
55
56        return $path . '?v=' . $modificationTime;
57    }
58}