2018-01-07 06:11:23 +01:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $current_dir
|
2018-01-10 01:33:39 +01:00
|
|
|
|
* @param bool $has_explicit_root
|
2018-01-25 17:32:54 +01:00
|
|
|
|
* @param string $vendor_dir
|
2018-01-07 06:11:23 +01:00
|
|
|
|
*
|
2018-03-03 21:25:35 +01:00
|
|
|
|
* @psalm-suppress MixedInferred
|
|
|
|
|
*
|
2019-04-20 22:37:27 +02:00
|
|
|
|
* @return ?\Composer\Autoload\ClassLoader
|
2018-01-07 06:11:23 +01:00
|
|
|
|
*/
|
2018-01-25 17:32:54 +01:00
|
|
|
|
function requireAutoloaders($current_dir, $has_explicit_root, $vendor_dir)
|
2018-01-07 06:11:23 +01:00
|
|
|
|
{
|
|
|
|
|
$autoload_roots = [$current_dir];
|
|
|
|
|
|
|
|
|
|
$psalm_dir = dirname(__DIR__);
|
|
|
|
|
|
2019-03-27 16:55:10 +01:00
|
|
|
|
/** @psalm-suppress UndefinedConstant */
|
|
|
|
|
$in_phar = \Phar::running() || strpos(__NAMESPACE__, 'HumbugBox');
|
|
|
|
|
|
|
|
|
|
if ($in_phar) {
|
|
|
|
|
require_once(__DIR__ . '/../vendor/autoload.php');
|
|
|
|
|
|
|
|
|
|
// hack required for JsonMapper
|
|
|
|
|
require_once __DIR__ . '/../vendor/netresearch/jsonmapper/src/JsonMapper.php';
|
|
|
|
|
require_once __DIR__ . '/../vendor/netresearch/jsonmapper/src/JsonMapper/Exception.php';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (\realpath($psalm_dir) !== \realpath($current_dir) && !$in_phar) {
|
2018-01-07 06:11:23 +01:00
|
|
|
|
$autoload_roots[] = $psalm_dir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$autoload_files = [];
|
|
|
|
|
|
|
|
|
|
foreach ($autoload_roots as $autoload_root) {
|
|
|
|
|
$has_autoloader = false;
|
|
|
|
|
|
|
|
|
|
$nested_autoload_file = dirname(dirname($autoload_root)) . DIRECTORY_SEPARATOR . 'autoload.php';
|
|
|
|
|
|
2018-03-08 23:56:52 +01:00
|
|
|
|
// note: don't realpath $nested_autoload_file, or phar version will fail
|
2018-01-07 06:11:23 +01:00
|
|
|
|
if (file_exists($nested_autoload_file)) {
|
2018-03-08 23:56:52 +01:00
|
|
|
|
if (!in_array($nested_autoload_file, $autoload_files, false)) {
|
|
|
|
|
$autoload_files[] = $nested_autoload_file;
|
2018-03-03 21:19:05 +01:00
|
|
|
|
}
|
2018-01-07 06:11:23 +01:00
|
|
|
|
$has_autoloader = true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-25 19:07:36 +01:00
|
|
|
|
$vendor_autoload_file =
|
|
|
|
|
$autoload_root . DIRECTORY_SEPARATOR . $vendor_dir . DIRECTORY_SEPARATOR . 'autoload.php';
|
2018-01-07 06:11:23 +01:00
|
|
|
|
|
2018-03-08 23:56:52 +01:00
|
|
|
|
// note: don't realpath $vendor_autoload_file, or phar version will fail
|
2018-01-07 06:11:23 +01:00
|
|
|
|
if (file_exists($vendor_autoload_file)) {
|
2018-03-08 23:56:52 +01:00
|
|
|
|
if (!in_array($vendor_autoload_file, $autoload_files, false)) {
|
|
|
|
|
$autoload_files[] = $vendor_autoload_file;
|
2018-03-03 21:19:05 +01:00
|
|
|
|
}
|
2018-01-07 06:11:23 +01:00
|
|
|
|
$has_autoloader = true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-09 06:03:32 +02:00
|
|
|
|
if (!$has_autoloader && file_exists($autoload_root . '/composer.json')) {
|
2018-01-07 06:11:23 +01:00
|
|
|
|
$error_message = 'Could not find any composer autoloaders in ' . $autoload_root;
|
|
|
|
|
|
2018-01-10 01:33:39 +01:00
|
|
|
|
if (!$has_explicit_root) {
|
2018-01-07 06:11:23 +01:00
|
|
|
|
$error_message .= PHP_EOL . 'Add a --root=[your/project/directory] flag '
|
|
|
|
|
. 'to specify a particular project to run Psalm on.';
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 16:30:41 +02:00
|
|
|
|
fwrite(STDERR, $error_message . PHP_EOL);
|
2018-04-17 17:48:29 +02:00
|
|
|
|
exit(1);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-03 21:19:05 +01:00
|
|
|
|
$first_autoloader = null;
|
|
|
|
|
|
2018-01-07 06:11:23 +01:00
|
|
|
|
foreach ($autoload_files as $file) {
|
2018-03-03 21:25:35 +01:00
|
|
|
|
/**
|
|
|
|
|
* @psalm-suppress UnresolvableInclude
|
2019-03-18 12:47:57 +01:00
|
|
|
|
* @psalm-suppress MixedAssignment
|
|
|
|
|
* @var mixed
|
2018-03-03 21:25:35 +01:00
|
|
|
|
*/
|
2018-03-03 21:19:05 +01:00
|
|
|
|
$autoloader = require_once $file;
|
|
|
|
|
|
2019-03-18 12:47:57 +01:00
|
|
|
|
if (!$first_autoloader
|
|
|
|
|
&& $autoloader instanceof \Composer\Autoload\ClassLoader
|
|
|
|
|
) {
|
2018-03-03 21:19:05 +01:00
|
|
|
|
$first_autoloader = $autoloader;
|
|
|
|
|
}
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
2018-02-25 18:14:35 +01:00
|
|
|
|
|
2019-04-20 22:37:27 +02:00
|
|
|
|
if ($first_autoloader === null && !$in_phar) {
|
2019-04-18 18:12:43 +02:00
|
|
|
|
if (!$autoload_files) {
|
2019-05-30 16:30:41 +02:00
|
|
|
|
fwrite(STDERR, 'Failed to find a valid Composer autoloader' . "\n");
|
2019-04-18 18:12:43 +02:00
|
|
|
|
} else {
|
2019-05-30 16:30:41 +02:00
|
|
|
|
fwrite(STDERR, 'Failed to find a valid Composer autoloader in ' . implode(', ', $autoload_files) . "\n");
|
2019-04-18 18:12:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 16:30:41 +02:00
|
|
|
|
fwrite(
|
|
|
|
|
STDERR,
|
|
|
|
|
'Please make sure you’ve run `composer install` in the current directory before using Psalm.' . "\n"
|
|
|
|
|
);
|
2019-03-18 12:47:57 +01:00
|
|
|
|
exit(1);
|
2018-03-03 21:25:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 00:36:36 +02:00
|
|
|
|
define('PSALM_VERSION', (string) \PackageVersions\Versions::getVersion('vimeo/psalm'));
|
|
|
|
|
define('PHP_PARSER_VERSION', (string) \PackageVersions\Versions::getVersion('nikic/php-parser'));
|
2018-03-03 21:19:05 +01:00
|
|
|
|
|
|
|
|
|
return $first_autoloader;
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-01-25 17:32:54 +01:00
|
|
|
|
* @param string $current_dir
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
* @psalm-suppress PossiblyFalseArgument
|
|
|
|
|
* @psalm-suppress MixedArrayAccess
|
|
|
|
|
* @psalm-suppress MixedAssignment
|
|
|
|
|
*/
|
|
|
|
|
function getVendorDir($current_dir)
|
|
|
|
|
{
|
2018-01-25 19:07:36 +01:00
|
|
|
|
$composer_json_path = $current_dir . DIRECTORY_SEPARATOR . 'composer.json';
|
2018-01-25 17:32:54 +01:00
|
|
|
|
|
|
|
|
|
if (!file_exists($composer_json_path)) {
|
|
|
|
|
return 'vendor';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$composer_json = json_decode(file_get_contents($composer_json_path), true)) {
|
|
|
|
|
throw new \UnexpectedValueException('Invalid composer.json at ' . $composer_json_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isset($composer_json['config']['vendor-dir'])) {
|
2018-01-25 19:07:36 +01:00
|
|
|
|
return (string) $composer_json['config']['vendor-dir'];
|
2018-01-25 17:32:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 'vendor';
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-02 07:10:50 +02:00
|
|
|
|
/**
|
|
|
|
|
* @return string[]
|
|
|
|
|
*/
|
|
|
|
|
function getArguments() : array
|
|
|
|
|
{
|
|
|
|
|
global $argv;
|
|
|
|
|
|
|
|
|
|
if (!$argv) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$filtered_input_paths = [];
|
|
|
|
|
|
|
|
|
|
for ($i = 0; $i < count($argv); ++$i) {
|
|
|
|
|
/** @var string */
|
|
|
|
|
$input_path = $argv[$i];
|
|
|
|
|
|
|
|
|
|
if (realpath($input_path) !== false) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($input_path[0] === '-' && strlen($input_path) === 2) {
|
|
|
|
|
if ($input_path[1] === 'c' || $input_path[1] === 'f') {
|
|
|
|
|
++$i;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($input_path[0] === '-' && $input_path[2] === '=') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$filtered_input_paths[] = $input_path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $filtered_input_paths;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-25 17:32:54 +01:00
|
|
|
|
/**
|
|
|
|
|
* @param string|array|null|false $f_paths
|
2018-01-07 06:11:23 +01:00
|
|
|
|
*
|
|
|
|
|
* @return string[]|null
|
|
|
|
|
*/
|
|
|
|
|
function getPathsToCheck($f_paths)
|
|
|
|
|
{
|
|
|
|
|
global $argv;
|
|
|
|
|
|
|
|
|
|
$paths_to_check = [];
|
|
|
|
|
|
|
|
|
|
if ($f_paths) {
|
|
|
|
|
$input_paths = is_array($f_paths) ? $f_paths : [$f_paths];
|
|
|
|
|
} else {
|
|
|
|
|
$input_paths = $argv ? $argv : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($input_paths) {
|
|
|
|
|
$filtered_input_paths = [];
|
|
|
|
|
|
|
|
|
|
for ($i = 0; $i < count($input_paths); ++$i) {
|
|
|
|
|
/** @var string */
|
|
|
|
|
$input_path = $input_paths[$i];
|
|
|
|
|
|
|
|
|
|
if (realpath($input_path) === realpath(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'psalm')
|
|
|
|
|
|| realpath($input_path) === realpath(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'psalter')
|
2018-11-27 06:46:19 +01:00
|
|
|
|
|| realpath($input_path) === realpath(\Phar::running(false))
|
2018-01-07 06:11:23 +01:00
|
|
|
|
) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($input_path[0] === '-' && strlen($input_path) === 2) {
|
|
|
|
|
if ($input_path[1] === 'c' || $input_path[1] === 'f') {
|
|
|
|
|
++$i;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($input_path[0] === '-' && $input_path[2] === '=') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (substr($input_path, 0, 2) === '--' && strlen($input_path) > 2) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$filtered_input_paths[] = $input_path;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-18 12:23:15 +01:00
|
|
|
|
if ($filtered_input_paths === ['-']) {
|
2018-01-18 12:48:57 +01:00
|
|
|
|
$meta = stream_get_meta_data(STDIN);
|
|
|
|
|
stream_set_blocking(STDIN, false);
|
2018-01-18 14:35:25 +01:00
|
|
|
|
if ($stdin = fgets(STDIN)) {
|
2018-01-18 12:48:57 +01:00
|
|
|
|
$filtered_input_paths = preg_split('/\s+/', trim($stdin));
|
|
|
|
|
}
|
|
|
|
|
/** @var bool */
|
|
|
|
|
$blocked = $meta['blocked'];
|
|
|
|
|
stream_set_blocking(STDIN, $blocked);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-17 15:35:08 +02:00
|
|
|
|
foreach ($filtered_input_paths as $path_to_check) {
|
2018-01-07 06:11:23 +01:00
|
|
|
|
if ($path_to_check[0] === '-') {
|
2019-05-30 16:30:41 +02:00
|
|
|
|
fwrite(STDERR, 'Invalid usage, expecting psalm [options] [file...]' . PHP_EOL);
|
2018-04-17 17:48:29 +02:00
|
|
|
|
exit(1);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!file_exists($path_to_check)) {
|
2019-05-30 16:30:41 +02:00
|
|
|
|
fwrite(STDERR, 'Cannot locate ' . $path_to_check . PHP_EOL);
|
2018-04-17 17:48:29 +02:00
|
|
|
|
exit(1);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$path_to_check = realpath($path_to_check);
|
|
|
|
|
|
|
|
|
|
if (!$path_to_check) {
|
2019-05-30 16:30:41 +02:00
|
|
|
|
fwrite(STDERR, 'Error getting realpath for file' . PHP_EOL);
|
2018-04-17 17:48:29 +02:00
|
|
|
|
exit(1);
|
2018-01-07 06:11:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$paths_to_check[] = $path_to_check;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$paths_to_check) {
|
|
|
|
|
$paths_to_check = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $paths_to_check;
|
|
|
|
|
}
|