Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow setting php version from config or composer.json #2715

Merged
merged 2 commits into from Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -15,13 +15,14 @@
],
"require": {
"php": "^7.1.3|^8",
"ext-SimpleXML": "*",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-SimpleXML": "*",
"ext-tokenizer": "*",
"amphp/amp": "^2.1",
"amphp/byte-stream": "^1.5",
"composer/semver": "^1.5",
"composer/xdebug-handler": "^1.1",
"felixfbecker/advanced-json-rpc": "^3.0.3",
"felixfbecker/language-server-protocol": "^1.4",
Expand Down
1 change: 1 addition & 0 deletions config.xsd
Expand Up @@ -26,6 +26,7 @@
<xs:attribute name="errorBaseline" type="xs:string" />
<xs:attribute name="maxStringLength" type="xs:string" />
<xs:attribute name="name" type="xs:string" />
<xs:attribute name="phpVersion" type="xs:string" />
<xs:attribute name="serializer" type="xs:string" />

<xs:attribute name="addParamDefaultToDocblockType" type="xs:boolean" default="false" />
Expand Down
46 changes: 46 additions & 0 deletions src/Psalm/Config.php
@@ -1,6 +1,7 @@
<?php
namespace Psalm;

use Composer\Semver\Semver;
use Webmozart\PathUtil\Path;
use function array_merge;
use function array_pop;
Expand Down Expand Up @@ -182,6 +183,13 @@ class Config
*/
public $base_dir;

/**
* The PHP version to assume as declared in the config file
*
* @var string|null
*/
private $configured_php_version;

/**
* @var array<int, string>
*/
Expand Down Expand Up @@ -745,6 +753,10 @@ private static function fromXmlAndPaths(string $base_dir, string $file_contents,
$base_dir = $current_dir;
}

if (isset($config_xml['phpVersion'])) {
$config->configured_php_version = (string) $config_xml['phpVersion'];
}

if (isset($config_xml['autoloader'])) {
$autoloader_path = $config->base_dir . DIRECTORY_SEPARATOR . $config_xml['autoloader'];

Expand Down Expand Up @@ -1861,8 +1873,42 @@ public function addStubFile(string $stub_file)
$this->stub_files[] = $stub_file;
}

public function getPhpVersion(): ?string
{
if (isset($this->configured_php_version)) {
return $this->configured_php_version;
}

return $this->getPHPVersionFromComposerJson();
}

private function setBooleanAttribute(string $name, bool $value): void
{
$this->$name = $value;
}

/**
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedArrayAccess
*/
private function getPHPVersionFromComposerJson(): ?string
{
$composer_json_path = $this->base_dir . DIRECTORY_SEPARATOR. 'composer.json';

if (file_exists($composer_json_path)) {
if (!$composer_json = json_decode(file_get_contents($composer_json_path), true)) {
throw new \UnexpectedValueException('Invalid composer.json at ' . $composer_json_path);
}
$php_version = $composer_json['require']['php'] ?? null;

if ($php_version) {
foreach (['5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0'] as $candidate) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like having to do this loop here hard-coding versions, but IMHO there's no way to deterministically reverse a composer range to end up with a minimum version.

What still is a problem is that the knowledge of what versions of PHP psalm has actual support for isn't centralized but so far only existed in this regex here:

if (!preg_match('/^(5\.[456]|7\.[01234]|8\.[0])(\..*)?$/', $version)) {

maybe for the future it would be worth to actually have proper API to get to this information so further duplication isn't needed.

if (Semver::satisfies($candidate, (string)$php_version)) {
return $candidate;
}
}
}
}
return null;
}
}
4 changes: 4 additions & 0 deletions src/psalm.php
Expand Up @@ -493,6 +493,10 @@ function ($arg) {
$progress
);

if (!isset($options['php-version'])) {
$options['php-version'] = $config->getPhpVersion();
}

if (isset($options['php-version'])) {
if (!is_string($options['php-version'])) {
die('Expecting a version number in the format x.y' . PHP_EOL);
Expand Down
26 changes: 26 additions & 0 deletions tests/Config/ConfigTest.php
Expand Up @@ -1451,4 +1451,30 @@ public function testGetPossiblePsr4Path()
$config->getPotentialComposerFilePathForClassLike('Psalm\\Tests\\Foo')
);
}

/**
* @return void
*/
public function testTakesPhpVersionFromConfigFile()
{
$cfg = Config::loadFromXML(
dirname(__DIR__, 2),
'<?xml version="1.0"?><psalm phpVersion="7.1"></psalm>'
);
$this->assertSame('7.1', $cfg->getPhpVersion());
}

/**
* @return void
*/
public function testReadsComposerJsonForPhpVersion()
{

$root = __DIR__ . '/../fixtures/ComposerPhpVersion';
$cfg = Config::loadFromXML($root, "<?xml version=\"1.0\"?><psalm></psalm>");
$this->assertSame('7.2', $cfg->getPhpVersion());

$cfg = Config::loadFromXML($root, "<?xml version=\"1.0\"?><psalm phpVersion='8.0'></psalm>");
$this->assertSame('8.0', $cfg->getPhpVersion());
}
}
5 changes: 5 additions & 0 deletions tests/fixtures/ComposerPhpVersion/composer.json
@@ -0,0 +1,5 @@
{
"require": {
"php": "7.2|7.3,<8"
}
}