1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | /* |
||||
6 | * This file is part of the humbug/php-scoper package. |
||||
7 | * |
||||
8 | * Copyright (c) 2017 Théo FIDRY <[email protected]>, |
||||
9 | * Pádraic Brady <[email protected]> |
||||
10 | * |
||||
11 | * For the full copyright and license information, please view the LICENSE |
||||
12 | * file that was distributed with this source code. |
||||
13 | */ |
||||
14 | |||||
15 | namespace Humbug\PhpScoper\Configuration; |
||||
16 | |||||
17 | use Humbug\PhpScoper\Patcher\ComposerPatcher; |
||||
18 | use Humbug\PhpScoper\Patcher\Patcher; |
||||
19 | use Humbug\PhpScoper\Patcher\PatcherChain; |
||||
20 | use Humbug\PhpScoper\Patcher\SymfonyPatcher; |
||||
21 | use InvalidArgumentException; |
||||
22 | use RuntimeException; |
||||
23 | use SplFileInfo; |
||||
24 | use Symfony\Component\Filesystem\Filesystem; |
||||
25 | use Symfony\Component\Finder\Finder; |
||||
0 ignored issues
–
show
|
|||||
26 | use function array_filter; |
||||
27 | use function array_key_exists; |
||||
28 | use function array_keys; |
||||
29 | use function array_map; |
||||
30 | use function array_merge; |
||||
31 | use function array_unique; |
||||
32 | use function array_unshift; |
||||
33 | use function bin2hex; |
||||
34 | use function dirname; |
||||
35 | use function file_exists; |
||||
36 | use function gettype; |
||||
37 | use function Humbug\PhpScoper\chain; |
||||
38 | use function in_array; |
||||
39 | use function is_array; |
||||
40 | use function is_callable; |
||||
41 | use function is_dir; |
||||
42 | use function is_file; |
||||
43 | use function is_link; |
||||
44 | use function is_readable; |
||||
45 | use function is_string; |
||||
46 | use function random_bytes; |
||||
47 | use function readlink as native_readlink; |
||||
48 | use function realpath; |
||||
49 | use function Safe\file_get_contents; |
||||
50 | use function Safe\sprintf; |
||||
51 | use function trim; |
||||
52 | use const DIRECTORY_SEPARATOR; |
||||
53 | use const SORT_STRING; |
||||
54 | |||||
55 | final class ConfigurationFactory |
||||
56 | { |
||||
57 | public const DEFAULT_FILE_NAME = 'scoper.inc.php'; |
||||
58 | |||||
59 | private Filesystem $fileSystem; |
||||
60 | private SymbolsConfigurationFactory $configurationWhitelistFactory; |
||||
61 | |||||
62 | public function __construct( |
||||
63 | Filesystem $fileSystem, |
||||
64 | SymbolsConfigurationFactory $configurationWhitelistFactory |
||||
65 | ) { |
||||
66 | $this->fileSystem = $fileSystem; |
||||
67 | $this->configurationWhitelistFactory = $configurationWhitelistFactory; |
||||
68 | } |
||||
69 | |||||
70 | /** |
||||
71 | * @param non-empty-string|null $path Absolute canonical path to the configuration file. |
||||
0 ignored issues
–
show
|
|||||
72 | * @param list<non-empty-string> $paths List of absolute canonical paths to append besides the one configured |
||||
73 | */ |
||||
74 | public function create(?string $path = null, array $paths = []): Configuration |
||||
75 | { |
||||
76 | if (null === $path) { |
||||
77 | $config = []; |
||||
78 | } else { |
||||
79 | $config = $this->loadConfigFile($path); |
||||
80 | } |
||||
81 | |||||
82 | self::validateConfigKeys($config); |
||||
83 | |||||
84 | $prefix = self::retrievePrefix($config); |
||||
85 | |||||
86 | $excludedFiles = null === $path |
||||
87 | ? [] |
||||
88 | : $this->retrieveExcludedFiles( |
||||
89 | dirname($path), |
||||
90 | $config, |
||||
91 | ); |
||||
92 | |||||
93 | $patchers = self::retrievePatchers($config); |
||||
94 | |||||
95 | array_unshift($patchers, new SymfonyPatcher()); |
||||
96 | array_unshift($patchers, new ComposerPatcher()); |
||||
97 | |||||
98 | $symbolsConfiguration = $this->configurationWhitelistFactory->createSymbolsConfiguration($config); |
||||
99 | |||||
100 | $finders = self::retrieveFinders($config); |
||||
101 | $filesFromPaths = self::retrieveFilesFromPaths($paths); |
||||
102 | $filesWithContents = self::retrieveFilesWithContents(chain($filesFromPaths, ...$finders)); |
||||
103 | |||||
104 | return new Configuration( |
||||
105 | $path, |
||||
106 | $prefix, |
||||
107 | $filesWithContents, |
||||
108 | self::retrieveFilesWithContents($excludedFiles), |
||||
109 | new PatcherChain($patchers), |
||||
110 | $symbolsConfiguration, |
||||
111 | ); |
||||
112 | } |
||||
113 | |||||
114 | /** |
||||
115 | * @param string[] $paths |
||||
116 | */ |
||||
117 | public function createWithPaths(Configuration $config, array $paths): Configuration |
||||
118 | { |
||||
119 | $filesWithContents = self::retrieveFilesWithContents( |
||||
120 | chain( |
||||
121 | self::retrieveFilesFromPaths( |
||||
122 | array_unique($paths, SORT_STRING), |
||||
123 | ), |
||||
124 | ), |
||||
125 | ); |
||||
126 | |||||
127 | return new Configuration( |
||||
128 | $config->getPath(), |
||||
129 | $config->getPrefix(), |
||||
130 | array_merge( |
||||
131 | $config->getFilesWithContents(), |
||||
132 | $filesWithContents, |
||||
133 | ), |
||||
134 | $config->getExcludedFilesWithContents(), |
||||
135 | $config->getPatcher(), |
||||
136 | $config->getSymbolsConfiguration(), |
||||
137 | ); |
||||
138 | } |
||||
139 | |||||
140 | public function createWithPrefix(Configuration $config, string $prefix): Configuration |
||||
141 | { |
||||
142 | $prefix = self::retrievePrefix([ConfigurationKeys::PREFIX_KEYWORD => $prefix]); |
||||
143 | |||||
144 | return new Configuration( |
||||
145 | $config->getPath(), |
||||
146 | $prefix, |
||||
147 | $config->getFilesWithContents(), |
||||
148 | $config->getExcludedFilesWithContents(), |
||||
149 | $config->getPatcher(), |
||||
150 | $config->getSymbolsConfiguration(), |
||||
151 | ); |
||||
152 | } |
||||
153 | |||||
154 | private function loadConfigFile(string $path): array |
||||
155 | { |
||||
156 | if (!$this->fileSystem->isAbsolutePath($path)) { |
||||
157 | throw new InvalidArgumentException( |
||||
158 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
159 | 'Expected the path of the configuration file to load to be an absolute path, got "%s" instead', |
||||
160 | $path, |
||||
161 | ), |
||||
162 | ); |
||||
163 | } |
||||
164 | |||||
165 | if (!file_exists($path)) { |
||||
166 | throw new InvalidArgumentException( |
||||
167 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
168 | 'Expected the path of the configuration file to exists but the file "%s" could not be found', |
||||
169 | $path, |
||||
170 | ), |
||||
171 | ); |
||||
172 | } |
||||
173 | |||||
174 | $isADirectoryLink = is_link($path) |
||||
175 | && false !== native_readlink($path) |
||||
176 | && is_file(native_readlink($path)); |
||||
177 | |||||
178 | if (!$isADirectoryLink && !is_file($path)) { |
||||
179 | throw new InvalidArgumentException( |
||||
180 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
181 | 'Expected the path of the configuration file to be a file but "%s" appears to be a directory.', |
||||
182 | $path, |
||||
183 | ), |
||||
184 | ); |
||||
185 | } |
||||
186 | |||||
187 | $config = include $path; |
||||
188 | |||||
189 | if (!is_array($config)) { |
||||
190 | throw new InvalidArgumentException( |
||||
191 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
192 | 'Expected configuration to be an array, found "%s" instead.', |
||||
193 | gettype($config), |
||||
194 | ), |
||||
195 | ); |
||||
196 | } |
||||
197 | |||||
198 | return $config; |
||||
199 | } |
||||
200 | |||||
201 | private static function validateConfigKeys(array $config): void |
||||
202 | { |
||||
203 | array_map( |
||||
204 | static fn (string $key) => self::validateConfigKey($key), |
||||
205 | array_keys($config), |
||||
206 | ); |
||||
207 | } |
||||
208 | |||||
209 | private static function validateConfigKey(string $key): void |
||||
210 | { |
||||
211 | if (in_array($key, ConfigurationKeys::KEYWORDS, true)) { |
||||
212 | return; |
||||
213 | } |
||||
214 | |||||
215 | throw new InvalidArgumentException( |
||||
216 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
217 | 'Invalid configuration key value "%s" found.', |
||||
218 | $key, |
||||
219 | ), |
||||
220 | ); |
||||
221 | } |
||||
222 | |||||
223 | /** |
||||
224 | * @return non-empty-string |
||||
0 ignored issues
–
show
|
|||||
225 | */ |
||||
226 | private static function retrievePrefix(array $config): string |
||||
227 | { |
||||
228 | $prefix = trim((string) ($config[ConfigurationKeys::PREFIX_KEYWORD] ?? '')); |
||||
229 | |||||
230 | return '' === $prefix ? self::generateRandomPrefix() : $prefix; |
||||
231 | } |
||||
232 | |||||
233 | /** |
||||
234 | * @return array<(callable(string,string,string): string)|Patcher> |
||||
0 ignored issues
–
show
|
|||||
235 | */ |
||||
236 | private static function retrievePatchers(array $config): array |
||||
237 | { |
||||
238 | if (!array_key_exists(ConfigurationKeys::PATCHERS_KEYWORD, $config)) { |
||||
239 | return []; |
||||
240 | } |
||||
241 | |||||
242 | $patchers = $config[ConfigurationKeys::PATCHERS_KEYWORD]; |
||||
243 | |||||
244 | if (!is_array($patchers)) { |
||||
245 | throw new InvalidArgumentException( |
||||
246 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
247 | 'Expected patchers to be an array of callables, found "%s" instead.', |
||||
248 | gettype($patchers), |
||||
249 | ), |
||||
250 | ); |
||||
251 | } |
||||
252 | |||||
253 | foreach ($patchers as $index => $patcher) { |
||||
254 | if (is_callable($patcher)) { |
||||
255 | continue; |
||||
256 | } |
||||
257 | |||||
258 | throw new InvalidArgumentException( |
||||
259 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
260 | 'Expected patchers to be an array of callables, the "%d" element is not.', |
||||
261 | $index, |
||||
262 | ), |
||||
263 | ); |
||||
264 | } |
||||
265 | |||||
266 | return $patchers; |
||||
267 | } |
||||
268 | |||||
269 | /** |
||||
270 | * @return string[] Absolute paths |
||||
271 | */ |
||||
272 | private function retrieveExcludedFiles(string $dirPath, array $config): array |
||||
273 | { |
||||
274 | if (!array_key_exists(ConfigurationKeys::EXCLUDED_FILES_KEYWORD, $config)) { |
||||
275 | return []; |
||||
276 | } |
||||
277 | |||||
278 | $excludedFiles = $config[ConfigurationKeys::EXCLUDED_FILES_KEYWORD]; |
||||
279 | |||||
280 | if (!is_array($excludedFiles)) { |
||||
281 | throw new InvalidArgumentException( |
||||
282 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
283 | 'Expected excluded files to be an array of strings, found "%s" instead.', |
||||
284 | gettype($excludedFiles), |
||||
285 | ), |
||||
286 | ); |
||||
287 | } |
||||
288 | |||||
289 | foreach ($excludedFiles as $index => $file) { |
||||
290 | if (!is_string($file)) { |
||||
291 | throw new InvalidArgumentException( |
||||
292 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
293 | 'Expected excluded files to be an array of string, the "%d" element is not.', |
||||
294 | $index, |
||||
295 | ), |
||||
296 | ); |
||||
297 | } |
||||
298 | |||||
299 | if (!$this->fileSystem->isAbsolutePath($file)) { |
||||
300 | $file = $dirPath.DIRECTORY_SEPARATOR.$file; |
||||
301 | } |
||||
302 | |||||
303 | $excludedFiles[$index] = realpath($file); |
||||
304 | } |
||||
305 | |||||
306 | return array_filter($excludedFiles); |
||||
307 | } |
||||
308 | |||||
309 | /** |
||||
310 | * @return Finder[] |
||||
311 | */ |
||||
312 | private static function retrieveFinders(array $config): array |
||||
313 | { |
||||
314 | if (!array_key_exists(ConfigurationKeys::FINDER_KEYWORD, $config)) { |
||||
315 | return []; |
||||
316 | } |
||||
317 | |||||
318 | $finders = $config[ConfigurationKeys::FINDER_KEYWORD]; |
||||
319 | |||||
320 | if (!is_array($finders)) { |
||||
321 | throw new InvalidArgumentException( |
||||
322 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
323 | 'Expected finders to be an array of "%s", found "%s" instead.', |
||||
324 | Finder::class, |
||||
325 | gettype($finders), |
||||
326 | ), |
||||
327 | ); |
||||
328 | } |
||||
329 | |||||
330 | foreach ($finders as $index => $finder) { |
||||
331 | if ($finder instanceof Finder) { |
||||
332 | continue; |
||||
333 | } |
||||
334 | |||||
335 | throw new InvalidArgumentException( |
||||
336 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
337 | 'Expected finders to be an array of "%s", the "%d" element is not.', |
||||
338 | Finder::class, |
||||
339 | $index, |
||||
340 | ), |
||||
341 | ); |
||||
342 | } |
||||
343 | |||||
344 | return $finders; |
||||
345 | } |
||||
346 | |||||
347 | /** |
||||
348 | * @param string[] $paths |
||||
349 | * |
||||
350 | * @return iterable<SplFileInfo> |
||||
351 | */ |
||||
352 | private static function retrieveFilesFromPaths(array $paths): iterable |
||||
353 | { |
||||
354 | if ([] === $paths) { |
||||
355 | return []; |
||||
356 | } |
||||
357 | |||||
358 | $pathsToSearch = []; |
||||
359 | $filesToAppend = []; |
||||
360 | |||||
361 | foreach ($paths as $path) { |
||||
362 | if (!file_exists($path)) { |
||||
363 | throw new RuntimeException( |
||||
364 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
365 | 'Could not find the file "%s".', |
||||
366 | $path, |
||||
367 | ), |
||||
368 | ); |
||||
369 | } |
||||
370 | |||||
371 | if (is_dir($path)) { |
||||
372 | $pathsToSearch[] = $path; |
||||
373 | } else { |
||||
374 | $filesToAppend[] = $path; |
||||
375 | } |
||||
376 | } |
||||
377 | |||||
378 | $finder = new Finder(); |
||||
379 | |||||
380 | $finder->files() |
||||
381 | ->in($pathsToSearch) |
||||
382 | ->append($filesToAppend) |
||||
383 | ->filter( |
||||
384 | static fn (SplFileInfo $fileInfo) => $fileInfo->isLink() ? false : null, |
||||
385 | ) |
||||
386 | ->sortByName(); |
||||
387 | |||||
388 | return $finder; |
||||
389 | } |
||||
390 | |||||
391 | /** |
||||
392 | * @param iterable<SplFileInfo|string> $files |
||||
393 | * |
||||
394 | * @return array<string, array{string, string}> Array of tuple with the first argument being the file path and the second its contents |
||||
395 | */ |
||||
396 | private static function retrieveFilesWithContents(iterable $files): array |
||||
397 | { |
||||
398 | $filesWithContents = []; |
||||
399 | |||||
400 | foreach ($files as $filePathOrFileInfo) { |
||||
401 | $filePath = $filePathOrFileInfo instanceof SplFileInfo |
||||
402 | ? $filePathOrFileInfo->getRealPath() |
||||
403 | : realpath($filePathOrFileInfo); |
||||
404 | |||||
405 | if (!$filePath) { |
||||
406 | throw new RuntimeException( |
||||
407 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
408 | 'Could not find the file "%s".', |
||||
409 | (string) $filePathOrFileInfo, |
||||
410 | ), |
||||
411 | ); |
||||
412 | } |
||||
413 | |||||
414 | if (!is_readable($filePath)) { |
||||
415 | throw new RuntimeException( |
||||
416 | sprintf( |
||||
0 ignored issues
–
show
The function
Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
417 | 'Could not read the file "%s".', |
||||
418 | $filePath, |
||||
419 | ), |
||||
420 | ); |
||||
421 | } |
||||
422 | |||||
423 | $filesWithContents[$filePath] = [$filePath, file_get_contents($filePath)]; |
||||
424 | } |
||||
425 | |||||
426 | return $filesWithContents; |
||||
427 | } |
||||
428 | |||||
429 | /** |
||||
430 | * @return non-empty-string |
||||
0 ignored issues
–
show
|
|||||
431 | */ |
||||
432 | private static function generateRandomPrefix(): string |
||||
433 | { |
||||
434 | return '_PhpScoper'.bin2hex(random_bytes(6)); |
||||
435 | } |
||||
436 | } |
||||
437 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths