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\ErrorBaseline; |
||
5 | use Psalm\Internal\Analyzer\ProjectAnalyzer; |
||
6 | use Psalm\Internal\Provider; |
||
7 | use Psalm\Config; |
||
8 | use Psalm\Internal\IncludeCollector; |
||
9 | use Psalm\IssueBuffer; |
||
10 | use Psalm\Progress\DebugProgress; |
||
11 | use Psalm\Progress\DefaultProgress; |
||
12 | use Psalm\Progress\LongProgress; |
||
13 | use Psalm\Progress\VoidProgress; |
||
14 | |||
15 | // show all errors |
||
16 | error_reporting(-1); |
||
17 | |||
18 | $valid_short_options = [ |
||
19 | 'f:', |
||
20 | 'm', |
||
21 | 'h', |
||
22 | 'v', |
||
23 | 'c:', |
||
24 | 'i', |
||
25 | 'r:', |
||
26 | ]; |
||
27 | |||
28 | $valid_long_options = [ |
||
29 | 'clear-cache', |
||
30 | 'clear-global-cache', |
||
31 | 'config:', |
||
32 | 'debug', |
||
33 | 'debug-by-line', |
||
34 | 'debug-emitted-issues', |
||
35 | 'diff', |
||
36 | 'diff-methods', |
||
37 | 'disable-extension:', |
||
38 | 'find-dead-code::', |
||
39 | 'find-unused-code::', |
||
40 | 'find-unused-variables', |
||
41 | 'find-references-to:', |
||
42 | 'help', |
||
43 | 'ignore-baseline', |
||
44 | 'init', |
||
45 | 'monochrome', |
||
46 | 'no-cache', |
||
47 | 'no-reflection-cache', |
||
48 | 'no-file-cache', |
||
49 | 'output-format:', |
||
50 | 'plugin:', |
||
51 | 'report:', |
||
52 | 'report-show-info:', |
||
53 | 'root:', |
||
54 | 'set-baseline:', |
||
55 | 'show-info:', |
||
56 | 'show-snippet:', |
||
57 | 'stats', |
||
58 | 'threads:', |
||
59 | 'update-baseline', |
||
60 | 'use-baseline:', |
||
61 | 'use-ini-defaults', |
||
62 | 'version', |
||
63 | 'php-version:', |
||
64 | 'generate-json-map:', |
||
65 | 'generate-stubs:', |
||
66 | 'alter', |
||
67 | 'language-server', |
||
68 | 'refactor', |
||
69 | 'shepherd::', |
||
70 | 'no-progress', |
||
71 | 'long-progress', |
||
72 | 'no-suggestions', |
||
73 | 'include-php-versions', // used for baseline |
||
74 | 'pretty-print', // used for JSON reports |
||
75 | 'track-tainted-input', |
||
76 | 'taint-analysis', |
||
77 | 'security-analysis', |
||
78 | 'find-unused-psalm-suppress', |
||
79 | 'error-level:', |
||
80 | ]; |
||
81 | |||
82 | gc_collect_cycles(); |
||
83 | gc_disable(); |
||
84 | |||
85 | $args = array_slice($argv, 1); |
||
86 | |||
87 | // get options from command line |
||
88 | $options = getopt(implode('', $valid_short_options), $valid_long_options); |
||
89 | |||
90 | if (isset($options['alter'])) { |
||
91 | require_once __DIR__ . '/psalter.php'; |
||
92 | exit; |
||
93 | } |
||
94 | |||
95 | if (isset($options['language-server'])) { |
||
96 | require_once __DIR__ . '/psalm-language-server.php'; |
||
97 | exit; |
||
98 | } |
||
99 | |||
100 | if (isset($options['refactor'])) { |
||
101 | require_once __DIR__ . '/psalm-refactor.php'; |
||
102 | exit; |
||
103 | } |
||
104 | |||
105 | require_once __DIR__ . '/Psalm/Internal/exception_handler.php'; |
||
106 | |||
107 | array_map( |
||
108 | /** |
||
109 | * @param string $arg |
||
110 | * |
||
111 | * @return void |
||
112 | */ |
||
113 | View Code Duplication | function ($arg) use ($valid_long_options, $valid_short_options) { |
|
114 | if (substr($arg, 0, 2) === '--' && $arg !== '--') { |
||
115 | $arg_name = preg_replace('/=.*$/', '', substr($arg, 2)); |
||
116 | |||
117 | if (!in_array($arg_name, $valid_long_options) |
||
118 | && !in_array($arg_name . ':', $valid_long_options) |
||
119 | && !in_array($arg_name . '::', $valid_long_options) |
||
120 | ) { |
||
121 | fwrite( |
||
122 | STDERR, |
||
123 | 'Unrecognised argument "--' . $arg_name . '"' . PHP_EOL |
||
124 | . 'Type --help to see a list of supported arguments'. PHP_EOL |
||
125 | ); |
||
126 | exit(1); |
||
127 | } |
||
128 | } elseif (substr($arg, 0, 1) === '-' && $arg !== '-' && $arg !== '--') { |
||
129 | $arg_name = preg_replace('/=.*$/', '', substr($arg, 1)); |
||
130 | |||
131 | if (!in_array($arg_name, $valid_short_options) && !in_array($arg_name . ':', $valid_short_options)) { |
||
132 | fwrite( |
||
133 | STDERR, |
||
134 | 'Unrecognised argument "-' . $arg_name . '"' . PHP_EOL |
||
135 | . 'Type --help to see a list of supported arguments'. PHP_EOL |
||
136 | ); |
||
137 | exit(1); |
||
138 | } |
||
139 | } |
||
140 | }, |
||
141 | $args |
||
142 | ); |
||
143 | |||
144 | View Code Duplication | if (!array_key_exists('use-ini-defaults', $options)) { |
|
145 | ini_set('display_errors', 'stderr'); |
||
146 | ini_set('display_startup_errors', '1'); |
||
147 | ini_set('memory_limit', (string) (8 * 1024 * 1024 * 1024)); |
||
148 | } |
||
149 | |||
150 | if (array_key_exists('help', $options)) { |
||
151 | $options['h'] = false; |
||
152 | } |
||
153 | |||
154 | if (array_key_exists('version', $options)) { |
||
155 | $options['v'] = false; |
||
156 | } |
||
157 | |||
158 | if (array_key_exists('init', $options)) { |
||
159 | $options['i'] = false; |
||
160 | } |
||
161 | |||
162 | if (array_key_exists('monochrome', $options)) { |
||
163 | $options['m'] = false; |
||
164 | } |
||
165 | |||
166 | if (isset($options['config'])) { |
||
167 | $options['c'] = $options['config']; |
||
168 | } |
||
169 | |||
170 | View Code Duplication | if (isset($options['c']) && is_array($options['c'])) { |
|
171 | fwrite(STDERR, 'Too many config files provided' . PHP_EOL); |
||
172 | exit(1); |
||
173 | } |
||
174 | |||
175 | |||
176 | if (array_key_exists('h', $options)) { |
||
177 | echo getPsalmHelpText(); |
||
178 | /* |
||
179 | --shepherd[=host] |
||
180 | Send data to Shepherd, Psalm's GitHub integration tool. |
||
181 | `host` is the location of the Shepherd server. It defaults to shepherd.dev |
||
182 | More information is available at https://psalm.dev/shepherd |
||
183 | */ |
||
184 | |||
185 | exit; |
||
186 | } |
||
187 | |||
188 | View Code Duplication | if (getcwd() === false) { |
|
189 | fwrite(STDERR, 'Cannot get current working directory' . PHP_EOL); |
||
190 | exit(1); |
||
191 | } |
||
192 | |||
193 | if (isset($options['root'])) { |
||
194 | $options['r'] = $options['root']; |
||
195 | } |
||
196 | |||
197 | $current_dir = (string)getcwd() . DIRECTORY_SEPARATOR; |
||
198 | |||
199 | View Code Duplication | if (isset($options['r']) && is_string($options['r'])) { |
|
200 | $root_path = realpath($options['r']); |
||
201 | |||
202 | if (!$root_path) { |
||
203 | fwrite( |
||
204 | STDERR, |
||
205 | 'Could not locate root directory ' . $current_dir . DIRECTORY_SEPARATOR . $options['r'] . PHP_EOL |
||
206 | ); |
||
207 | exit(1); |
||
208 | } |
||
209 | |||
210 | $current_dir = $root_path . DIRECTORY_SEPARATOR; |
||
211 | } |
||
212 | |||
213 | $path_to_config = get_path_to_config($options); |
||
214 | |||
215 | $vendor_dir = getVendorDir($current_dir); |
||
216 | |||
217 | require_once __DIR__ . '/' . 'Psalm/Internal/IncludeCollector.php'; |
||
218 | |||
219 | $include_collector = new IncludeCollector(); |
||
220 | $first_autoloader = $include_collector->runAndCollect( |
||
221 | function () use ($current_dir, $options, $vendor_dir) { |
||
222 | return requireAutoloaders($current_dir, isset($options['r']), $vendor_dir); |
||
223 | } |
||
224 | ); |
||
225 | |||
226 | |||
227 | if (array_key_exists('v', $options)) { |
||
228 | echo 'Psalm ' . PSALM_VERSION . PHP_EOL; |
||
229 | exit; |
||
230 | } |
||
231 | |||
232 | $output_format = isset($options['output-format']) && is_string($options['output-format']) |
||
233 | ? $options['output-format'] |
||
234 | : \Psalm\Report::TYPE_CONSOLE; |
||
235 | |||
236 | $init_level = null; |
||
237 | $init_source_dir = null; |
||
238 | |||
239 | if (isset($options['i'])) { |
||
240 | if (file_exists($current_dir . 'psalm.xml')) { |
||
241 | die('A config file already exists in the current directory' . PHP_EOL); |
||
242 | } |
||
243 | |||
244 | $args = array_values(array_filter( |
||
245 | $args, |
||
246 | /** |
||
247 | * @param string $arg |
||
248 | * |
||
249 | * @return bool |
||
250 | */ |
||
251 | function ($arg) { |
||
252 | return $arg !== '--ansi' |
||
253 | && $arg !== '--no-ansi' |
||
254 | && $arg !== '-i' |
||
255 | && $arg !== '--init' |
||
256 | && $arg !== '--debug' |
||
257 | && $arg !== '--debug-by-line' |
||
258 | && $arg !== '--debug-emitted-issues' |
||
259 | && strpos($arg, '--disable-extension=') !== 0 |
||
260 | && strpos($arg, '--root=') !== 0 |
||
261 | && strpos($arg, '--r=') !== 0; |
||
262 | } |
||
263 | )); |
||
264 | |||
265 | if (count($args)) { |
||
266 | if (count($args) > 2) { |
||
267 | die('Too many arguments provided for psalm --init' . PHP_EOL); |
||
268 | } |
||
269 | |||
270 | if (isset($args[1])) { |
||
271 | if (!preg_match('/^[1-8]$/', $args[1])) { |
||
272 | die('Config strictness must be a number between 1 and 8 inclusive' . PHP_EOL); |
||
273 | } |
||
274 | |||
275 | $init_level = (int)$args[1]; |
||
276 | } |
||
277 | |||
278 | $init_source_dir = $args[0]; |
||
279 | } |
||
280 | |||
281 | $vendor_dir = getVendorDir($current_dir); |
||
282 | |||
283 | if ($init_level === null) { |
||
284 | echo "Calculating best config level based on project files\n"; |
||
285 | Psalm\Config\Creator::createBareConfig($current_dir, $init_source_dir, $vendor_dir); |
||
286 | $config = \Psalm\Config::getInstance(); |
||
287 | } else { |
||
288 | try { |
||
289 | $template_contents = Psalm\Config\Creator::getContents( |
||
290 | $current_dir, |
||
291 | $init_source_dir, |
||
292 | $init_level, |
||
293 | $vendor_dir |
||
294 | ); |
||
295 | } catch (Psalm\Exception\ConfigCreationException $e) { |
||
296 | die($e->getMessage() . PHP_EOL); |
||
297 | } |
||
298 | |||
299 | if (!file_put_contents($current_dir . 'psalm.xml', $template_contents)) { |
||
300 | die('Could not write to psalm.xml' . PHP_EOL); |
||
301 | } |
||
302 | |||
303 | exit('Config file created successfully. Please re-run psalm.' . PHP_EOL); |
||
304 | } |
||
305 | } else { |
||
306 | $config = initialiseConfig($path_to_config, $current_dir, $output_format, $first_autoloader); |
||
307 | |||
308 | if (isset($options['error-level']) |
||
309 | && is_numeric($options['error-level']) |
||
310 | ) { |
||
311 | $config_level = (int) $options['error-level']; |
||
312 | |||
313 | if (!in_array($config_level, [1, 2, 3, 4, 5, 6, 7, 8], true)) { |
||
314 | throw new \Psalm\Exception\ConfigException( |
||
315 | 'Invalid error level ' . $config_level |
||
316 | ); |
||
317 | } |
||
318 | |||
319 | $config->level = $config_level; |
||
320 | } |
||
321 | } |
||
322 | |||
323 | $config->setIncludeCollector($include_collector); |
||
324 | |||
325 | if ($config->resolve_from_config_file) { |
||
326 | $current_dir = $config->base_dir; |
||
327 | chdir($current_dir); |
||
328 | } |
||
329 | |||
330 | $in_ci = isset($_SERVER['TRAVIS']) |
||
331 | || isset($_SERVER['CIRCLECI']) |
||
332 | || isset($_SERVER['APPVEYOR']) |
||
333 | || isset($_SERVER['JENKINS_URL']) |
||
334 | || isset($_SERVER['SCRUTINIZER']) |
||
335 | || isset($_SERVER['GITLAB_CI']) |
||
336 | || isset($_SERVER['GITHUB_WORKFLOW']); |
||
337 | |||
338 | // disable progressbar on CI |
||
339 | if ($in_ci) { |
||
340 | $options['long-progress'] = true; |
||
341 | } |
||
342 | |||
343 | if (isset($options['threads'])) { |
||
344 | $threads = (int)$options['threads']; |
||
345 | } elseif (isset($options['debug']) || $in_ci) { |
||
346 | $threads = 1; |
||
347 | } else { |
||
348 | $threads = max(1, ProjectAnalyzer::getCpuCount() - 1); |
||
349 | } |
||
350 | |||
351 | if (!isset($options['threads']) |
||
352 | && !isset($options['debug']) |
||
353 | && $threads === 1 |
||
354 | && ini_get('pcre.jit') === '1' |
||
355 | && PHP_OS === 'Darwin' |
||
356 | && version_compare(PHP_VERSION, '7.3.0') >= 0 |
||
357 | && version_compare(PHP_VERSION, '7.4.0') < 0 |
||
358 | ) { |
||
359 | echo( |
||
360 | 'If you want to run Psalm as a language server, or run Psalm with' . PHP_EOL |
||
361 | . 'multiple processes (--threads=4), beware:' . PHP_EOL |
||
362 | . \Psalm\Internal\Fork\Pool::MAC_PCRE_MESSAGE . PHP_EOL . PHP_EOL |
||
363 | ); |
||
364 | } |
||
365 | |||
366 | $ini_handler = new \Psalm\Internal\Fork\PsalmRestarter('PSALM'); |
||
367 | |||
368 | if (isset($options['disable-extension'])) { |
||
369 | if (is_array($options['disable-extension'])) { |
||
370 | /** @psalm-suppress MixedAssignment */ |
||
371 | foreach ($options['disable-extension'] as $extension) { |
||
372 | if (is_string($extension)) { |
||
373 | $ini_handler->disableExtension($extension); |
||
374 | } |
||
375 | } |
||
376 | } elseif (is_string($options['disable-extension'])) { |
||
377 | $ini_handler->disableExtension($options['disable-extension']); |
||
378 | } |
||
379 | } |
||
380 | |||
381 | if ($threads > 1) { |
||
382 | $ini_handler->disableExtension('grpc'); |
||
383 | } |
||
384 | |||
385 | $ini_handler->disableExtension('uopz'); |
||
386 | |||
387 | $type_map_location = null; |
||
388 | |||
389 | if (isset($options['generate-json-map']) && is_string($options['generate-json-map'])) { |
||
390 | $type_map_location = $options['generate-json-map']; |
||
391 | } |
||
392 | |||
393 | $stubs_location = null; |
||
394 | |||
395 | if (isset($options['generate-stubs']) && is_string($options['generate-stubs'])) { |
||
396 | $stubs_location = $options['generate-stubs']; |
||
397 | } |
||
398 | |||
399 | // If Xdebug is enabled, restart without it |
||
400 | $ini_handler->check(); |
||
401 | |||
402 | if (is_null($config->load_xdebug_stub) && '' !== $ini_handler->getSkippedVersion()) { |
||
403 | $config->load_xdebug_stub = true; |
||
404 | } |
||
405 | |||
406 | if (isset($options['debug-emitted-issues'])) { |
||
407 | $config->debug_emitted_issues = true; |
||
408 | } |
||
409 | |||
410 | setlocale(LC_CTYPE, 'C'); |
||
411 | |||
412 | View Code Duplication | if (isset($options['set-baseline'])) { |
|
0 ignored issues
–
show
|
|||
413 | if (is_array($options['set-baseline'])) { |
||
414 | die('Only one baseline file can be created at a time' . PHP_EOL); |
||
415 | } |
||
416 | } |
||
417 | |||
418 | $paths_to_check = getPathsToCheck(isset($options['f']) ? $options['f'] : null); |
||
419 | |||
420 | $plugins = []; |
||
421 | |||
422 | View Code Duplication | if (isset($options['plugin'])) { |
|
423 | $plugins = $options['plugin']; |
||
424 | |||
425 | if (!is_array($plugins)) { |
||
426 | $plugins = [$plugins]; |
||
427 | } |
||
428 | } |
||
429 | |||
430 | |||
431 | |||
432 | $show_info = isset($options['show-info']) |
||
433 | ? $options['show-info'] === 'true' || $options['show-info'] === '1' |
||
434 | : false; |
||
435 | |||
436 | $is_diff = isset($options['diff']); |
||
437 | |||
438 | /** @var false|'always'|'auto' $find_unused_code */ |
||
439 | $find_unused_code = false; |
||
440 | if (isset($options['find-dead-code'])) { |
||
441 | $options['find-unused-code'] = $options['find-dead-code']; |
||
442 | } |
||
443 | |||
444 | if (isset($options['find-unused-code'])) { |
||
445 | if ($options['find-unused-code'] === 'always') { |
||
446 | $find_unused_code = 'always'; |
||
447 | } else { |
||
448 | $find_unused_code = 'auto'; |
||
449 | } |
||
450 | } |
||
451 | |||
452 | $find_unused_variables = isset($options['find-unused-variables']); |
||
453 | |||
454 | $find_references_to = isset($options['find-references-to']) && is_string($options['find-references-to']) |
||
455 | ? $options['find-references-to'] |
||
456 | : null; |
||
457 | |||
458 | if (isset($options['shepherd'])) { |
||
459 | if (is_string($options['shepherd'])) { |
||
460 | $config->shepherd_host = $options['shepherd']; |
||
461 | } |
||
462 | $shepherd_plugin = __DIR__ . '/Psalm/Plugin/Shepherd.php'; |
||
463 | |||
464 | if (!file_exists($shepherd_plugin)) { |
||
465 | die('Could not find Shepherd plugin location ' . $shepherd_plugin . PHP_EOL); |
||
466 | } |
||
467 | |||
468 | $plugins[] = $shepherd_plugin; |
||
469 | } |
||
470 | |||
471 | View Code Duplication | if (isset($options['clear-cache'])) { |
|
472 | $cache_directory = $config->getCacheDirectory(); |
||
473 | |||
474 | Config::removeCacheDirectory($cache_directory); |
||
475 | echo 'Cache directory deleted' . PHP_EOL; |
||
476 | exit; |
||
477 | } |
||
478 | |||
479 | View Code Duplication | if (isset($options['clear-global-cache'])) { |
|
480 | $cache_directory = $config->getGlobalCacheDirectory(); |
||
481 | |||
482 | if ($cache_directory) { |
||
483 | Config::removeCacheDirectory($cache_directory); |
||
484 | echo 'Global cache directory deleted' . PHP_EOL; |
||
485 | } |
||
486 | |||
487 | exit; |
||
488 | } |
||
489 | |||
490 | $debug = array_key_exists('debug', $options) || array_key_exists('debug-by-line', $options); |
||
491 | |||
492 | if ($debug) { |
||
493 | $progress = new DebugProgress(); |
||
494 | } elseif (isset($options['no-progress'])) { |
||
495 | $progress = new VoidProgress(); |
||
496 | } else { |
||
497 | $show_errors = !$config->error_baseline || isset($options['ignore-baseline']); |
||
498 | if (isset($options['long-progress'])) { |
||
499 | $progress = new LongProgress($show_errors, $show_info); |
||
500 | } else { |
||
501 | $progress = new DefaultProgress($show_errors, $show_info); |
||
502 | } |
||
503 | } |
||
504 | |||
505 | if (isset($options['no-cache']) || isset($options['i'])) { |
||
506 | $providers = new Provider\Providers( |
||
507 | new Provider\FileProvider |
||
508 | ); |
||
509 | } else { |
||
510 | $no_reflection_cache = isset($options['no-reflection-cache']); |
||
511 | $no_file_cache = isset($options['no-file-cache']); |
||
512 | |||
513 | $file_storage_cache_provider = $no_reflection_cache |
||
514 | ? null |
||
515 | : new Provider\FileStorageCacheProvider($config); |
||
516 | |||
517 | $classlike_storage_cache_provider = $no_reflection_cache |
||
518 | ? null |
||
519 | : new Provider\ClassLikeStorageCacheProvider($config); |
||
520 | |||
521 | $providers = new Provider\Providers( |
||
522 | new Provider\FileProvider, |
||
523 | new Provider\ParserCacheProvider($config, !$no_file_cache), |
||
524 | $file_storage_cache_provider, |
||
525 | $classlike_storage_cache_provider, |
||
526 | new Provider\FileReferenceCacheProvider($config), |
||
527 | new Provider\ProjectCacheProvider($current_dir . DIRECTORY_SEPARATOR . 'composer.lock') |
||
528 | ); |
||
529 | } |
||
530 | |||
531 | $stdout_report_options = new \Psalm\Report\ReportOptions(); |
||
532 | $stdout_report_options->use_color = !array_key_exists('m', $options); |
||
533 | $stdout_report_options->show_info = $show_info; |
||
534 | $stdout_report_options->show_suggestions = !array_key_exists('no-suggestions', $options); |
||
535 | /** |
||
536 | * @psalm-suppress PropertyTypeCoercion |
||
537 | */ |
||
538 | $stdout_report_options->format = $output_format; |
||
539 | $stdout_report_options->show_snippet = !isset($options['show-snippet']) || $options['show-snippet'] !== "false"; |
||
540 | $stdout_report_options->pretty = isset($options['pretty-print']) && $options['pretty-print'] !== "false"; |
||
541 | |||
542 | /** @var list<string>|string $report_file_paths type guaranteed by argument to getopt() */ |
||
543 | $report_file_paths = $options['report'] ?? []; |
||
544 | if (is_string($report_file_paths)) { |
||
545 | $report_file_paths = [$report_file_paths]; |
||
546 | } |
||
547 | $project_analyzer = new ProjectAnalyzer( |
||
548 | $config, |
||
549 | $providers, |
||
550 | $stdout_report_options, |
||
551 | ProjectAnalyzer::getFileReportOptions( |
||
552 | $report_file_paths, |
||
553 | isset($options['report-show-info']) |
||
554 | ? $options['report-show-info'] !== 'false' && $options['report-show-info'] !== '0' |
||
555 | : true |
||
556 | ), |
||
557 | $threads, |
||
558 | $progress |
||
559 | ); |
||
560 | |||
561 | if (!isset($options['php-version'])) { |
||
562 | $options['php-version'] = $config->getPhpVersion(); |
||
563 | } |
||
564 | |||
565 | View Code Duplication | if (isset($options['php-version'])) { |
|
566 | if (!is_string($options['php-version'])) { |
||
567 | die('Expecting a version number in the format x.y' . PHP_EOL); |
||
568 | } |
||
569 | |||
570 | $project_analyzer->setPhpVersion($options['php-version']); |
||
571 | } |
||
572 | |||
573 | if ($type_map_location) { |
||
574 | $project_analyzer->getCodebase()->store_node_types = true; |
||
575 | } |
||
576 | |||
577 | $start_time = microtime(true); |
||
578 | |||
579 | if (array_key_exists('debug-by-line', $options)) { |
||
580 | $project_analyzer->debug_lines = true; |
||
581 | } |
||
582 | |||
583 | if ($config->find_unused_code) { |
||
584 | $find_unused_code = 'auto'; |
||
585 | } |
||
586 | |||
587 | if ($find_references_to !== null) { |
||
588 | $project_analyzer->getCodebase()->collectLocations(); |
||
589 | $project_analyzer->show_issues = false; |
||
590 | } |
||
591 | |||
592 | if ($find_unused_code) { |
||
593 | $project_analyzer->getCodebase()->reportUnusedCode($find_unused_code); |
||
594 | } |
||
595 | |||
596 | if ($config->find_unused_variables || $find_unused_variables) { |
||
597 | $project_analyzer->getCodebase()->reportUnusedVariables(); |
||
598 | } |
||
599 | |||
600 | if ($config->run_taint_analysis || (isset($options['track-tainted-input']) |
||
601 | || isset($options['security-analysis']) |
||
602 | || isset($options['taint-analysis'])) |
||
603 | ) { |
||
604 | $project_analyzer->trackTaintedInputs(); |
||
605 | } |
||
606 | |||
607 | if (isset($options['find-unused-psalm-suppress'])) { |
||
608 | $project_analyzer->trackUnusedSuppressions(); |
||
609 | } |
||
610 | |||
611 | /** @var string $plugin_path */ |
||
612 | foreach ($plugins as $plugin_path) { |
||
613 | $config->addPluginPath($plugin_path); |
||
614 | } |
||
615 | |||
616 | if ($paths_to_check === null) { |
||
617 | $project_analyzer->check($current_dir, $is_diff); |
||
618 | } elseif ($paths_to_check) { |
||
619 | $project_analyzer->checkPaths($paths_to_check); |
||
620 | } |
||
621 | |||
622 | if ($find_references_to) { |
||
623 | $project_analyzer->findReferencesTo($find_references_to); |
||
624 | } |
||
625 | |||
626 | if (isset($options['set-baseline']) && is_string($options['set-baseline'])) { |
||
627 | if ($is_diff) { |
||
628 | fwrite(STDERR, 'Cannot set baseline in --diff mode' . PHP_EOL); |
||
629 | } else { |
||
630 | fwrite(STDERR, 'Writing error baseline to file...' . PHP_EOL); |
||
631 | |||
632 | ErrorBaseline::create( |
||
633 | new \Psalm\Internal\Provider\FileProvider, |
||
634 | $options['set-baseline'], |
||
635 | IssueBuffer::getIssuesData(), |
||
636 | $config->include_php_versions_in_error_baseline || isset($options['include-php-versions']) |
||
637 | ); |
||
638 | |||
639 | fwrite(STDERR, "Baseline saved to {$options['set-baseline']}."); |
||
640 | |||
641 | update_config_file( |
||
642 | $config, |
||
643 | $path_to_config ?? $current_dir, |
||
644 | $options['set-baseline'] |
||
645 | ); |
||
646 | |||
647 | fwrite(STDERR, PHP_EOL); |
||
648 | } |
||
649 | } |
||
650 | |||
651 | $issue_baseline = []; |
||
652 | |||
653 | if (isset($options['update-baseline'])) { |
||
654 | if ($is_diff) { |
||
655 | fwrite(STDERR, 'Cannot update baseline in --diff mode' . PHP_EOL); |
||
656 | } else { |
||
657 | $baselineFile = Config::getInstance()->error_baseline; |
||
658 | |||
659 | if (empty($baselineFile)) { |
||
660 | die('Cannot update baseline, because no baseline file is configured.' . PHP_EOL); |
||
661 | } |
||
662 | |||
663 | try { |
||
664 | $issue_current_baseline = ErrorBaseline::read( |
||
665 | new \Psalm\Internal\Provider\FileProvider, |
||
666 | $baselineFile |
||
667 | ); |
||
668 | $total_issues_current_baseline = ErrorBaseline::countTotalIssues($issue_current_baseline); |
||
669 | |||
670 | $issue_baseline = ErrorBaseline::update( |
||
671 | new \Psalm\Internal\Provider\FileProvider, |
||
672 | $baselineFile, |
||
673 | IssueBuffer::getIssuesData(), |
||
674 | $config->include_php_versions_in_error_baseline || isset($options['include-php-versions']) |
||
675 | ); |
||
676 | $total_issues_updated_baseline = ErrorBaseline::countTotalIssues($issue_baseline); |
||
677 | |||
678 | $total_fixed_issues = $total_issues_current_baseline - $total_issues_updated_baseline; |
||
679 | |||
680 | if ($total_fixed_issues > 0) { |
||
681 | echo str_repeat('-', 30) . "\n"; |
||
682 | echo $total_fixed_issues . ' errors fixed' . "\n"; |
||
683 | } |
||
684 | } catch (\Psalm\Exception\ConfigException $exception) { |
||
685 | fwrite(STDERR, 'Could not update baseline file: ' . $exception->getMessage() . PHP_EOL); |
||
686 | exit(1); |
||
687 | } |
||
688 | } |
||
689 | } |
||
690 | |||
691 | if (isset($options['use-baseline'])) { |
||
692 | View Code Duplication | if (!is_string($options['use-baseline'])) { |
|
693 | fwrite(STDERR, '--use-baseline must be a string' . PHP_EOL); |
||
694 | exit(1); |
||
695 | } |
||
696 | |||
697 | $baseline_file_path = $options['use-baseline']; |
||
698 | } else { |
||
699 | $baseline_file_path = Config::getInstance()->error_baseline; |
||
700 | } |
||
701 | |||
702 | if ($baseline_file_path && !isset($options['ignore-baseline'])) { |
||
703 | try { |
||
704 | $issue_baseline = ErrorBaseline::read( |
||
705 | new \Psalm\Internal\Provider\FileProvider, |
||
706 | $baseline_file_path |
||
707 | ); |
||
708 | } catch (\Psalm\Exception\ConfigException $exception) { |
||
709 | fwrite(STDERR, 'Error while reading baseline: ' . $exception->getMessage() . PHP_EOL); |
||
710 | exit(1); |
||
711 | } |
||
712 | } |
||
713 | |||
714 | if ($type_map_location) { |
||
715 | $file_map = $providers->file_reference_provider->getFileMaps(); |
||
716 | |||
717 | $name_file_map = []; |
||
718 | |||
719 | $expected_references = []; |
||
720 | |||
721 | foreach ($file_map as $file_path => $map) { |
||
722 | $file_name = $config->shortenFileName($file_path); |
||
723 | foreach ($map[0] as $map_parts) { |
||
724 | $expected_references[$map_parts[1]] = true; |
||
725 | } |
||
726 | $map[2] = []; |
||
727 | $name_file_map[$file_name] = $map; |
||
728 | } |
||
729 | |||
730 | $reference_dictionary = \Psalm\Internal\Codebase\ReferenceMapGenerator::getReferenceMap( |
||
731 | $providers->classlike_storage_provider, |
||
732 | $expected_references |
||
733 | ); |
||
734 | |||
735 | $type_map_string = json_encode(['files' => $name_file_map, 'references' => $reference_dictionary]); |
||
736 | |||
737 | $providers->file_provider->setContents( |
||
738 | $type_map_location, |
||
739 | $type_map_string |
||
740 | ); |
||
741 | } |
||
742 | |||
743 | if ($stubs_location) { |
||
744 | $providers->file_provider->setContents( |
||
745 | $stubs_location, |
||
746 | \Psalm\Internal\Stubs\Generator\StubsGenerator::getAll( |
||
747 | $project_analyzer->getCodebase(), |
||
748 | $providers->classlike_storage_provider, |
||
749 | $providers->file_storage_provider |
||
750 | ) |
||
751 | ); |
||
752 | } |
||
753 | |||
754 | if (!isset($options['i'])) { |
||
755 | IssueBuffer::finish( |
||
756 | $project_analyzer, |
||
757 | !$paths_to_check, |
||
758 | $start_time, |
||
759 | isset($options['stats']), |
||
760 | $issue_baseline |
||
761 | ); |
||
762 | } else { |
||
763 | $issues_by_file = IssueBuffer::getIssuesData(); |
||
764 | |||
765 | if (!$issues_by_file) { |
||
766 | $init_level = 1; |
||
767 | } else { |
||
768 | $codebase = $project_analyzer->getCodebase(); |
||
769 | $mixed_counts = $codebase->analyzer->getTotalTypeCoverage($codebase); |
||
770 | |||
771 | $init_level = \Psalm\Config\Creator::getLevel( |
||
772 | array_merge(...array_values($issues_by_file)), |
||
773 | (int) array_sum($mixed_counts) |
||
774 | ); |
||
775 | } |
||
776 | |||
777 | echo "\n" . 'Detected level ' . $init_level . ' as a suitable initial default' . "\n"; |
||
778 | |||
779 | try { |
||
780 | $template_contents = Psalm\Config\Creator::getContents( |
||
781 | $current_dir, |
||
782 | $init_source_dir, |
||
783 | $init_level, |
||
784 | $vendor_dir |
||
785 | ); |
||
786 | } catch (Psalm\Exception\ConfigCreationException $e) { |
||
787 | die($e->getMessage() . PHP_EOL); |
||
788 | } |
||
789 | |||
790 | if (!file_put_contents($current_dir . 'psalm.xml', $template_contents)) { |
||
791 | die('Could not write to psalm.xml' . PHP_EOL); |
||
792 | } |
||
793 | |||
794 | exit('Config file created successfully. Please re-run psalm.' . PHP_EOL); |
||
795 | } |
||
796 |
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.