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