1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Allow resolving directories from config file location (continued) (#1910)

* Extract function getPsalmHelpText() from psalm.php

* Extract initialiseConfig from psalm.php

* Add -c as valid short option for psalter and psalm-refactor

* Use initialiseConfig in psalter, psalm-refactor and psalm-language-server as well as psalm

* Rely on psalm --alter resolving directory from config file in test

* Remove erronous condition for config file path

This code was based on me wrongly thinking that the config file location
was seprated from the argument name with a space instead of an equals
sign

* Use config dir as current dir in psalm and psalm-refactor, as with psalter and psalm-language-server

* Remove redundant duplicated code

* Refactor: move calls to \Psalm\Config::setComposerClassLoader inside initialiseConfig

* PHPCS fix

* Extract function get_path_to_config from command scripts

* Refactor - extract functions from \Psalm\Config::loadFromXML

* Refactor - reduce verbosity of config loading code

* Allow running e2e tests on windows

* Fix testCompactReport on Windows

* convert line endings to make testCompactReport pass on Windows
This commit is contained in:
Barney Laurance 2019-07-07 13:55:53 +01:00 committed by Matthew Brown
parent 94f934627c
commit ea83068deb
8 changed files with 261 additions and 307 deletions

View File

@ -563,17 +563,13 @@ class Config
/**
* Creates a new config object from an XML string
*
* @throws ConfigException
*
* @param string $base_dir
* @param string $file_contents
* @param string|null $current_dir Current working directory, if different to $base_dir
*
* @return self
* @psalm-suppress MixedArgument
* @psalm-suppress MixedPropertyFetch
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
* @psalm-suppress MixedPropertyAssignment
*/
public static function loadFromXML($base_dir, $file_contents, $current_dir = null)
{
@ -581,8 +577,17 @@ class Config
$current_dir = $base_dir;
}
$config = new static();
self::validateXmlConfig($file_contents);
return self::fromXmlAndPaths($base_dir, $file_contents, $current_dir);
}
/**
* @throws ConfigException
*/
private static function validateXmlConfig(string $file_contents): void
{
$schema_path = dirname(dirname(__DIR__)) . '/config.xsd';
if (!file_exists($schema_path)) {
@ -625,36 +630,60 @@ class Config
}
libxml_clear_errors();
}
}
/**
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
* @psalm-suppress MixedPropertyAssignment
* @psalm-suppress MixedArgument
* @psalm-suppress MixedPropertyFetch
*
* @throws ConfigException
*/
private static function fromXmlAndPaths(string $base_dir, string $file_contents, string $current_dir): self
{
$config = new static();
$config_xml = new SimpleXMLElement($file_contents);
if (isset($config_xml['useDocblockTypes'])) {
$attribute_text = (string) $config_xml['useDocblockTypes'];
$config->use_docblock_types = $attribute_text === 'true' || $attribute_text === '1';
$booleanAttributes = [
'useDocblockTypes' => 'use_docblock_types',
'useDocblockPropertyTypes' => 'use_docblock_property_types',
'throwExceptionOnError' => 'throw_exception',
'hideExternalErrors' => 'hide_external_errors',
'resolveFromConfigFile' => 'resolve_from_config_file',
'allowFileIncludes' => 'allow_includes',
'totallyTyped' => 'totally_typed',
'strictBinaryOperands' => 'strict_binary_operands',
'requireVoidReturnType' => 'add_void_docblocks',
'useAssertForType' => 'use_assert_for_type',
'rememberPropertyAssignmentsAfterCall' => 'remember_property_assignments_after_call',
'allowPhpStormGenerics' => 'allow_phpstorm_generics',
'allowStringToStandInForClass' => 'allow_string_standin_for_class',
'usePhpDocMethodsWithoutMagicCall' => 'use_phpdoc_method_without_magic_or_parent',
'memoizeMethodCallResults' => 'memoize_method_calls',
'hoistConstants' => 'hoist_constants',
'addParamDefaultToDocblockType' => 'add_param_default_to_docblock_type',
'checkForThrowsDocblock' => 'check_for_throws_docblock',
'checkForThrowsInGlobalScope' => 'check_for_throws_in_global_scope',
'forbidEcho' => 'forbid_echo',
'ignoreInternalFunctionFalseReturn' => 'ignore_internal_falsable_issues',
'ignoreInternalFunctionNullReturn' => 'ignore_internal_nullable_issues',
];
foreach ($booleanAttributes as $xmlName => $internalName) {
if (isset($config_xml[$xmlName])) {
$attribute_text = (string) $config_xml[$xmlName];
$config->setBooleanAttribute(
$internalName,
$attribute_text === 'true' || $attribute_text === '1'
);
}
}
if (isset($config_xml['useDocblockPropertyTypes'])) {
$attribute_text = (string) $config_xml['useDocblockPropertyTypes'];
$config->use_docblock_property_types = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['throwExceptionOnError'])) {
$attribute_text = (string) $config_xml['throwExceptionOnError'];
$config->throw_exception = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['hideExternalErrors'])) {
$attribute_text = (string) $config_xml['hideExternalErrors'];
$config->hide_external_errors = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['resolveFromConfigFile'])) {
$attribute_text = (string) $config_xml['resolveFromConfigFile'];
$config->resolve_from_config_file = $attribute_text === 'true' || $attribute_text === '1';
}
if ($config->resolve_from_config_file) {
$config->base_dir = $base_dir;
} else {
@ -686,36 +715,6 @@ class Config
trigger_error('Could not create cache directory: ' . $config->cache_directory, E_USER_ERROR);
}
if (isset($config_xml['allowFileIncludes'])) {
$attribute_text = (string) $config_xml['allowFileIncludes'];
$config->allow_includes = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['totallyTyped'])) {
$attribute_text = (string) $config_xml['totallyTyped'];
$config->totally_typed = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['strictBinaryOperands'])) {
$attribute_text = (string) $config_xml['strictBinaryOperands'];
$config->strict_binary_operands = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['requireVoidReturnType'])) {
$attribute_text = (string) $config_xml['requireVoidReturnType'];
$config->add_void_docblocks = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['useAssertForType'])) {
$attribute_text = (string) $config_xml['useAssertForType'];
$config->use_assert_for_type = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['rememberPropertyAssignmentsAfterCall'])) {
$attribute_text = (string) $config_xml['rememberPropertyAssignmentsAfterCall'];
$config->remember_property_assignments_after_call = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['serializer'])) {
$attribute_text = (string) $config_xml['serializer'];
$config->use_igbinary = $attribute_text === 'igbinary';
@ -723,50 +722,6 @@ class Config
$config->use_igbinary = version_compare($igbinary_version, '2.0.5') >= 0;
}
if (isset($config_xml['allowPhpStormGenerics'])) {
$attribute_text = (string) $config_xml['allowPhpStormGenerics'];
$config->allow_phpstorm_generics = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['allowStringToStandInForClass'])) {
$attribute_text = (string) $config_xml['allowStringToStandInForClass'];
$config->allow_string_standin_for_class = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['usePhpDocMethodsWithoutMagicCall'])) {
$attribute_text = (string) $config_xml['usePhpDocMethodsWithoutMagicCall'];
$config->use_phpdoc_method_without_magic_or_parent = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['memoizeMethodCallResults'])) {
$attribute_text = (string) $config_xml['memoizeMethodCallResults'];
$config->memoize_method_calls = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['hoistConstants'])) {
$attribute_text = (string) $config_xml['hoistConstants'];
$config->hoist_constants = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['addParamDefaultToDocblockType'])) {
$attribute_text = (string) $config_xml['addParamDefaultToDocblockType'];
$config->add_param_default_to_docblock_type = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['checkForThrowsDocblock'])) {
$attribute_text = (string) $config_xml['checkForThrowsDocblock'];
$config->check_for_throws_docblock = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['checkForThrowsInGlobalScope'])) {
$attribute_text = (string) $config_xml['checkForThrowsInGlobalScope'];
$config->check_for_throws_in_global_scope = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['forbidEcho'])) {
$attribute_text = (string) $config_xml['forbidEcho'];
$config->forbid_echo = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['findUnusedCode'])) {
$attribute_text = (string) $config_xml['findUnusedCode'];
@ -779,16 +734,6 @@ class Config
$config->find_unused_variables = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['ignoreInternalFunctionFalseReturn'])) {
$attribute_text = (string) $config_xml['ignoreInternalFunctionFalseReturn'];
$config->ignore_internal_falsable_issues = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['ignoreInternalFunctionNullReturn'])) {
$attribute_text = (string) $config_xml['ignoreInternalFunctionNullReturn'];
$config->ignore_internal_nullable_issues = $attribute_text === 'true' || $attribute_text === '1';
}
if (isset($config_xml['errorBaseline'])) {
$attribute_text = (string) $config_xml['errorBaseline'];
$config->error_baseline = $attribute_text;
@ -1811,4 +1756,9 @@ class Config
{
$this->stub_files[] = $stub_file;
}
private function setBooleanAttribute(string $name, bool $value): void
{
$this->$name = $value;
}
}

