This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | require_once('command_functions.php'); |
||
3 | |||
4 | use Psalm\DocComment; |
||
5 | use Psalm\Internal\Analyzer\ProjectAnalyzer; |
||
6 | use Psalm\Config; |
||
7 | use Psalm\Internal\IncludeCollector; |
||
8 | use Psalm\IssueBuffer; |
||
9 | use Psalm\Progress\DebugProgress; |
||
10 | use Psalm\Progress\DefaultProgress; |
||
11 | |||
12 | // show all errors |
||
13 | error_reporting(-1); |
||
14 | ini_set('display_errors', '1'); |
||
15 | ini_set('display_startup_errors', '1'); |
||
16 | $memLimit = getMemoryLimitInBytes(); |
||
17 | // Magic number is 4096M in bytes |
||
18 | if ($memLimit > 0 && $memLimit < 8 * 1024 * 1024 * 1024) { |
||
19 | ini_set('memory_limit', (string) (8 * 1024 * 1024 * 1024)); |
||
20 | } |
||
21 | |||
22 | gc_collect_cycles(); |
||
23 | gc_disable(); |
||
24 | |||
25 | require_once __DIR__ . '/Psalm/Internal/exception_handler.php'; |
||
26 | |||
27 | $args = array_slice($argv, 1); |
||
28 | |||
29 | $valid_short_options = ['f:', 'm', 'h', 'r:', 'c:']; |
||
30 | $valid_long_options = [ |
||
31 | 'help', 'debug', 'debug-by-line', 'debug-emitted-issues', 'config:', 'file:', 'root:', |
||
32 | 'plugin:', 'issues:', 'list-supported-issues', 'php-version:', 'dry-run', 'safe-types', |
||
33 | 'find-unused-code', 'threads:', 'codeowner:', |
||
34 | 'allow-backwards-incompatible-changes:', |
||
35 | 'add-newline-between-docblock-annotations:', |
||
36 | 'no-cache' |
||
37 | ]; |
||
38 | |||
39 | // get options from command line |
||
40 | $options = getopt(implode('', $valid_short_options), $valid_long_options); |
||
41 | |||
42 | array_map( |
||
43 | /** |
||
44 | * @param string $arg |
||
45 | * |
||
46 | * @return void |
||
47 | */ |
||
48 | View Code Duplication | function ($arg) use ($valid_long_options, $valid_short_options) { |
|
0 ignored issues
–
show
|
|||
49 | if (substr($arg, 0, 2) === '--' && $arg !== '--') { |
||
50 | $arg_name = preg_replace('/=.*$/', '', substr($arg, 2)); |
||
51 | |||
52 | if ($arg_name === 'alter') { |
||
53 | // valid option for psalm, ignored by psalter |
||
54 | return; |
||
55 | } |
||
56 | |||
57 | if (!in_array($arg_name, $valid_long_options) |
||
58 | && !in_array($arg_name . ':', $valid_long_options) |
||
59 | && !in_array($arg_name . '::', $valid_long_options) |
||
60 | ) { |
||
61 | fwrite( |
||
62 | STDERR, |
||
63 | 'Unrecognised argument "--' . $arg_name . '"' . PHP_EOL |
||
64 | . 'Type --help to see a list of supported arguments'. PHP_EOL |
||
65 | ); |
||
66 | exit(1); |
||
67 | } |
||
68 | } elseif (substr($arg, 0, 2) === '-' && $arg !== '-' && $arg !== '--') { |
||
69 | $arg_name = preg_replace('/=.*$/', '', substr($arg, 1)); |
||
70 | |||
71 | if (!in_array($arg_name, $valid_short_options) && !in_array($arg_name . ':', $valid_short_options)) { |
||
72 | fwrite( |
||
73 | STDERR, |
||
74 | 'Unrecognised argument "-' . $arg_name . '"' . PHP_EOL |
||
75 | . 'Type --help to see a list of supported arguments'. PHP_EOL |
||
76 | ); |
||
77 | exit(1); |
||
78 | } |
||
79 | } |
||
80 | }, |
||
81 | $args |
||
82 | ); |
||
83 | |||
84 | if (array_key_exists('help', $options)) { |
||
85 | $options['h'] = false; |
||
86 | } |
||
87 | |||
88 | if (array_key_exists('monochrome', $options)) { |
||
89 | $options['m'] = false; |
||
90 | } |
||
91 | |||
92 | if (isset($options['config'])) { |
||
93 | $options['c'] = $options['config']; |
||
94 | } |
||
95 | |||
96 | View Code Duplication | if (isset($options['c']) && is_array($options['c'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
97 | die('Too many config files provided' . PHP_EOL); |
||
98 | } |
||
99 | |||
100 | if (array_key_exists('h', $options)) { |
||
101 | echo <<< HELP |
||
102 | Usage: |
||
103 | psalter [options] [file...] |
||
104 | |||
105 | Options: |
||
106 | -h, --help |
||
107 | Display this help message |
||
108 | |||
109 | --debug, --debug-by-line, --debug-emitted-issues |
||
110 | Debug information |
||
111 | |||
112 | -c, --config=psalm.xml |
||
113 | Path to a psalm.xml configuration file. Run psalm --init to create one. |
||
114 | |||
115 | -m, --monochrome |
||
116 | Enable monochrome output |
||
117 | |||
118 | -r, --root |
||
119 | If running Psalm globally you'll need to specify a project root. Defaults to cwd |
||
120 | |||
121 | --plugin=PATH |
||
122 | Executes a plugin, an alternative to using the Psalm config |
||
123 | |||
124 | --dry-run |
||
125 | Shows a diff of all the changes, without making them |
||
126 | |||
127 | --safe-types |
||
128 | Only update PHP types when the new type information comes from other PHP types, |
||
129 | as opposed to type information that just comes from docblocks |
||
130 | |||
131 | --php-version=PHP_MAJOR_VERSION.PHP_MINOR_VERSION |
||
132 | |||
133 | --issues=IssueType1,IssueType2 |
||
134 | If any issues can be fixed automatically, Psalm will update the codebase. To fix as many issues as possible, |
||
135 | use --issues=all |
||
136 | |||
137 | --list-supported-issues |
||
138 | Display the list of issues that psalter knows how to fix |
||
139 | |||
140 | --find-unused-code |
||
141 | Include unused code as a candidate for removal |
||
142 | |||
143 | --threads=INT |
||
144 | If greater than one, Psalm will run analysis on multiple threads, speeding things up. |
||
145 | |||
146 | --codeowner=[codeowner] |
||
147 | You can specify a GitHub code ownership group, and only that owner's code will be updated. |
||
148 | |||
149 | --allow-backwards-incompatible-changes=BOOL |
||
150 | Allow Psalm modify method signatures that could break code outside the project. Defaults to true. |
||
151 | |||
152 | --add-newline-between-docblock-annotations=BOOL |
||
153 | Whether to add or not add a new line between docblock annotations. Defaults to true. |
||
154 | |||
155 | --no-cache |
||
156 | Runs Psalm without using cache |
||
157 | HELP; |
||
158 | |||
159 | exit; |
||
160 | } |
||
161 | |||
162 | if (!isset($options['issues']) && |
||
163 | !isset($options['list-supported-issues']) && |
||
164 | (!isset($options['plugin']) || $options['plugin'] === false) |
||
165 | ) { |
||
166 | fwrite(STDERR, 'Please specify the issues you want to fix with --issues=IssueOne,IssueTwo or --issues=all, ' . |
||
167 | 'or provide a plugin that has its own manipulations with --plugin=path/to/plugin.php' . PHP_EOL); |
||
168 | exit(1); |
||
169 | } |
||
170 | |||
171 | if (isset($options['root'])) { |
||
172 | $options['r'] = $options['root']; |
||
173 | } |
||
174 | |||
175 | $current_dir = (string)getcwd() . DIRECTORY_SEPARATOR; |
||
176 | |||
177 | View Code Duplication | if (isset($options['r']) && is_string($options['r'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
178 | $root_path = realpath($options['r']); |
||
179 | |||
180 | if (!$root_path) { |
||
181 | die('Could not locate root directory ' . $current_dir . DIRECTORY_SEPARATOR . $options['r'] . PHP_EOL); |
||
182 | } |
||
183 | |||
184 | $current_dir = $root_path . DIRECTORY_SEPARATOR; |
||
185 | } |
||
186 | |||
187 | $vendor_dir = getVendorDir($current_dir); |
||
188 | |||
189 | require_once __DIR__ . '/Psalm/Internal/IncludeCollector.php'; |
||
190 | $include_collector = new IncludeCollector(); |
||
191 | $first_autoloader = $include_collector->runAndCollect( |
||
192 | function () use ($current_dir, $options, $vendor_dir) { |
||
193 | return requireAutoloaders($current_dir, isset($options['r']), $vendor_dir); |
||
194 | } |
||
195 | ); |
||
196 | |||
197 | |||
198 | // If Xdebug is enabled, restart without it |
||
199 | (new \Composer\XdebugHandler\XdebugHandler('PSALTER'))->check(); |
||
200 | |||
201 | $paths_to_check = getPathsToCheck(isset($options['f']) ? $options['f'] : null); |
||
202 | |||
203 | $path_to_config = get_path_to_config($options); |
||
204 | |||
205 | $config = initialiseConfig($path_to_config, $current_dir, \Psalm\Report::TYPE_CONSOLE, $first_autoloader); |
||
206 | $config->setIncludeCollector($include_collector); |
||
207 | |||
208 | if ($config->resolve_from_config_file) { |
||
209 | $current_dir = $config->base_dir; |
||
210 | chdir($current_dir); |
||
211 | } |
||
212 | |||
213 | $threads = isset($options['threads']) ? (int)$options['threads'] : 1; |
||
214 | |||
215 | if (isset($options['no-cache'])) { |
||
216 | $providers = new Psalm\Internal\Provider\Providers( |
||
217 | new Psalm\Internal\Provider\FileProvider() |
||
218 | ); |
||
219 | } else { |
||
220 | $providers = new Psalm\Internal\Provider\Providers( |
||
221 | new Psalm\Internal\Provider\FileProvider(), |
||
222 | new Psalm\Internal\Provider\ParserCacheProvider($config, false), |
||
223 | new Psalm\Internal\Provider\FileStorageCacheProvider($config), |
||
224 | new Psalm\Internal\Provider\ClassLikeStorageCacheProvider($config), |
||
225 | null, |
||
226 | new Psalm\Internal\Provider\ProjectCacheProvider($current_dir . DIRECTORY_SEPARATOR . 'composer.lock') |
||
227 | ); |
||
228 | } |
||
229 | |||
230 | if (array_key_exists('list-supported-issues', $options)) { |
||
231 | echo implode(',', ProjectAnalyzer::getSupportedIssuesToFix()) . PHP_EOL; |
||
232 | exit(); |
||
233 | } |
||
234 | |||
235 | $debug = array_key_exists('debug', $options) || array_key_exists('debug-by-line', $options); |
||
236 | $progress = $debug |
||
237 | ? new DebugProgress() |
||
238 | : new DefaultProgress(); |
||
239 | |||
240 | $stdout_report_options = new \Psalm\Report\ReportOptions(); |
||
241 | $stdout_report_options->use_color = !array_key_exists('m', $options); |
||
242 | |||
243 | $project_analyzer = new ProjectAnalyzer( |
||
244 | $config, |
||
245 | $providers, |
||
246 | $stdout_report_options, |
||
247 | [], |
||
248 | $threads, |
||
249 | $progress |
||
250 | ); |
||
251 | |||
252 | if (array_key_exists('debug-by-line', $options)) { |
||
253 | $project_analyzer->debug_lines = true; |
||
254 | } |
||
255 | |||
256 | if (array_key_exists('debug-emitted-issues', $options)) { |
||
257 | $config->debug_emitted_issues = true; |
||
258 | } |
||
259 | |||
260 | $config->visitComposerAutoloadFiles($project_analyzer); |
||
261 | |||
262 | if (array_key_exists('issues', $options)) { |
||
263 | if (!is_string($options['issues']) || !$options['issues']) { |
||
264 | die('Expecting a comma-separated list of issues' . PHP_EOL); |
||
265 | } |
||
266 | |||
267 | $issues = explode(',', $options['issues']); |
||
268 | |||
269 | $keyed_issues = []; |
||
270 | |||
271 | foreach ($issues as $issue) { |
||
272 | $keyed_issues[$issue] = true; |
||
273 | } |
||
274 | } else { |
||
275 | $keyed_issues = []; |
||
276 | } |
||
277 | |||
278 | View Code Duplication | if (isset($options['php-version'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
279 | if (!is_string($options['php-version'])) { |
||
280 | die('Expecting a version number in the format x.y' . PHP_EOL); |
||
281 | } |
||
282 | |||
283 | $project_analyzer->setPhpVersion($options['php-version']); |
||
284 | } |
||
285 | |||
286 | if (isset($options['codeowner'])) { |
||
287 | if (file_exists('CODEOWNERS')) { |
||
288 | $codeowners_file_path = realpath('CODEOWNERS'); |
||
289 | } elseif (file_exists('.github/CODEOWNERS')) { |
||
290 | $codeowners_file_path = realpath('.github/CODEOWNERS'); |
||
291 | } elseif (file_exists('docs/CODEOWNERS')) { |
||
292 | $codeowners_file_path = realpath('docs/CODEOWNERS'); |
||
293 | } else { |
||
294 | die('Cannot use --codeowner without a CODEOWNERS file' . PHP_EOL); |
||
295 | } |
||
296 | |||
297 | $codeowners_file = file_get_contents($codeowners_file_path); |
||
298 | |||
299 | $codeowner_lines = array_map( |
||
300 | function (string $line) : array { |
||
301 | $line_parts = preg_split('/\s+/', $line); |
||
302 | |||
303 | $file_selector = substr(array_shift($line_parts), 1); |
||
304 | return [$file_selector, $line_parts]; |
||
305 | }, |
||
306 | array_filter( |
||
307 | explode("\n", $codeowners_file), |
||
308 | function (string $line) : bool { |
||
309 | $line = trim($line); |
||
310 | |||
311 | // currently we don’t match wildcard files or files that could appear anywhere |
||
312 | // in the repo |
||
313 | return $line && $line[0] === '/' && strpos($line, '*') === false; |
||
314 | } |
||
315 | ) |
||
316 | ); |
||
317 | |||
318 | $codeowner_files = []; |
||
319 | |||
320 | foreach ($codeowner_lines as list($path, $owners)) { |
||
321 | if (!file_exists($path)) { |
||
322 | continue; |
||
323 | } |
||
324 | |||
325 | foreach ($owners as $i => $owner) { |
||
326 | $owners[$i] = strtolower($owner); |
||
327 | } |
||
328 | |||
329 | if (!is_dir($path)) { |
||
330 | if (pathinfo($path, PATHINFO_EXTENSION) === 'php') { |
||
331 | $codeowner_files[$path] = $owners; |
||
332 | } |
||
333 | } else { |
||
334 | foreach ($providers->file_provider->getFilesInDir($path, ['php']) as $php_file_path) { |
||
335 | $codeowner_files[$php_file_path] = $owners; |
||
336 | } |
||
337 | } |
||
338 | } |
||
339 | |||
340 | if (!$codeowner_files) { |
||
341 | die('Could not find any available entries in CODEOWNERS' . PHP_EOL); |
||
342 | } |
||
343 | |||
344 | $desired_codeowners = is_array($options['codeowner']) ? $options['codeowner'] : [$options['codeowner']]; |
||
345 | |||
346 | /** @psalm-suppress MixedAssignment */ |
||
347 | foreach ($desired_codeowners as $desired_codeowner) { |
||
348 | if (!is_string($desired_codeowner)) { |
||
349 | die('Invalid --codeowner ' . (string)$desired_codeowner . PHP_EOL); |
||
350 | } |
||
351 | |||
352 | if ($desired_codeowner[0] !== '@') { |
||
353 | die('--codeowner option must start with @' . PHP_EOL); |
||
354 | } |
||
355 | |||
356 | $matched_file = false; |
||
357 | |||
358 | foreach ($codeowner_files as $file_path => $owners) { |
||
359 | if (in_array(strtolower($desired_codeowner), $owners)) { |
||
360 | $paths_to_check[] = $file_path; |
||
361 | $matched_file = true; |
||
362 | } |
||
363 | } |
||
364 | |||
365 | if (!$matched_file) { |
||
366 | die('User/group ' . $desired_codeowner . ' does not own any PHP files' . PHP_EOL); |
||
367 | } |
||
368 | } |
||
369 | } |
||
370 | |||
371 | if (isset($options['allow-backwards-incompatible-changes'])) { |
||
372 | $allow_backwards_incompatible_changes = filter_var( |
||
373 | $options['allow-backwards-incompatible-changes'], |
||
374 | FILTER_VALIDATE_BOOLEAN, |
||
375 | ['flags' => FILTER_NULL_ON_FAILURE] |
||
376 | ); |
||
377 | |||
378 | if ($allow_backwards_incompatible_changes === null) { |
||
379 | die('--allow-backwards-incompatible-changes expects a boolean value [true|false|1|0]' . PHP_EOL); |
||
380 | } |
||
381 | |||
382 | $project_analyzer->getCodebase()->allow_backwards_incompatible_changes = $allow_backwards_incompatible_changes; |
||
383 | } |
||
384 | |||
385 | if (isset($options['add-newline-between-docblock-annotations'])) { |
||
386 | $doc_block_add_new_line_before_return = filter_var( |
||
387 | $options['add-newline-between-docblock-annotations'], |
||
388 | FILTER_VALIDATE_BOOLEAN, |
||
389 | ['flags' => FILTER_NULL_ON_FAILURE] |
||
390 | ); |
||
391 | |||
392 | if ($doc_block_add_new_line_before_return === null) { |
||
393 | die('--add-newline-between-docblock-annotations expects a boolean value [true|false|1|0]' . PHP_EOL); |
||
394 | } |
||
395 | |||
396 | Psalm\Internal\Scanner\ParsedDocblock::addNewLineBetweenAnnotations($doc_block_add_new_line_before_return); |
||
397 | } |
||
398 | |||
399 | $plugins = []; |
||
400 | |||
401 | View Code Duplication | if (isset($options['plugin'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
402 | $plugins = $options['plugin']; |
||
403 | |||
404 | if (!is_array($plugins)) { |
||
405 | $plugins = [$plugins]; |
||
406 | } |
||
407 | } |
||
408 | |||
409 | /** @var string $plugin_path */ |
||
410 | foreach ($plugins as $plugin_path) { |
||
411 | Config::getInstance()->addPluginPath($current_dir . $plugin_path); |
||
412 | } |
||
413 | |||
414 | $find_unused_code = array_key_exists('find-unused-code', $options); |
||
415 | |||
416 | foreach ($keyed_issues as $issue_name => $_) { |
||
417 | // MissingParamType requires the scanning of all files to inform possible params |
||
418 | if (strpos($issue_name, 'Unused') !== false |
||
419 | || $issue_name === 'MissingParamType' |
||
420 | || $issue_name === 'UnnecessaryVarAnnotation' |
||
421 | || $issue_name === 'all' |
||
422 | ) { |
||
423 | $find_unused_code = true; |
||
424 | } |
||
425 | } |
||
426 | |||
427 | if ($find_unused_code) { |
||
428 | $project_analyzer->getCodebase()->reportUnusedCode(); |
||
429 | } |
||
430 | |||
431 | $project_analyzer->alterCodeAfterCompletion( |
||
432 | array_key_exists('dry-run', $options), |
||
433 | array_key_exists('safe-types', $options) |
||
434 | ); |
||
435 | |||
436 | if ($keyed_issues === ['all' => true]) { |
||
437 | $project_analyzer->setAllIssuesToFix(); |
||
438 | } else { |
||
439 | try { |
||
440 | $project_analyzer->setIssuesToFix($keyed_issues); |
||
441 | } catch (\Psalm\Exception\UnsupportedIssueToFixException $e) { |
||
442 | fwrite(STDERR, $e->getMessage() . PHP_EOL); |
||
443 | exit(1); |
||
444 | } |
||
445 | } |
||
446 | |||
447 | $start_time = microtime(true); |
||
448 | |||
449 | if ($paths_to_check === null || count($paths_to_check) > 1 || $find_unused_code) { |
||
450 | if ($paths_to_check) { |
||
451 | $files_to_update = []; |
||
452 | |||
453 | foreach ($paths_to_check as $path_to_check) { |
||
454 | if (!is_dir($path_to_check)) { |
||
455 | $files_to_update[] = (string) realpath($path_to_check); |
||
456 | } else { |
||
457 | foreach ($providers->file_provider->getFilesInDir($path_to_check, ['php']) as $php_file_path) { |
||
458 | $files_to_update[] = $php_file_path; |
||
459 | } |
||
460 | } |
||
461 | } |
||
462 | |||
463 | $project_analyzer->getCodebase()->analyzer->setFilesToUpdate($files_to_update); |
||
464 | } |
||
465 | |||
466 | $project_analyzer->check($current_dir); |
||
467 | } elseif ($paths_to_check) { |
||
468 | foreach ($paths_to_check as $path_to_check) { |
||
469 | if (is_dir($path_to_check)) { |
||
470 | $project_analyzer->checkDir($path_to_check); |
||
471 | } else { |
||
472 | $project_analyzer->checkFile($path_to_check); |
||
473 | } |
||
474 | } |
||
475 | } |
||
476 | |||
477 | IssueBuffer::finish($project_analyzer, false, $start_time); |
||
478 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.