-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathScriptFileDetector.php
More file actions
194 lines (161 loc) · 6.93 KB
/
ScriptFileDetector.php
File metadata and controls
194 lines (161 loc) · 6.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<?php
declare(strict_types=1);
namespace MagicPush\CliToolkit\Parametizer\ScriptDetector;
use Override;
/**
* @method array<\string, \string> getDetectedData() (string) alias => (string) absolute path
* @noinspection PhpUnnecessaryFullyQualifiedNameInspection
*/
class ScriptFileDetector extends ScriptDetectorAbstract {
/** @see Parametizer::newConfig() */
protected const string SUBSTR_PARAMETIZER_CONSTRUCT = 'Parametizer::newConfig(';
/** @see Parametizer::run() */
protected const string SUBSTR_PARAMETIZER_EXEC = '->run()';
/** @see ScriptClassLauncher::create() */
protected const string SUBSTR_LAUNCHER_CONSTRUCT = 'ScriptClassLauncher::create(';
/** @see ScriptClassLauncher::execute() */
protected const string SUBSTR_LAUNCHER_EXEC = '->execute()';
/** @var array<string, string> (string) alias => (string) absolute path */
protected array $detectedFilePathsByAliases = [];
/** @var array<string, string> [both keys and values] Script file absolute paths */
protected array $searchedScriptPaths = [];
public function scriptPath(string $scriptPath): static {
$realPath = realpath($scriptPath);
try {
if (false === $realPath) {
throw new ScriptDetectorRuntimeException("Unable to retrieve the absolute path for '{$scriptPath}'");
}
if (array_key_exists($realPath, $this->searchedScriptPaths)) {
throw new ScriptDetectorRuntimeException("Duplicate script path requested: {$realPath}");
}
} catch (ScriptDetectorRuntimeException $e) {
if (!$this->throwOnException) {
return $this;
}
throw $e;
}
$this->searchedScriptPaths[$realPath] = $realPath;
return $this;
}
public function scriptPaths(array $scriptPaths): static {
foreach ($scriptPaths as $scriptPath) {
$this->scriptPath($scriptPath);
}
return $this;
}
/**
* @param bool $isForExactFile If file contents do not meet certain requirements:
* * `false`: the file will be skipped;
* * `true`: {@see ScriptDetectorRuntimeException} may be thrown,
* if {@see static::$throwOnException} is enabled.
*/
protected function processDetectedFileContentsInternal(
string $filePath,
string $fileContents,
bool $isForExactFile,
): void {
try {
$isParametizerScriptDetected =
(
str_contains($fileContents, static::SUBSTR_PARAMETIZER_CONSTRUCT)
&& str_contains($fileContents, static::SUBSTR_PARAMETIZER_EXEC)
)
|| (
str_contains($fileContents, static::SUBSTR_LAUNCHER_CONSTRUCT)
&& str_contains($fileContents, static::SUBSTR_LAUNCHER_EXEC)
);
if (!$isParametizerScriptDetected) {
throw new ScriptDetectorRuntimeException(
sprintf(
"Script file '{$filePath}' should contain one of these pairs of substrings:"
. " '%s' + '%s' OR '%s' + '%s'",
static::SUBSTR_PARAMETIZER_CONSTRUCT,
static::SUBSTR_PARAMETIZER_EXEC,
static::SUBSTR_LAUNCHER_CONSTRUCT,
static::SUBSTR_LAUNCHER_EXEC,
),
);
}
} catch (ScriptDetectorRuntimeException $e) {
if (!$isForExactFile || !$this->throwOnException) {
return;
}
throw $e;
}
$alias = basename($filePath, '.' . static::FILE_EXTENSION);
if (array_key_exists($alias, $this->detectedFilePathsByAliases)) {
if (!$this->throwOnException) {
return;
}
throw new ScriptDetectorRuntimeException(
"Duplicate script basename '{$alias}' for path '{$filePath}'."
. " Already detected: {$this->detectedFilePathsByAliases[$alias]}",
);
}
$this->detectedFilePathsByAliases[$alias] = $filePath;
}
#[Override]
protected function clearMemoryCache(): void {
$this->detectedFilePathsByAliases = [];
}
#[Override]
protected function hasMinimalCustomSearchSettings(): bool {
return (bool) $this->searchedScriptPaths;
}
#[Override]
protected function processDetectedFileContents(string $filePath, string $fileContents): void {
$this->processDetectedFileContentsInternal($filePath, $fileContents, isForExactFile: false);
}
#[Override]
protected function processCustomDetections(): void {
foreach ($this->searchedScriptPaths as $scriptPath) {
try {
if (!is_readable($scriptPath)) {
throw new ScriptDetectorRuntimeException("Script file is not readable: {$scriptPath}");
}
if (!str_ends_with($scriptPath, '.' . static::FILE_EXTENSION)) {
throw new ScriptDetectorRuntimeException(
sprintf("Missing extension '%s' for the script file: {$scriptPath}", static::FILE_EXTENSION),
);
}
$scriptContents = file_get_contents($scriptPath);
if (false === $scriptContents) {
throw new ScriptDetectorRuntimeException("Unable to read script file contents: {$scriptPath}");
}
$this->processDetectedFileContentsInternal($scriptPath, $scriptContents, isForExactFile: true);
} catch (ScriptDetectorRuntimeException $e) {
if (!$this->throwOnException) {
continue;
}
throw $e;
}
}
}
/**
* @param array<string, string> $dataFromCache (string) alias => (string) absolute path,
* same as {@see static::$detectedFilePathsByAliases}
*/
#[Override]
protected function loadDataFromCache(array $dataFromCache): void {
foreach ($dataFromCache as $alias => $filePath) {
if (!is_readable($filePath)) {
if (!$this->throwOnException) {
continue;
}
throw new ScriptDetectorRuntimeException("Script file is not readable or does not exist: {$filePath}");
}
$this->detectedFilePathsByAliases[$alias] = $filePath;
}
}
#[Override]
protected function getDataToStoreInCache(): array {
return $this->detectedFilePathsByAliases;
}
/**
* @return array<string, string> getDetectedData() (string) alias => (string) absolute path
*/
#[Override]
protected function getDataProcessedAfterDetection(): array {
return $this->detectedFilePathsByAliases;
}
}