View File

@ -1,5 +1,9 @@
<?php
use Composer\Autoload\ClassLoader;
use Psalm\Config;
use Psalm\Exception\ConfigException;
/**
* @param string $current_dir
* @param bool $has_explicit_root
@ -81,7 +85,7 @@ function requireAutoloaders($current_dir, $has_explicit_root, $vendor_dir)
$autoloader = require_once $file;
if (!$first_autoloader
&& $autoloader instanceof \Composer\Autoload\ClassLoader
&& $autoloader instanceof ClassLoader
) {
$first_autoloader = $autoloader;
}
@ -201,11 +205,6 @@ function getPathsToCheck($f_paths)
/** @var string */
$input_path = $input_paths[$i];
if ($i > 0 && in_array($input_paths[$i-1], ['-c', '--config'])) {
// This is the path to the config file, not a path to check.
continue;
}
if (realpath($input_path) === realpath(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'psalm')
|| realpath($input_path) === realpath(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'psalter')
|| realpath($input_path) === realpath(Phar::running(false))
@ -270,3 +269,151 @@ function getPathsToCheck($f_paths)
return $paths_to_check;
}
function getPsalmHelpText(): string
{
return <<<HELP
Usage:
psalm [options] [file...]
Options:
-h, --help
Display this help message
-v, --version
Display the Psalm version
-i, --init [source_dir=src] [level=3]
Create a psalm config file in the current directory that points to [source_dir]
at the required level, from 1, most strict, to 8, most permissive.
--debug
Debug information
--debug-by-line
Debug information on a line-by-line level
-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
--show-info[=BOOLEAN]
Show non-exception parser findings
--show-snippet[=true]
Show code snippets with errors. Options are 'true' or 'false'
--diff
Runs Psalm in diff mode, only checking files that have changed (and their dependents)
--diff-methods
Only checks methods that have changed (and their dependents)
--output-format=console
Changes the output format. Possible values: compact, console, emacs, json, pylint, xml, checkstyle, sonarqube
--find-dead-code[=auto]
--find-unused-code[=auto]
Look for unused code. Options are 'auto' or 'always'. If no value is specified, default is 'auto'
--find-references-to=[class|method|property]
Searches the codebase for references to the given fully-qualified class or method,
where method is in the format class::methodName
--threads=INT
If greater than one, Psalm will run analysis on multiple threads, speeding things up.
--report=PATH
The path where to output report file. The output format is based on the file extension.
(Currently supported format: ".json", ".xml", ".txt", ".emacs")
--report-show-info[=BOOLEAN]
Whether the report should include non-errors in its output (defaults to true)
--clear-cache
Clears all cache files that Psalm uses for this specific project
--clear-global-cache
Clears all cache files that Psalm uses for all projects
--no-cache
Runs Psalm without using cache
--no-reflection-cache
Runs Psalm without using cached representations of unchanged classes and files.
Useful if you want the afterClassLikeVisit plugin hook to run every time you visit a file.
--plugin=PATH
Executes a plugin, an alternative to using the Psalm config
--stats
Shows a breakdown of Psalm's ability to infer types in the codebase
--use-ini-defaults
Use PHP-provided ini defaults for memory and error display
--disable-extension=[extension]
Used to disable certain extensions while Psalm is running.
--set-baseline=PATH
Save all current error level issues to a file, to mark them as info in subsequent runs
--ignore-baseline
Ignore the error baseline
--update-baseline
Update the baseline by removing fixed issues. This will not add new issues to the baseline
--generate-json-map=PATH
Generate a map of node references and types in JSON format, saved to the given path.
--no-progress
Disable the progress indicator
--alter
Run Psalter
--language-server
Run Psalm Language Server
HELP;
}
function initialiseConfig(
?string $path_to_config,
string $current_dir,
string $output_format,
?ClassLoader $first_autoloader
): Config {
try {
if ($path_to_config) {
$config = Config::loadFromXMLFile($path_to_config, $current_dir);
} else {
$config = Config::getConfigForPath($current_dir, $current_dir, $output_format);
}
} catch (Psalm\Exception\ConfigException $e) {
fwrite(STDERR, $e->getMessage() . PHP_EOL);
exit(1);
}
$config->setComposerClassLoader($first_autoloader);
return $config;
}
function get_path_to_config(array $options): ?string
{
$path_to_config = isset($options['c']) && is_string($options['c']) ? realpath($options['c']) : null;
if ($path_to_config === false) {
/** @psalm-suppress InvalidCast */
fwrite(STDERR, 'Could not resolve path to config ' . (string)$options['c'] . PHP_EOL);
exit(1);
}
return $path_to_config;
}

