Skip to content

Commit 5a132f1

Browse files
authored
Refactor ComposerPhpVersionFactory, ConstantResolver
1 parent 6787df2 commit 5a132f1

File tree

8 files changed

+97
-84
lines changed

8 files changed

+97
-84
lines changed

Diff for: conf/config.neon

-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,6 @@ services:
345345
-
346346
class: PHPStan\Php\ComposerPhpVersionFactory
347347
arguments:
348-
phpVersion: %phpVersion%
349348
composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths%
350349

351350
-

Diff for: src/Analyser/ConstantResolver.php

+68-21
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
namespace PHPStan\Analyser;
44

55
use PhpParser\Node\Name;
6+
use PHPStan\Php\ComposerPhpVersionFactory;
67
use PHPStan\Php\PhpVersion;
78
use PHPStan\Reflection\NamespaceAnswerer;
89
use PHPStan\Reflection\ReflectionProvider;
910
use PHPStan\Reflection\ReflectionProvider\ReflectionProviderProvider;
11+
use PHPStan\ShouldNotHappenException;
1012
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
1113
use PHPStan\Type\Constant\ConstantFloatType;
1214
use PHPStan\Type\Constant\ConstantIntegerType;
@@ -21,6 +23,8 @@
2123
use PHPStan\Type\UnionType;
2224
use function array_key_exists;
2325
use function in_array;
26+
use function is_array;
27+
use function is_int;
2428
use function max;
2529
use function sprintf;
2630
use const INF;
@@ -35,12 +39,13 @@ final class ConstantResolver
3539

