From 03def00f2747595909219f37ea5f24d19b095c84 Mon Sep 17 00:00:00 2001 From: Bruce Weirdan Date: Sun, 27 Nov 2022 08:05:30 -0400 Subject: [PATCH] Check runtime requirements `vendor/autoload/check_platform.php` may be disabled by the user `composer.json`, so we have to repeat the check for runtime requirements. Fixes vimeo/psalm#7560 --- src/Psalm/Internal/Cli/LanguageServer.php | 1 + src/Psalm/Internal/Cli/Plugin.php | 1 + src/Psalm/Internal/Cli/Psalm.php | 1 + src/Psalm/Internal/Cli/Psalter.php | 1 + src/Psalm/Internal/Cli/Refactor.php | 1 + src/Psalm/Internal/CliUtils.php | 58 ++++++++++++++++++++++- 6 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Cli/LanguageServer.php b/src/Psalm/Internal/Cli/LanguageServer.php index f78e4d7f647..68c9a669fe4 100644 --- a/src/Psalm/Internal/Cli/LanguageServer.php +++ b/src/Psalm/Internal/Cli/LanguageServer.php @@ -61,6 +61,7 @@ final class LanguageServer /** @param array $argv */ public static function run(array $argv): void { + CliUtils::checkRuntimeRequirements(); gc_disable(); ErrorHandler::install($argv); $valid_short_options = [ diff --git a/src/Psalm/Internal/Cli/Plugin.php b/src/Psalm/Internal/Cli/Plugin.php index cafaf4597cb..2388238262d 100644 --- a/src/Psalm/Internal/Cli/Plugin.php +++ b/src/Psalm/Internal/Cli/Plugin.php @@ -26,6 +26,7 @@ final class Plugin { public static function run(): void { + CliUtils::checkRuntimeRequirements(); $current_dir = (string)getcwd() . DIRECTORY_SEPARATOR; $vendor_dir = CliUtils::getVendorDir($current_dir); CliUtils::requireAutoloaders($current_dir, false, $vendor_dir); diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php index 52653747a7f..3731ba6e912 100644 --- a/src/Psalm/Internal/Cli/Psalm.php +++ b/src/Psalm/Internal/Cli/Psalm.php @@ -165,6 +165,7 @@ final class Psalm */ public static function run(array $argv): void { + CliUtils::checkRuntimeRequirements(); gc_collect_cycles(); gc_disable(); diff --git a/src/Psalm/Internal/Cli/Psalter.php b/src/Psalm/Internal/Cli/Psalter.php index d5f1898648e..a8b66e5cd62 100644 --- a/src/Psalm/Internal/Cli/Psalter.php +++ b/src/Psalm/Internal/Cli/Psalter.php @@ -94,6 +94,7 @@ final class Psalter /** @param array $argv */ public static function run(array $argv): void { + CliUtils::checkRuntimeRequirements(); gc_collect_cycles(); gc_disable(); diff --git a/src/Psalm/Internal/Cli/Refactor.php b/src/Psalm/Internal/Cli/Refactor.php index e6caa6f7162..16a056a8152 100644 --- a/src/Psalm/Internal/Cli/Refactor.php +++ b/src/Psalm/Internal/Cli/Refactor.php @@ -67,6 +67,7 @@ final class Refactor /** @param array $argv */ public static function run(array $argv): void { + CliUtils::checkRuntimeRequirements(); ini_set('memory_limit', '8192M'); gc_collect_cycles(); diff --git a/src/Psalm/Internal/CliUtils.php b/src/Psalm/Internal/CliUtils.php index c3d536eeb5d..76afad8d136 100644 --- a/src/Psalm/Internal/CliUtils.php +++ b/src/Psalm/Internal/CliUtils.php @@ -13,11 +13,13 @@ use Psalm\Report; use RuntimeException; +use function array_filter; use function array_slice; use function assert; use function count; use function define; use function dirname; +use function extension_loaded; use function fgets; use function file_exists; use function file_get_contents; @@ -47,6 +49,8 @@ use const DIRECTORY_SEPARATOR; use const JSON_THROW_ON_ERROR; use const PHP_EOL; +use const PHP_VERSION; +use const PHP_VERSION_ID; use const STDERR; use const STDIN; @@ -88,7 +92,7 @@ public static function requireAutoloaders( foreach ($autoload_roots as $autoload_root) { $has_autoloader = false; - $nested_autoload_file = dirname($autoload_root, 2). DIRECTORY_SEPARATOR . 'autoload.php'; + $nested_autoload_file = dirname($autoload_root, 2) . DIRECTORY_SEPARATOR . 'autoload.php'; // note: don't realpath $nested_autoload_file, or phar version will fail if (file_exists($nested_autoload_file)) { @@ -300,7 +304,7 @@ public static function getPathsToCheck($f_paths): ?array if ($stdin = fgets(STDIN)) { $filtered_input_paths = preg_split('/\s+/', trim($stdin)); if ($filtered_input_paths === false) { - throw new RuntimeException('Invalid paths: '.preg_last_error_msg()); + throw new RuntimeException('Invalid paths: ' . preg_last_error_msg()); } } $blocked = $meta['blocked']; @@ -515,4 +519,54 @@ public static function runningInCI(): bool || isset($_SERVER['GITHUB_WORKFLOW']) || isset($_SERVER['DRONE']); } + + public static function checkRuntimeRequirements(): void + { + $required_php_version = 7_04_00; + $required_php_version_text = '7.4.0'; + + // the following list was taken from vendor/composer/platform_check.php + // It includes both Psalm's requirements (from composer.json) and the + // requirements of our dependencies `netresearch/jsonmapper` and + // `phpdocumentor/reflection-docblock`. The latter is transitive + // dependency of `felixfbecker/advanced-json-rpc` + $required_extensions = [ + 'dom', + 'filter', + 'json', + 'libxml', + 'pcre', + 'reflection', + 'simplexml', + 'spl', + 'tokenizer', + ]; + $issues = []; + + if (PHP_VERSION_ID < $required_php_version) { + $issues[] = 'Psalm requires a PHP version ">= ' . $required_php_version_text . '".' + . ' You are running ' . PHP_VERSION . '.'; + } + + $missing_extensions = array_filter( + $required_extensions, + static fn(string $ext) => !extension_loaded($ext) + ); + + if ($missing_extensions) { + $issues[] = 'Psalm requires the following PHP extensions to be installed: ' + . implode(', ', $missing_extensions) + . '.'; + } + + if ($issues) { + fwrite( + STDERR, + 'Psalm has detected issues in your platform:' . PHP_EOL . PHP_EOL + . implode(PHP_EOL, $issues) + . PHP_EOL . PHP_EOL + ); + exit(1); + } + } }