View File

@ -199,13 +199,7 @@ $ini_handler->check();
setlocale(LC_CTYPE, 'C');
$path_to_config = isset($options['c']) && is_string($options['c']) ? realpath($options['c']) : null;
if ($path_to_config === false) {
/** @psalm-suppress InvalidCast */
fwrite(STDERR, 'Could not resolve path to config ' . (string)$options['c'] . PHP_EOL);
exit(1);
}
$path_to_config = get_path_to_config($options);
if (isset($options['tcp'])) {
if (!is_string($options['tcp'])) {
@ -216,20 +210,14 @@ if (isset($options['tcp'])) {
$find_dead_code = isset($options['find-dead-code']);
// initialise custom config, if passed
try {
if ($path_to_config) {
$config = Config::loadFromXMLFile($path_to_config, $current_dir);
} else {
$config = Config::getConfigForPath($current_dir, $current_dir, \Psalm\Report::TYPE_CONSOLE);
}
} catch (Psalm\Exception\ConfigException $e) {
fwrite(STDERR, $e->getMessage());
exit(1);
$config = initialiseConfig($path_to_config, $current_dir, \Psalm\Report::TYPE_CONSOLE, $first_autoloader);
if ($config->resolve_from_config_file) {
$current_dir = $config->base_dir;
chdir($current_dir);
}
$config->setServerMode();
$config->setComposerClassLoader($first_autoloader);
if (isset($options['clear-cache'])) {
$cache_directory = $config->getCacheDirectory();

View File

@ -18,7 +18,7 @@ gc_disable();
$args = array_slice($argv, 1);
$valid_short_options = ['f:', 'm', 'h', 'r:'];
$valid_short_options = ['f:', 'm', 'h', 'r:', 'c:'];
$valid_long_options = [
'help', 'debug', 'config:', 'root:',
'threads:', 'move:', 'into:', 'rename:', 'to:',
@ -141,12 +141,7 @@ $first_autoloader = requireAutoloaders($current_dir, isset($options['r']), $vend
// If XDebug is enabled, restart without it
(new \Composer\XdebugHandler\XdebugHandler('PSALTER'))->check();
$path_to_config = isset($options['c']) && is_string($options['c']) ? realpath($options['c']) : null;
if ($path_to_config === false) {
/** @psalm-suppress InvalidCast */
die('Could not resolve path to config ' . (string)$options['c'] . PHP_EOL);
}
$path_to_config = get_path_to_config($options);
$args = getArguments();
@ -230,15 +225,12 @@ if (!$to_refactor) {
die('No --move or --rename arguments supplied' . PHP_EOL);
}
// initialise custom config, if passed
// Initializing the config can be slow, so any UI logic should precede it, if possible
if ($path_to_config) {
$config = Config::loadFromXMLFile($path_to_config, $current_dir);
} else {
$config = Config::getConfigForPath($current_dir, $current_dir, \Psalm\Report::TYPE_CONSOLE);
}
$config = initialiseConfig($path_to_config, $current_dir, \Psalm\Report::TYPE_CONSOLE, $first_autoloader);
$config->setComposerClassLoader($first_autoloader);
if ($config->resolve_from_config_file) {
$current_dir = $config->base_dir;
chdir($current_dir);
}
$threads = isset($options['threads'])
? (int)$options['threads']

View File

@ -154,118 +154,9 @@ if (isset($options['c']) && is_array($options['c'])) {
exit(1);
}
if (array_key_exists('h', $options)) {
echo <<<HELP
Usage:
psalm [options] [file...]
Options:
-h, --help
Display this help message
-v, --version
Display the Psalm version
-i, --init [source_dir=src] [level=3]
Create a psalm config file in the current directory that points to [source_dir]
at the required level, from 1, most strict, to 8, most permissive.
--debug
Debug information
--debug-by-line
Debug information on a line-by-line level
-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
--show-info[=BOOLEAN]
Show non-exception parser findings
--show-snippet[=true]
Show code snippets with errors. Options are 'true' or 'false'
--diff
Runs Psalm in diff mode, only checking files that have changed (and their dependents)
--diff-methods
Only checks methods that have changed (and their dependents)
--output-format=console
Changes the output format. Possible values: compact, console, emacs, json, pylint, xml, checkstyle, sonarqube
--find-dead-code[=auto]
--find-unused-code[=auto]
Look for unused code. Options are 'auto' or 'always'. If no value is specified, default is 'auto'
--find-references-to=[class|method|property]
Searches the codebase for references to the given fully-qualified class or method,
where method is in the format class::methodName
--threads=INT
If greater than one, Psalm will run analysis on multiple threads, speeding things up.
--report=PATH
The path where to output report file. The output format is based on the file extension.
(Currently supported format: ".json", ".xml", ".txt", ".emacs")
--report-show-info[=BOOLEAN]
Whether the report should include non-errors in its output (defaults to true)
--clear-cache
Clears all cache files that Psalm uses for this specific project
--clear-global-cache
Clears all cache files that Psalm uses for all projects
--no-cache
Runs Psalm without using cache
--no-reflection-cache
Runs Psalm without using cached representations of unchanged classes and files.
Useful if you want the afterClassLikeVisit plugin hook to run every time you visit a file.
--plugin=PATH
Executes a plugin, an alternative to using the Psalm config
--stats
Shows a breakdown of Psalm's ability to infer types in the codebase
--use-ini-defaults
Use PHP-provided ini defaults for memory and error display
--disable-extension=[extension]
Used to disable certain extensions while Psalm is running.
--set-baseline=PATH
Save all current error level issues to a file, to mark them as info in subsequent runs
--ignore-baseline
Ignore the error baseline
--update-baseline
Update the baseline by removing fixed issues. This will not add new issues to the baseline
--generate-json-map=PATH
Generate a map of node references and types in JSON format, saved to the given path.
--no-progress
Disable the progress indicator
--alter
Run Psalter
--language-server
Run Psalm Language Server
HELP;
echo getPsalmHelpText();
/*
--shepherd[=host]
Send data to Shepherd, Psalm's GitHub integration tool.
@ -287,13 +178,7 @@ if (isset($options['root'])) {
$current_dir = (string)getcwd() . DIRECTORY_SEPARATOR;
$path_to_config = isset($options['c']) && is_string($options['c']) ? realpath($options['c']) : null;
if ($path_to_config === false) {
/** @psalm-suppress InvalidCast */
fwrite(STDERR, 'Could not resolve path to config ' . (string)$options['c'] . PHP_EOL);
exit(1);
}
$path_to_config = get_path_to_config($options);
$vendor_dir = getVendorDir($current_dir);
@ -363,20 +248,11 @@ if (array_key_exists('v', $options)) {
exit;
}
// initialise custom config, if passed
try {
if ($path_to_config) {
$config = Config::loadFromXMLFile($path_to_config, $current_dir);
} else {
$config = Config::getConfigForPath($current_dir, $current_dir, $output_format);
}
} catch (Psalm\Exception\ConfigException $e) {
fwrite(STDERR, $e->getMessage() . PHP_EOL);
exit(1);
}
$config = initialiseConfig($path_to_config, $current_dir, $output_format, $first_autoloader);
if ($config->resolve_from_config_file) {
$current_dir = $config->base_dir;
chdir($current_dir);
}
@ -505,8 +381,6 @@ if (isset($options['shepherd'])) {
$plugins[] = $shepherd_plugin;
}
$config->setComposerClassLoader($first_autoloader);
if (isset($options['clear-cache'])) {
$cache_directory = $config->getCacheDirectory();

View File

@ -18,7 +18,7 @@ gc_disable();
$args = array_slice($argv, 1);
$valid_short_options = ['f:', 'm', 'h', 'r:'];
$valid_short_options = ['f:', 'm', 'h', 'r:', 'c:'];
$valid_long_options = [
'help', 'debug', 'debug-by-line', 'config:', 'file:', 'root:',
'plugin:', 'issues:', 'list-supported-issues', 'php-version:', 'dry-run', 'safe-types',
@ -178,22 +178,15 @@ $first_autoloader = requireAutoloaders($current_dir, isset($options['r']), $vend
$paths_to_check = getPathsToCheck(isset($options['f']) ? $options['f'] : null);
$path_to_config = isset($options['c']) && is_string($options['c']) ? realpath($options['c']) : null;
$path_to_config = get_path_to_config($options);
if ($path_to_config === false) {
/** @psalm-suppress InvalidCast */
die('Could not resolve path to config ' . (string)$options['c'] . PHP_EOL);
$config = initialiseConfig($path_to_config, $current_dir, \Psalm\Report::TYPE_CONSOLE, $first_autoloader);
if ($config->resolve_from_config_file) {
$current_dir = $config->base_dir;
chdir($current_dir);
}
// initialise custom config, if passed
if ($path_to_config) {
$config = Config::loadFromXMLFile($path_to_config, $current_dir);
} else {
$config = Config::getConfigForPath($current_dir, $current_dir, \Psalm\Report::TYPE_CONSOLE);
}
$config->setComposerClassLoader($first_autoloader);
$threads = isset($options['threads']) ? (int)$options['threads'] : 1;
$providers = new Psalm\Internal\Provider\Providers(

View File

@ -91,7 +91,7 @@ class PsalmEndToEndTest extends TestCase
$this->assertStringContainsString(
'No errors found!',
$this->runPsalm(['--alter', '--issues=all'], false, false)['STDOUT'] // @todo get --alter working with config dir
$this->runPsalm(['--alter', '--issues=all'], false, true)['STDOUT']
);
$this->assertSame(0, $this->runPsalm([])['CODE']);
@ -100,7 +100,7 @@ class PsalmEndToEndTest extends TestCase
public function testPsalter(): void
{
$this->runPsalmInit();
(new Process([$this->psalter, '--alter', '--issues=InvalidReturnType'], self::$tmpDir))->mustRun();
(new Process(['php', $this->psalter, '--alter', '--issues=InvalidReturnType'], self::$tmpDir))->mustRun();
$this->assertSame(0, $this->runPsalm([])['CODE']);
}
@ -124,7 +124,7 @@ class PsalmEndToEndTest extends TestCase
file_put_contents(self::$tmpDir . '/src/psalm.xml', $psalmXmlContent);
$process = new Process([$this->psalm, '--config', 'src/psalm.xml'], self::$tmpDir);
$process = new Process(['php', $this->psalm, '--config=src/psalm.xml'], self::$tmpDir);
$process->run();
$this->assertSame(1, $process->getExitCode());
$this->assertStringContainsString('InvalidReturnType', $process->getOutput());
@ -140,10 +140,12 @@ class PsalmEndToEndTest extends TestCase
// As config files all contain `resolveFromConfigFile="true"` Psalm shouldn't need to be run from the same
// directory that the code being analysed exists in.
// Windows doesn't read shabangs, so to allow this to work on windows we run `php psalm` rather than just `psalm`.
if ($relyOnConfigDir) {
$process = new Process(array_merge([$this->psalm, '-c', self::$tmpDir . '/psalm.xml'], $args), null);
$process = new Process(array_merge(['php', $this->psalm, '-c=' . self::$tmpDir . '/psalm.xml'], $args), null);
} else {
$process = new Process(array_merge([$this->psalm], $args), self::$tmpDir);
$process = new Process(array_merge(['php', $this->psalm], $args), self::$tmpDir);
}
if (!$shouldFail) {

View File

@ -5,6 +5,7 @@ use function file_get_contents;
use function json_decode;
use function ob_end_clean;
use function ob_start;
use function preg_replace;
use Psalm\Context;
use Psalm\Internal\Analyzer\FileAnalyzer;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
@ -408,10 +409,9 @@ INFO: PossiblyUndefinedGlobalVariable - somefile.php:15:6 - Possibly undefined g
'| ERROR | 7 | UndefinedConstant | Const CHANGE_ME is not defined |' . "\n" .
'| INFO | 15 | PossiblyUndefinedGlobalVariable | Possibly undefined global variable $a, first seen on line 10 |' . "\n" .
'+----------+------+---------------------------------+---------------------------------------------------------------+' . "\n",
IssueBuffer::getOutput($compact_report_options)
$this->toUnixLineEndings(IssueBuffer::getOutput($compact_report_options))
);
}
/**
* @return void
*/
@ -493,4 +493,12 @@ INFO: PossiblyUndefinedGlobalVariable - somefile.php:15:6 - Possibly undefined g
', file_get_contents(__DIR__ . '/test-report.json'));
unlink(__DIR__ . '/test-report.json');
}
/**
* Needed when running on Windows
*/
private function toUnixLineEndings(string $output): string
{
return preg_replace('~\r\n?~', "\n", $output);
}
}