3640
/**
3741
* @param string[] $dynamicConstantNames
42+
* @param int|array{min: int, max: int}|null $phpVersion
3843
*/
3944
public function __construct(
4045
private ReflectionProviderProvider $reflectionProviderProvider,
4146
private array $dynamicConstantNames,
42-
private ?PhpVersion $composerMinPhpVersion,
43-
private ?PhpVersion $composerMaxPhpVersion,
47+
private int|array|null $phpVersion,
48+
private ComposerPhpVersionFactory $composerPhpVersionFactory,
4449
)
4550
{
4651
}
@@ -83,15 +88,23 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type
8388
new AccessoryNonFalsyStringType(),
8489
]);
8590
}
91+
92+
$minPhpVersion = null;
93+
$maxPhpVersion = null;
94+
if (in_array($resolvedConstantName, ['PHP_VERSION_ID', 'PHP_MAJOR_VERSION', 'PHP_MINOR_VERSION', 'PHP_RELEASE_VERSION'], true)) {
95+
$minPhpVersion = $this->getMinPhpVersion();
96+
$maxPhpVersion = $this->getMaxPhpVersion();
97+
}
98+
8699
if ($resolvedConstantName === 'PHP_MAJOR_VERSION') {
87100
$minMajor = 5;
88101
$maxMajor = null;
89102

90-
if ($this->composerMinPhpVersion !== null) {
91-
$minMajor = max($minMajor, $this->composerMinPhpVersion->getMajorVersionId());
103+
if ($minPhpVersion !== null) {
104+
$minMajor = max($minMajor, $minPhpVersion->getMajorVersionId());
92105
}
93-
if ($this->composerMaxPhpVersion !== null) {
94-
$maxMajor = $this->composerMaxPhpVersion->getMajorVersionId();
106+
if ($maxPhpVersion !== null) {
107+
$maxMajor = $maxPhpVersion->getMajorVersionId();
95108
}
96109

97110
return $this->createInteger($minMajor, $maxMajor);
@@ -101,12 +114,12 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type
101114
$maxMinor = null;
102115

103116
if (
104-
$this->composerMinPhpVersion !== null
105-
&& $this->composerMaxPhpVersion !== null
106-
&& $this->composerMaxPhpVersion->getMajorVersionId() === $this->composerMinPhpVersion->getMajorVersionId()
117+
$minPhpVersion !== null
118+
&& $maxPhpVersion !== null
119+
&& $maxPhpVersion->getMajorVersionId() === $minPhpVersion->getMajorVersionId()
107120
) {
108-
$minMinor = $this->composerMinPhpVersion->getMinorVersionId();
109-
$maxMinor = $this->composerMaxPhpVersion->getMinorVersionId();
121+
$minMinor = $minPhpVersion->getMinorVersionId();
122+
$maxMinor = $maxPhpVersion->getMinorVersionId();
110123
}
111124

112125
return $this->createInteger($minMinor, $maxMinor);
@@ -116,25 +129,25 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type
116129
$maxRelease = null;
117130

118131
if (
119-
$this->composerMinPhpVersion !== null
120-
&& $this->composerMaxPhpVersion !== null
121-
&& $this->composerMaxPhpVersion->getMajorVersionId() === $this->composerMinPhpVersion->getMajorVersionId()
122-
&& $this->composerMaxPhpVersion->getMinorVersionId() === $this->composerMinPhpVersion->getMinorVersionId()
132+
$minPhpVersion !== null
133+
&& $maxPhpVersion !== null
134+
&& $maxPhpVersion->getMajorVersionId() === $minPhpVersion->getMajorVersionId()
135+
&& $maxPhpVersion->getMinorVersionId() === $minPhpVersion->getMinorVersionId()
123136
) {
124-
$minRelease = $this->composerMinPhpVersion->getPatchVersionId();
125-
$maxRelease = $this->composerMaxPhpVersion->getPatchVersionId();
137+
$minRelease = $minPhpVersion->getPatchVersionId();
138+
$maxRelease = $maxPhpVersion->getPatchVersionId();
126139
}
127140

128141
return $this->createInteger($minRelease, $maxRelease);
129142
}
130143
if ($resolvedConstantName === 'PHP_VERSION_ID') {
131144
$minVersion = 50207;
132145
$maxVersion = null;
133-
if ($this->composerMinPhpVersion !== null) {
134-
$minVersion = max($minVersion, $this->composerMinPhpVersion->getVersionId());
146+
if ($minPhpVersion !== null) {
147+
$minVersion = max($minVersion, $minPhpVersion->getVersionId());
135148
}
136-
if ($this->composerMaxPhpVersion !== null) {
137-
$maxVersion = $this->composerMaxPhpVersion->getVersionId();
149+
if ($maxPhpVersion !== null) {
150+
$maxVersion = $maxPhpVersion->getVersionId();
138151
}
139152

140153
return $this->createInteger($minVersion, $maxVersion);
@@ -351,6 +364,40 @@ public function resolvePredefinedConstant(string $resolvedConstantName): ?Type
351364
return null;
352365
}
353366

367+
private function getMinPhpVersion(): ?PhpVersion
368+
{
369+
if (is_int($this->phpVersion)) {
370+
return null;
371+
}
372+
373+
if (is_array($this->phpVersion)) {
374+
if ($this->phpVersion['max'] < $this->phpVersion['min']) {
375+
throw new ShouldNotHappenException('Invalid PHP version range: phpVersion.max should be greater or equal to phpVersion.min.');
376+
}
377+
378+
return new PhpVersion($this->phpVersion['min']);
379+
}
380+
381+
return $this->composerPhpVersionFactory->getMinVersion();
382+
}
383+
384+
private function getMaxPhpVersion(): ?PhpVersion
385+
{
386+
if (is_int($this->phpVersion)) {
387+
return null;
388+
}
389+
390+
if (is_array($this->phpVersion)) {
391+
if ($this->phpVersion['max'] < $this->phpVersion['min']) {
392+
throw new ShouldNotHappenException('Invalid PHP version range: phpVersion.max should be greater or equal to phpVersion.min.');
393+
}
394+
395+
return new PhpVersion($this->phpVersion['max']);
396+
}
397+
398+
return $this->composerPhpVersionFactory->getMaxVersion();
399+
}
400+
354401
public function resolveConstantType(string $constantName, Type $constantType): Type
355402
{
356403
if ($constantType->isConstantValue()->yes() && in_array($constantName, $this->dynamicConstantNames, true)) {

Diff for: src/Analyser/ConstantResolverFactory.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public function create(): ConstantResolver
2323
return new ConstantResolver(
2424
$this->reflectionProviderProvider,
2525
$this->container->getParameter('dynamicConstantNames'),
26-
$composerFactory->getMinVersion(),
27-
$composerFactory->getMaxVersion(),
26+
$this->container->getParameter('phpVersion'),
27+
$composerFactory,
2828
);
2929
}
3030

Diff for: src/DependencyInjection/ValidateIgnoredErrorsExtension.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPStan\Command\IgnoredRegexValidator;
1313
use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
1414
use PHPStan\File\FileExcluder;
15+
use PHPStan\Php\ComposerPhpVersionFactory;
1516
use PHPStan\Php\PhpVersion;
1617
use PHPStan\PhpDoc\DirectTypeNodeResolverExtensionRegistryProvider;
1718
use PHPStan\PhpDoc\TypeNodeResolver;
@@ -65,7 +66,8 @@ public function loadConfiguration(): void
6566
$reflectionProviderProvider = new DirectReflectionProviderProvider($reflectionProvider);
6667
ReflectionProviderStaticAccessor::registerInstance($reflectionProvider);
6768
PhpVersionStaticAccessor::registerInstance(new PhpVersion(PHP_VERSION_ID));
68-
$constantResolver = new ConstantResolver($reflectionProviderProvider, [], null, null);
69+
$composerPhpVersionFactory = new ComposerPhpVersionFactory([]);
70+
$constantResolver = new ConstantResolver($reflectionProviderProvider, [], null, $composerPhpVersionFactory);
6971

7072
$phpDocParserConfig = new ParserConfig([]);
7173
$ignoredRegexValidator = new IgnoredRegexValidator(

Diff for: src/Php/ComposerPhpVersionFactory.php

+9-46
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,10 @@
33
namespace PHPStan\Php;
44

55
use Composer\Semver\VersionParser;
6-
use Nette\Utils\Json;
7-
use Nette\Utils\JsonException;
86
use Nette\Utils\Strings;
9-
use PHPStan\File\CouldNotReadFileException;
10-
use PHPStan\File\FileReader;
11-
use PHPStan\ShouldNotHappenException;
7+
use PHPStan\Internal\ComposerHelper;
128
use function count;
139
use function end;
14-
use function is_array;
15-
use function is_file;
16-
use function is_int;
1710
use function is_string;
1811
use function min;
1912
use function sprintf;
@@ -29,11 +22,9 @@ final class ComposerPhpVersionFactory
2922

3023
/**
3124
* @param string[] $composerAutoloaderProjectPaths
32-
* @param int|array{min: int, max: int}|null $phpVersion
3325
*/
3426
public function __construct(
3527
private array $composerAutoloaderProjectPaths,
36-
private int|array|null $phpVersion,
3728
)
3829
{
3930
}
@@ -42,23 +33,6 @@ private function initializeVersions(): void
4233
{
4334
$this->initialized = true;
4435

45-
$phpVersion = $this->phpVersion;
46-
47-
if (is_int($phpVersion)) {
48-
throw new ShouldNotHappenException();
49-
}
50-
51-
if (is_array($phpVersion)) {
52-
if ($phpVersion['max'] < $phpVersion['min']) {
53-
throw new ShouldNotHappenException('Invalid PHP version range: phpVersion.max should be greater or equal to phpVersion.min.');
54-
}
55-
56-
$this->minVersion = new PhpVersion($phpVersion['min']);
57-
$this->maxVersion = new PhpVersion($phpVersion['max']);
58-
59-
return;
60-
}
61-
6236
// don't limit minVersion... PHPStan can analyze even PHP5
6337
$this->maxVersion = new PhpVersion(PhpVersionFactory::MAX_PHP_VERSION);
6438

@@ -87,10 +61,6 @@ private function initializeVersions(): void
8761

8862
public function getMinVersion(): ?PhpVersion
8963
{
90-
if (is_int($this->phpVersion)) {
91-
return null;
92-
}
93-
9464
if ($this->initialized === false) {
9565
$this->initializeVersions();
9666
}
@@ -100,10 +70,6 @@ public function getMinVersion(): ?PhpVersion
10070

10171
public function getMaxVersion(): ?PhpVersion
10272
{
103-
if (is_int($this->phpVersion)) {
104-
return null;
105-
}
106-
10773
if ($this->initialized === false) {
10874
$this->initializeVersions();
10975
}
@@ -114,21 +80,18 @@ public function getMaxVersion(): ?PhpVersion
11480
private function getComposerRequireVersion(): ?string
11581
{
11682
$composerPhpVersion = null;
83+
11784
if (count($this->composerAutoloaderProjectPaths) > 0) {
118-
$composerJsonPath = end($this->composerAutoloaderProjectPaths) . '/composer.json';
119-
if (is_file($composerJsonPath)) {
120-
try {
121-
$composerJsonContents = FileReader::read($composerJsonPath);
122-
$composer = Json::decode($composerJsonContents, Json::FORCE_ARRAY);
123-
$requiredVersion = $composer['require']['php'] ?? null;
124-
if (is_string($requiredVersion)) {
125-
$composerPhpVersion = $requiredVersion;
126-
}
127-
} catch (CouldNotReadFileException | JsonException) {
128-
// pass
85+
$composer = ComposerHelper::getComposerConfig(end($this->composerAutoloaderProjectPaths));
86+
if ($composer !== null) {
87+
$requiredVersion = $composer['require']['php'] ?? null;
88+
89+
if (is_string($requiredVersion)) {
90+
$composerPhpVersion = $requiredVersion;
12991
}
13092
}
13193
}
94+
13295
return $composerPhpVersion;
13396
}
13497

Diff for: src/Testing/PHPStanTestCase.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use PHPStan\Internal\DirectoryCreatorException;
2222
use PHPStan\Node\Printer\ExprPrinter;
2323
use PHPStan\Parser\Parser;
24+
use PHPStan\Php\ComposerPhpVersionFactory;
2425
use PHPStan\Php\PhpVersion;
2526
use PHPStan\PhpDoc\TypeNodeResolver;
2627
use PHPStan\PhpDoc\TypeStringResolver;
@@ -137,7 +138,8 @@ public static function createScopeFactory(ReflectionProvider $reflectionProvider
137138
}
138139

139140
$reflectionProviderProvider = new DirectReflectionProviderProvider($reflectionProvider);
140-
$constantResolver = new ConstantResolver($reflectionProviderProvider, $dynamicConstantNames, null, null);
141+
$composerPhpVersionFactory = $container->getByType(ComposerPhpVersionFactory::class);
142+
$constantResolver = new ConstantResolver($reflectionProviderProvider, $dynamicConstantNames, null, $composerPhpVersionFactory);
141143

142144
$initializerExprTypeResolver = new InitializerExprTypeResolver(
143145
$constantResolver,

Diff for: tests/PHPStan/Analyser/nsrt/bug-4434.php

+10-10
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ class HelloWorld
1010
public function testSendEmailToLog(): void
1111
{
1212
foreach ([1] as $emailFile) {
13-
assertType('int<5, max>', PHP_MAJOR_VERSION);
14-
assertType('int<5, max>', \PHP_MAJOR_VERSION);
13+
assertType('int<5, 8>', PHP_MAJOR_VERSION);
14+
assertType('int<5, 8>', \PHP_MAJOR_VERSION);
1515
if (PHP_MAJOR_VERSION === 7) {
1616
assertType('7', PHP_MAJOR_VERSION);
1717
assertType('7', \PHP_MAJOR_VERSION);
1818
} else {
19-
assertType('int<5, 6>|int<8, max>', PHP_MAJOR_VERSION);
20-
assertType('int<5, 6>|int<8, max>', \PHP_MAJOR_VERSION);
19+
assertType('8|int<5, 6>', PHP_MAJOR_VERSION);
20+
assertType('8|int<5, 6>', \PHP_MAJOR_VERSION);
2121
}
2222
}
2323
}
@@ -28,14 +28,14 @@ class HelloWorld2
2828
public function testSendEmailToLog(): void
2929
{
3030
foreach ([1] as $emailFile) {
31-
assertType('int<5, max>', PHP_MAJOR_VERSION);
32-
assertType('int<5, max>', \PHP_MAJOR_VERSION);
31+
assertType('int<5, 8>', PHP_MAJOR_VERSION);
32+
assertType('int<5, 8>', \PHP_MAJOR_VERSION);
3333
if (PHP_MAJOR_VERSION === 100) {
34-
assertType('100', PHP_MAJOR_VERSION);
35-
assertType('100', \PHP_MAJOR_VERSION);
34+
assertType('*NEVER*', PHP_MAJOR_VERSION);
35+
assertType('*NEVER*', \PHP_MAJOR_VERSION);
3636
} else {
37-
assertType('int<5, 99>|int<101, max>', PHP_MAJOR_VERSION);
38-
assertType('int<5, 99>|int<101, max>', \PHP_MAJOR_VERSION);
37+
assertType('int<5, 8>', PHP_MAJOR_VERSION);
38+
assertType('int<5, 8>', \PHP_MAJOR_VERSION);
3939
}
4040
}
4141
}

Diff for: tests/PHPStan/Analyser/nsrt/predefined-constants.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
// core, https://www.php.net/manual/en/reserved.constants.php
66
assertType('non-falsy-string', PHP_VERSION);
7-
assertType('int<5, max>', PHP_MAJOR_VERSION);
7+
assertType('int<5, 8>', PHP_MAJOR_VERSION);
88
assertType('int<0, max>', PHP_MINOR_VERSION);
99
assertType('int<0, max>', PHP_RELEASE_VERSION);
10-
assertType('int<50207, max>', PHP_VERSION_ID);
10+
assertType('int<50207, 80499>', PHP_VERSION_ID);
1111
assertType('string', PHP_EXTRA_VERSION);
1212
assertType('0|1', PHP_ZTS);
1313
assertType('0|1', PHP_DEBUG);

0 commit comments

Comments
 (0)