2018-01-07 06:11:23 +01:00
|
|
|
|
<?php
|
2020-07-22 21:57:24 +02:00
|
|
|
|
|
|
|
|
|
namespace Psalm;
|
|
|
|
|
|
2018-01-07 06:11:23 +01:00
|
|
|
|
require_once('command_functions.php');
|
|
|
|
|
|
2018-11-06 03:57:36 +01:00
|
|
|
|
use Psalm\Internal\Analyzer\ProjectAnalyzer;
|
2020-07-11 23:17:22 +02:00
|
|
|
|
use Psalm\Internal\IncludeCollector;
|
2019-05-30 16:30:41 +02:00
|
|
|
|
use Psalm\Progress\DebugProgress;
|
|
|
|
|
use Psalm\Progress\DefaultProgress;
|
2020-07-22 21:57:24 +02:00
|
|
|
|
use function error_reporting;
|
|
|
|
|
use function ini_set;
|
|
|
|
|
use function gc_collect_cycles;
|
|
|
|
|
use function gc_disable;
|
|
|
|
|
use function array_slice;
|
|
|
|
|
use function getopt;
|
|
|
|
|
use function implode;
|
|
|
|
|
use function array_map;
|
|
|
|
|
use function substr;
|
|
|
|
|
use function preg_replace;
|
|
|
|
|
use function in_array;
|
|
|
|
|
use function fwrite;
|
|
|
|
|
use const STDERR;
|
|
|
|
|
use const PHP_EOL;
|
|
|
|
|
use function array_key_exists;
|
|
|
|
|
use function is_array;
|
|
|
|
|
use function getcwd;
|
|
|
|
|
use const DIRECTORY_SEPARATOR;
|
|
|
|
|
use function is_string;
|
|
|
|
|
use function realpath;
|
|
|
|
|
use function chdir;
|
|
|
|
|
use function explode;
|
|
|
|
|
use function file_exists;
|
|
|
|
|
use function file_get_contents;
|
|
|
|
|
use function preg_split;
|
|
|
|
|
use function array_shift;
|
|
|
|
|
use function array_filter;
|
|
|
|
|
use function trim;
|
|
|
|
|
use function strpos;
|
|
|
|
|
use function strtolower;
|
|
|
|
|
use function is_dir;
|
|
|
|
|
use function pathinfo;
|
|
|
|
|
use const PATHINFO_EXTENSION;
|
|
|
|
|
use function filter_var;
|
|
|
|
|
use const FILTER_VALIDATE_BOOLEAN;
|
|
|
|
|
use const FILTER_NULL_ON_FAILURE;
|
|
|
|
|
use function microtime;
|
|
|
|
|
use function count;
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
|
|
|
|
// show all errors
|
|
|
|
|
error_reporting(-1);
|
2018-08-29 19:58:07 +02:00
|
|
|
|
ini_set('display_errors', '1');
|
|
|
|
|
ini_set('display_startup_errors', '1');
|
2020-02-24 22:18:46 +01:00
|
|
|
|
$memLimit = getMemoryLimitInBytes();
|
|
|
|
|
// Magic number is 4096M in bytes
|
2020-07-08 20:32:16 +02:00
|
|
|
|
if ($memLimit > 0 && $memLimit < 8 * 1024 * 1024 * 1024) {
|
|
|
|
|
ini_set('memory_limit', (string) (8 * 1024 * 1024 * 1024));
|
2020-02-24 22:18:46 +01:00
|
|
|
|
}
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2019-04-24 04:31:38 +02:00
|
|
|
|
gc_collect_cycles();
|
|
|
|
|
gc_disable();
|
|
|
|
|
|
2019-12-05 05:17:08 +01:00
|
|
|
|
require_once __DIR__ . '/Psalm/Internal/exception_handler.php';
|
|
|
|
|
|
2019-04-24 04:31:38 +02:00
|
|
|
|
$args = array_slice($argv, 1);
|
|
|
|
|
|
2019-07-07 14:55:53 +02:00
|
|
|
|
$valid_short_options = ['f:', 'm', 'h', 'r:', 'c:'];
|
2019-04-24 04:31:38 +02:00
|
|
|
|
$valid_long_options = [
|
2020-06-23 00:16:47 +02:00
|
|
|
|
'help', 'debug', 'debug-by-line', 'debug-emitted-issues', 'config:', 'file:', 'root:',
|
2019-05-27 16:05:15 +02:00
|
|
|
|
'plugin:', 'issues:', 'list-supported-issues', 'php-version:', 'dry-run', 'safe-types',
|
2019-04-26 17:23:19 +02:00
|
|
|
|
'find-unused-code', 'threads:', 'codeowner:',
|
2019-05-13 20:38:18 +02:00
|
|
|
|
'allow-backwards-incompatible-changes:',
|
2020-03-13 17:40:08 +01:00
|
|
|
|
'add-newline-between-docblock-annotations:',
|
2020-04-03 18:52:18 +02:00
|
|
|
|
'no-cache'
|
2019-04-24 04:31:38 +02:00
|
|
|
|
];
|
|
|
|
|
|
2018-01-07 06:11:23 +01:00
|
|
|
|
// get options from command line
|
2019-04-24 04:31:38 +02:00
|
|
|
|
$options = getopt(implode('', $valid_short_options), $valid_long_options);
|
|
|
|
|
|
|
|
|
|
array_map(
|
|
|
|
|
/**
|
|
|
|
|
* @param string $arg
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
function ($arg) use ($valid_long_options, $valid_short_options) {
|
|
|
|
|
if (substr($arg, 0, 2) === '--' && $arg !== '--') {
|
|
|
|
|
$arg_name = preg_replace('/=.*$/', '', substr($arg, 2));
|
|
|
|
|
|
2019-06-01 15:22:39 +02:00
|
|
|
|
if ($arg_name === 'alter') {
|
|
|
|
|
// valid option for psalm, ignored by psalter
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-24 04:31:38 +02:00
|
|
|
|
if (!in_array($arg_name, $valid_long_options)
|
|
|
|
|
&& !in_array($arg_name . ':', $valid_long_options)
|
|
|
|
|
&& !in_array($arg_name . '::', $valid_long_options)
|
|
|
|
|
) {
|
2019-05-30 16:30:41 +02:00
|
|
|
|
fwrite(
|
|
|
|
|
STDERR,
|
|
|
|
|
'Unrecognised argument "--' . $arg_name . '"' . PHP_EOL
|
|
|
|
|
. 'Type --help to see a list of supported arguments'. PHP_EOL
|
|
|
|
|
);
|
2019-04-24 04:31:38 +02:00
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
$args
|
2018-01-07 06:11:23 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (array_key_exists('help', $options)) {
|
|
|
|
|
$options['h'] = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (array_key_exists('monochrome', $options)) {
|
|
|
|
|
$options['m'] = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isset($options['config'])) {
|
|
|
|
|
$options['c'] = $options['config'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isset($options['c']) && is_array($options['c'])) {
|
|
|
|
|
die('Too many config files provided' . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (array_key_exists('h', $options)) {
|
|
|
|
|
echo <<< HELP
|
|
|
|
|
Usage:
|
2018-10-18 20:34:46 +02:00
|
|
|
|
psalter [options] [file...]
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
-h, --help
|
|
|
|
|
Display this help message
|
|
|
|
|
|
2020-06-23 00:16:47 +02:00
|
|
|
|
--debug, --debug-by-line, --debug-emitted-issues
|
2018-01-07 06:11:23 +01:00
|
|
|
|
Debug information
|
|
|
|
|
|
|
|
|
|
-c, --config=psalm.xml
|
|
|
|
|
Path to a psalm.xml configuration file. Run psalm --init to create one.
|
|
|
|
|
|
|
|
|
|
-m, --monochrome
|
|
|
|
|
Enable monochrome output
|
|
|
|
|
|
|
|
|
|
-r, --root
|
|
|
|
|
If running Psalm globally you'll need to specify a project root. Defaults to cwd
|
|
|
|
|
|
|
|
|
|
--plugin=PATH
|
|
|
|
|
Executes a plugin, an alternative to using the Psalm config
|
|
|
|
|
|
|
|
|
|
--dry-run
|
|
|
|
|
Shows a diff of all the changes, without making them
|
|
|
|
|
|
2018-01-07 23:17:18 +01:00
|
|
|
|
--safe-types
|
|
|
|
|
Only update PHP types when the new type information comes from other PHP types,
|
|
|
|
|
as opposed to type information that just comes from docblocks
|
|
|
|
|
|
2018-01-07 06:11:23 +01:00
|
|
|
|
--php-version=PHP_MAJOR_VERSION.PHP_MINOR_VERSION
|
|
|
|
|
|
|
|
|
|
--issues=IssueType1,IssueType2
|
2019-05-27 16:05:15 +02:00
|
|
|
|
If any issues can be fixed automatically, Psalm will update the codebase. To fix as many issues as possible,
|
|
|
|
|
use --issues=all
|
|
|
|
|
|
|
|
|
|
--list-supported-issues
|
|
|
|
|
Display the list of issues that psalter knows how to fix
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2019-04-24 04:31:38 +02:00
|
|
|
|
--find-unused-code
|
2019-04-17 17:12:18 +02:00
|
|
|
|
Include unused code as a candidate for removal
|
2019-04-17 19:15:06 +02:00
|
|
|
|
|
|
|
|
|
--threads=INT
|
|
|
|
|
If greater than one, Psalm will run analysis on multiple threads, speeding things up.
|
2019-04-26 17:23:19 +02:00
|
|
|
|
|
|
|
|
|
--codeowner=[codeowner]
|
|
|
|
|
You can specify a GitHub code ownership group, and only that owner's code will be updated.
|
2019-05-13 20:38:18 +02:00
|
|
|
|
|
|
|
|
|
--allow-backwards-incompatible-changes=BOOL
|
|
|
|
|
Allow Psalm modify method signatures that could break code outside the project. Defaults to true.
|
2019-05-27 16:05:15 +02:00
|
|
|
|
|
2020-03-13 17:40:08 +01:00
|
|
|
|
--add-newline-between-docblock-annotations=BOOL
|
|
|
|
|
Whether to add or not add a new line between docblock annotations. Defaults to true.
|
|
|
|
|
|
2020-04-03 18:52:18 +02:00
|
|
|
|
--no-cache
|
|
|
|
|
Runs Psalm without using cache
|
2018-01-07 06:11:23 +01:00
|
|
|
|
HELP;
|
|
|
|
|
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-27 16:05:15 +02:00
|
|
|
|
if (!isset($options['issues']) &&
|
|
|
|
|
!isset($options['list-supported-issues']) &&
|
|
|
|
|
(!isset($options['plugin']) || $options['plugin'] === false)
|
|
|
|
|
) {
|
|
|
|
|
fwrite(STDERR, 'Please specify the issues you want to fix with --issues=IssueOne,IssueTwo or --issues=all, ' .
|
|
|
|
|
'or provide a plugin that has its own manipulations with --plugin=path/to/plugin.php' . PHP_EOL);
|
|
|
|
|
exit(1);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isset($options['root'])) {
|
|
|
|
|
$options['r'] = $options['root'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$current_dir = (string)getcwd() . DIRECTORY_SEPARATOR;
|
|
|
|
|
|
|
|
|
|
if (isset($options['r']) && is_string($options['r'])) {
|
|
|
|
|
$root_path = realpath($options['r']);
|
|
|
|
|
|
|
|
|
|
if (!$root_path) {
|
|
|
|
|
die('Could not locate root directory ' . $current_dir . DIRECTORY_SEPARATOR . $options['r'] . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$current_dir = $root_path . DIRECTORY_SEPARATOR;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 21:57:24 +02:00
|
|
|
|
$vendor_dir = \Psalm\getVendorDir($current_dir);
|
2018-01-25 17:32:54 +01:00
|
|
|
|
|
2020-07-11 23:17:22 +02:00
|
|
|
|
require_once __DIR__ . '/Psalm/Internal/IncludeCollector.php';
|
|
|
|
|
$include_collector = new IncludeCollector();
|
|
|
|
|
$first_autoloader = $include_collector->runAndCollect(
|
2020-09-12 17:24:05 +02:00
|
|
|
|
function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader {
|
2020-07-11 23:17:22 +02:00
|
|
|
|
return requireAutoloaders($current_dir, isset($options['r']), $vendor_dir);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2019-09-14 16:13:39 +02:00
|
|
|
|
// If Xdebug is enabled, restart without it
|
2018-03-13 17:52:00 +01:00
|
|
|
|
(new \Composer\XdebugHandler\XdebugHandler('PSALTER'))->check();
|
|
|
|
|
|
2018-01-07 06:11:23 +01:00
|
|
|
|
$paths_to_check = getPathsToCheck(isset($options['f']) ? $options['f'] : null);
|
|
|
|
|
|
2019-07-07 14:55:53 +02:00
|
|
|
|
$path_to_config = get_path_to_config($options);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2019-07-07 14:55:53 +02:00
|
|
|
|
$config = initialiseConfig($path_to_config, $current_dir, \Psalm\Report::TYPE_CONSOLE, $first_autoloader);
|
2020-07-11 23:17:22 +02:00
|
|
|
|
$config->setIncludeCollector($include_collector);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2019-07-07 14:55:53 +02:00
|
|
|
|
if ($config->resolve_from_config_file) {
|
|
|
|
|
$current_dir = $config->base_dir;
|
|
|
|
|
chdir($current_dir);
|
2018-01-21 16:22:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-17 19:15:06 +02:00
|
|
|
|
$threads = isset($options['threads']) ? (int)$options['threads'] : 1;
|
|
|
|
|
|
2020-04-03 18:52:18 +02:00
|
|
|
|
if (isset($options['no-cache'])) {
|
2020-07-22 21:57:24 +02:00
|
|
|
|
$providers = new \Psalm\Internal\Provider\Providers(
|
|
|
|
|
new \Psalm\Internal\Provider\FileProvider()
|
2020-04-03 18:52:18 +02:00
|
|
|
|
);
|
|
|
|
|
} else {
|
2020-07-22 21:57:24 +02:00
|
|
|
|
$providers = new \Psalm\Internal\Provider\Providers(
|
|
|
|
|
new \Psalm\Internal\Provider\FileProvider(),
|
|
|
|
|
new \Psalm\Internal\Provider\ParserCacheProvider($config, false),
|
|
|
|
|
new \Psalm\Internal\Provider\FileStorageCacheProvider($config),
|
|
|
|
|
new \Psalm\Internal\Provider\ClassLikeStorageCacheProvider($config),
|
2020-04-12 17:41:01 +02:00
|
|
|
|
null,
|
2020-07-22 21:57:24 +02:00
|
|
|
|
new \Psalm\Internal\Provider\ProjectCacheProvider($current_dir . DIRECTORY_SEPARATOR . 'composer.lock')
|
2020-04-03 18:52:18 +02:00
|
|
|
|
);
|
|
|
|
|
}
|
2019-04-26 17:23:19 +02:00
|
|
|
|
|
2019-05-27 16:05:15 +02:00
|
|
|
|
if (array_key_exists('list-supported-issues', $options)) {
|
|
|
|
|
echo implode(',', ProjectAnalyzer::getSupportedIssuesToFix()) . PHP_EOL;
|
|
|
|
|
exit();
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-23 00:16:47 +02:00
|
|
|
|
$debug = array_key_exists('debug', $options) || array_key_exists('debug-by-line', $options);
|
2019-05-30 16:30:41 +02:00
|
|
|
|
$progress = $debug
|
|
|
|
|
? new DebugProgress()
|
|
|
|
|
: new DefaultProgress();
|
|
|
|
|
|
2019-06-09 18:37:28 +02:00
|
|
|
|
$stdout_report_options = new \Psalm\Report\ReportOptions();
|
|
|
|
|
$stdout_report_options->use_color = !array_key_exists('m', $options);
|
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
|
$project_analyzer = new ProjectAnalyzer(
|
2018-01-21 16:22:04 +01:00
|
|
|
|
$config,
|
2019-04-26 17:23:19 +02:00
|
|
|
|
$providers,
|
2019-06-09 18:37:28 +02:00
|
|
|
|
$stdout_report_options,
|
|
|
|
|
[],
|
2019-04-17 19:15:06 +02:00
|
|
|
|
$threads,
|
2019-05-30 16:30:41 +02:00
|
|
|
|
$progress
|
2018-01-07 06:11:23 +01:00
|
|
|
|
);
|
|
|
|
|
|
2019-04-17 19:15:06 +02:00
|
|
|
|
if (array_key_exists('debug-by-line', $options)) {
|
|
|
|
|
$project_analyzer->debug_lines = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-23 00:16:47 +02:00
|
|
|
|
if (array_key_exists('debug-emitted-issues', $options)) {
|
|
|
|
|
$config->debug_emitted_issues = true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
|
$config->visitComposerAutoloadFiles($project_analyzer);
|
2018-03-31 02:03:56 +02:00
|
|
|
|
|
2018-01-07 17:48:33 +01:00
|
|
|
|
if (array_key_exists('issues', $options)) {
|
|
|
|
|
if (!is_string($options['issues']) || !$options['issues']) {
|
|
|
|
|
die('Expecting a comma-separated list of issues' . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$issues = explode(',', $options['issues']);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2018-01-07 17:48:33 +01:00
|
|
|
|
$keyed_issues = [];
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2018-01-07 17:48:33 +01:00
|
|
|
|
foreach ($issues as $issue) {
|
|
|
|
|
$keyed_issues[$issue] = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$keyed_issues = [];
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isset($options['php-version'])) {
|
2019-02-07 18:25:57 +01:00
|
|
|
|
if (!is_string($options['php-version'])) {
|
2018-01-07 06:11:23 +01:00
|
|
|
|
die('Expecting a version number in the format x.y' . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-07 18:25:57 +01:00
|
|
|
|
$project_analyzer->setPhpVersion($options['php-version']);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-26 17:23:19 +02:00
|
|
|
|
if (isset($options['codeowner'])) {
|
|
|
|
|
if (file_exists('CODEOWNERS')) {
|
|
|
|
|
$codeowners_file_path = realpath('CODEOWNERS');
|
|
|
|
|
} elseif (file_exists('.github/CODEOWNERS')) {
|
|
|
|
|
$codeowners_file_path = realpath('.github/CODEOWNERS');
|
|
|
|
|
} elseif (file_exists('docs/CODEOWNERS')) {
|
|
|
|
|
$codeowners_file_path = realpath('docs/CODEOWNERS');
|
|
|
|
|
} else {
|
|
|
|
|
die('Cannot use --codeowner without a CODEOWNERS file' . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$codeowners_file = file_get_contents($codeowners_file_path);
|
|
|
|
|
|
|
|
|
|
$codeowner_lines = array_map(
|
|
|
|
|
function (string $line) : array {
|
|
|
|
|
$line_parts = preg_split('/\s+/', $line);
|
|
|
|
|
|
|
|
|
|
$file_selector = substr(array_shift($line_parts), 1);
|
|
|
|
|
return [$file_selector, $line_parts];
|
|
|
|
|
},
|
|
|
|
|
array_filter(
|
|
|
|
|
explode("\n", $codeowners_file),
|
|
|
|
|
function (string $line) : bool {
|
|
|
|
|
$line = trim($line);
|
|
|
|
|
|
|
|
|
|
// currently we don’t match wildcard files or files that could appear anywhere
|
|
|
|
|
// in the repo
|
|
|
|
|
return $line && $line[0] === '/' && strpos($line, '*') === false;
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$codeowner_files = [];
|
|
|
|
|
|
2020-09-02 06:17:41 +02:00
|
|
|
|
foreach ($codeowner_lines as [$path, $owners]) {
|
2019-04-26 17:23:19 +02:00
|
|
|
|
if (!file_exists($path)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($owners as $i => $owner) {
|
|
|
|
|
$owners[$i] = strtolower($owner);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_dir($path)) {
|
|
|
|
|
if (pathinfo($path, PATHINFO_EXTENSION) === 'php') {
|
|
|
|
|
$codeowner_files[$path] = $owners;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
foreach ($providers->file_provider->getFilesInDir($path, ['php']) as $php_file_path) {
|
|
|
|
|
$codeowner_files[$php_file_path] = $owners;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$codeowner_files) {
|
|
|
|
|
die('Could not find any available entries in CODEOWNERS' . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$desired_codeowners = is_array($options['codeowner']) ? $options['codeowner'] : [$options['codeowner']];
|
|
|
|
|
|
|
|
|
|
/** @psalm-suppress MixedAssignment */
|
|
|
|
|
foreach ($desired_codeowners as $desired_codeowner) {
|
|
|
|
|
if (!is_string($desired_codeowner)) {
|
|
|
|
|
die('Invalid --codeowner ' . (string)$desired_codeowner . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($desired_codeowner[0] !== '@') {
|
|
|
|
|
die('--codeowner option must start with @' . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$matched_file = false;
|
|
|
|
|
|
|
|
|
|
foreach ($codeowner_files as $file_path => $owners) {
|
|
|
|
|
if (in_array(strtolower($desired_codeowner), $owners)) {
|
|
|
|
|
$paths_to_check[] = $file_path;
|
|
|
|
|
$matched_file = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$matched_file) {
|
|
|
|
|
die('User/group ' . $desired_codeowner . ' does not own any PHP files' . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-13 20:38:18 +02:00
|
|
|
|
if (isset($options['allow-backwards-incompatible-changes'])) {
|
|
|
|
|
$allow_backwards_incompatible_changes = filter_var(
|
|
|
|
|
$options['allow-backwards-incompatible-changes'],
|
|
|
|
|
FILTER_VALIDATE_BOOLEAN,
|
|
|
|
|
['flags' => FILTER_NULL_ON_FAILURE]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($allow_backwards_incompatible_changes === null) {
|
2020-03-13 17:40:08 +01:00
|
|
|
|
die('--allow-backwards-incompatible-changes expects a boolean value [true|false|1|0]' . PHP_EOL);
|
2019-05-13 20:38:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$project_analyzer->getCodebase()->allow_backwards_incompatible_changes = $allow_backwards_incompatible_changes;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-13 17:40:08 +01:00
|
|
|
|
if (isset($options['add-newline-between-docblock-annotations'])) {
|
|
|
|
|
$doc_block_add_new_line_before_return = filter_var(
|
|
|
|
|
$options['add-newline-between-docblock-annotations'],
|
|
|
|
|
FILTER_VALIDATE_BOOLEAN,
|
|
|
|
|
['flags' => FILTER_NULL_ON_FAILURE]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($doc_block_add_new_line_before_return === null) {
|
|
|
|
|
die('--add-newline-between-docblock-annotations expects a boolean value [true|false|1|0]' . PHP_EOL);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 21:57:24 +02:00
|
|
|
|
\Psalm\Internal\Scanner\ParsedDocblock::addNewLineBetweenAnnotations($doc_block_add_new_line_before_return);
|
2020-03-13 17:40:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-07 17:48:33 +01:00
|
|
|
|
$plugins = [];
|
|
|
|
|
|
|
|
|
|
if (isset($options['plugin'])) {
|
|
|
|
|
$plugins = $options['plugin'];
|
|
|
|
|
|
|
|
|
|
if (!is_array($plugins)) {
|
|
|
|
|
$plugins = [$plugins];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @var string $plugin_path */
|
|
|
|
|
foreach ($plugins as $plugin_path) {
|
2019-01-08 15:16:59 +01:00
|
|
|
|
Config::getInstance()->addPluginPath($current_dir . $plugin_path);
|
2018-01-07 17:48:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-17 19:15:06 +02:00
|
|
|
|
$find_unused_code = array_key_exists('find-unused-code', $options);
|
|
|
|
|
|
2019-05-31 17:55:24 +02:00
|
|
|
|
foreach ($keyed_issues as $issue_name => $_) {
|
|
|
|
|
// MissingParamType requires the scanning of all files to inform possible params
|
2019-09-27 15:22:06 +02:00
|
|
|
|
if (strpos($issue_name, 'Unused') !== false
|
|
|
|
|
|| $issue_name === 'MissingParamType'
|
|
|
|
|
|| $issue_name === 'UnnecessaryVarAnnotation'
|
2020-07-08 20:32:16 +02:00
|
|
|
|
|| $issue_name === 'all'
|
|
|
|
|
) {
|
2019-05-31 17:55:24 +02:00
|
|
|
|
$find_unused_code = true;
|
2020-09-20 14:55:28 +02:00
|
|
|
|
break;
|
2019-05-31 17:55:24 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-17 19:15:06 +02:00
|
|
|
|
if ($find_unused_code) {
|
2019-04-17 17:12:18 +02:00
|
|
|
|
$project_analyzer->getCodebase()->reportUnusedCode();
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
|
$project_analyzer->alterCodeAfterCompletion(
|
2018-01-07 22:11:51 +01:00
|
|
|
|
array_key_exists('dry-run', $options),
|
|
|
|
|
array_key_exists('safe-types', $options)
|
2018-01-07 17:48:33 +01:00
|
|
|
|
);
|
2019-05-27 16:05:15 +02:00
|
|
|
|
|
|
|
|
|
if ($keyed_issues === ['all' => true]) {
|
|
|
|
|
$project_analyzer->setAllIssuesToFix();
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
$project_analyzer->setIssuesToFix($keyed_issues);
|
|
|
|
|
} catch (\Psalm\Exception\UnsupportedIssueToFixException $e) {
|
|
|
|
|
fwrite(STDERR, $e->getMessage() . PHP_EOL);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2018-10-10 22:05:06 +02:00
|
|
|
|
$start_time = microtime(true);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2019-04-26 17:23:19 +02:00
|
|
|
|
if ($paths_to_check === null || count($paths_to_check) > 1 || $find_unused_code) {
|
|
|
|
|
if ($paths_to_check) {
|
|
|
|
|
$files_to_update = [];
|
|
|
|
|
|
|
|
|
|
foreach ($paths_to_check as $path_to_check) {
|
|
|
|
|
if (!is_dir($path_to_check)) {
|
|
|
|
|
$files_to_update[] = (string) realpath($path_to_check);
|
|
|
|
|
} else {
|
|
|
|
|
foreach ($providers->file_provider->getFilesInDir($path_to_check, ['php']) as $php_file_path) {
|
|
|
|
|
$files_to_update[] = $php_file_path;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$project_analyzer->getCodebase()->analyzer->setFilesToUpdate($files_to_update);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
|
$project_analyzer->check($current_dir);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
} elseif ($paths_to_check) {
|
|
|
|
|
foreach ($paths_to_check as $path_to_check) {
|
|
|
|
|
if (is_dir($path_to_check)) {
|
2018-11-11 18:01:14 +01:00
|
|
|
|
$project_analyzer->checkDir($path_to_check);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
} else {
|
2018-11-11 18:01:14 +01:00
|
|
|
|
$project_analyzer->checkFile($path_to_check);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-09 17:49:10 +01:00
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
|
IssueBuffer::finish($project_analyzer, false, $start_time);
|