JsImportCacheBuster   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 66
Duplicated Lines 0 %

Test Coverage

Coverage 16%

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 66
ccs 4
cts 25
cp 0.16
rs 10
c 0
b 0
f 0
wmc 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B addVersionToJsImports() 0 43 9
1
<?php
2
3
namespace App\Infrastructure\JsCacheBusting;
4
5
use App\Infrastructure\Settings\Settings;
6
use FilesystemIterator;
7
use RecursiveDirectoryIterator;
8
use RecursiveIteratorIterator;
9
10
/**
11
 * Adds version number to js imports to break cache on version change.
12
 */
13
final class JsImportCacheBuster
14
{
15
    private ?string $version;
16
    private string $assetPath;
17
18 203
    public function __construct(Settings $settings)
19
    {
20 203
        $deploymentSettings = $settings->get('deployment');
21 203
        $this->version = $deploymentSettings['version'];
22 203
        $this->assetPath = $deploymentSettings['asset_path'];
23
    }
24
25
    /**
26
     * All js files inside the given directory that contain ES6 imports
27
     * are modified so that the imports have the version number at the
28
     * end of the file name as query parameters to break cache on
29
     * version change.
30
     * This function is called in PhpViewMiddleware only on dev env.
31
     * Performance wise, this function takes between 10 and 20ms when content
32
     * is unchanged and between 30 and 50ms when content is replaced.
33
     *
34
     * @return void
35
     */
36
    public function addVersionToJsImports(): void
37
    {
38
        // $start = hrtime(true);
39
        if (is_dir($this->assetPath)) {
40
            $rii = new RecursiveIteratorIterator(
41
                new RecursiveDirectoryIterator($this->assetPath, FilesystemIterator::SKIP_DOTS)
42
            );
43
44
            foreach ($rii as $file) {
45
                $fileInfo = pathinfo($file->getPathname());
46
47
                if (isset($fileInfo['extension']) && $fileInfo['extension'] === 'js') {
48
                    $content = file_get_contents($file->getPathname()) ?: '';
49
                    $originalContent = $content;
50
                    // Matches lines that have 'import ' then any string then ' from ' and single or double quote opening then
51
                    // any string (path) then '.js' and optionally v GET param '?v=234' and '";' at the end with single or double quotes
52
                    preg_match_all('/import (.|\n|\r|\t)*? from ("|\')(.*?)\.js(\?v=.*?)?("|\');/', $content, $matches);
53
                    // $matches is an array that contains all matches. In this case, the content is the following:
54
                    // Key [0] is the entire matching string including the search
55
                    // Key [1] first variable unknown string after the 'import ' word (e.g. '{requestDropdownOptions}', '{createModal}')
56
                    // Key [2] single or double quotes of path opening after "from"
57
                    // Key [3] variable unknown string after the opening single or double quotes after from (only path) e.g.
58
                    // '../general/js/requestUtil/fail-handler'
59
                    // Key [4] optional '?v=2' GET param and [5] closing quotes
60
                    // Loop over import paths
61
                    foreach ($matches[3] as $key => $importPath) {
62
                        $oldFullImport = $matches[0][$key];
63
                        // Remove query params if version is null
64
                        if ($this->version === null) {
65
                            $newImportPath = $importPath . '.js';
66
                        } else {
67
                            $newImportPath = $importPath . '.js?v=' . $this->version;
68
                        }
69
                        // Old import path potentially with GET param
70
                        $existingImportPath = $importPath . '.js' . $matches[4][$key];
71
                        // Search for old import path and replace with new one
72
                        $newFullImport = str_replace($existingImportPath, $newImportPath, $oldFullImport);
73
                        // Replace in file content
74
                        $content = str_replace($oldFullImport, $newFullImport, $content);
75
                    }
76
                    // Replace file contents with modified one if there are changes
77
                    if ($originalContent !== $content) {
78
                        file_put_contents($file->getPathname(), $content);
79
                    }
80
                }
81
            }
82
        }
83
        // Divided by a million gets milliseconds and a billion (+9) seconds
84
        // var_dump('Time used: ' . (hrtime(true) - $start) / 1e+6 . ' ms');
85
    }
86
}
87