Skip to content

Commit 1992810

Browse files
authored
dynamicConstantNames - allow specifying constant type
1 parent df961a7 commit 1992810

File tree

9 files changed

+63
-8
lines changed

9 files changed

+63
-8
lines changed

src/Analyser/ConstantResolver.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
use PhpParser\Node\Name;
66
use PHPStan\DependencyInjection\AutowiredService;
7+
use PHPStan\DependencyInjection\Container;
78
use PHPStan\Php\ComposerPhpVersionFactory;
89
use PHPStan\Php\PhpVersion;
10+
use PHPStan\PhpDoc\TypeStringResolver;
911
use PHPStan\Reflection\NamespaceAnswerer;
1012
use PHPStan\Reflection\ReflectionProvider;
1113
use PHPStan\Reflection\ReflectionProvider\ReflectionProviderProvider;
@@ -50,6 +52,7 @@ public function __construct(
5052
private array $dynamicConstantNames,
5153
private int|array|null $phpVersion,
5254
private ComposerPhpVersionFactory $composerPhpVersionFactory,
55+
private ?Container $container,
5356
)
5457
{
5558
}
@@ -404,8 +407,18 @@ private function getMaxPhpVersion(): ?PhpVersion
404407

405408
public function resolveConstantType(string $constantName, Type $constantType): Type
406409
{
407-
if ($constantType->isConstantValue()->yes() && in_array($constantName, $this->dynamicConstantNames, true)) {
408-
return $constantType->generalize(GeneralizePrecision::lessSpecific());
410+
if ($constantType->isConstantValue()->yes()) {
411+
if (array_key_exists($constantName, $this->dynamicConstantNames)) {
412+
$phpdocTypes = $this->dynamicConstantNames[$constantName];
413+
if ($this->container !== null) {
414+
$typeStringResolver = $this->container->getByType(TypeStringResolver::class);
415+
return $typeStringResolver->resolve($phpdocTypes, new NameScope(null, [], null));
416+
}
417+
return $constantType;
418+
}
419+
if (in_array($constantName, $this->dynamicConstantNames, true)) {
420+
return $constantType->generalize(GeneralizePrecision::lessSpecific());
421+
}
409422
}
410423

411424
return $constantType;
@@ -414,6 +427,22 @@ public function resolveConstantType(string $constantName, Type $constantType): T
414427
public function resolveClassConstantType(string $className, string $constantName, Type $constantType, ?Type $nativeType): Type
415428
{
416429
$lookupConstantName = sprintf('%s::%s', $className, $constantName);
430+
if (array_key_exists($lookupConstantName, $this->dynamicConstantNames)) {
431+
if ($constantType->isConstantValue()->yes()) {
432+
$phpdocTypes = $this->dynamicConstantNames[$lookupConstantName];
433+
if ($this->container !== null) {
434+
$typeStringResolver = $this->container->getByType(TypeStringResolver::class);
435+
return $typeStringResolver->resolve($phpdocTypes, new NameScope(null, [], $className));
436+
}
437+
}
438+
439+
if ($nativeType !== null) {
440+
return $nativeType;
441+
}
442+
443+
return $constantType;
444+
}
445+
417446
if (in_array($lookupConstantName, $this->dynamicConstantNames, true)) {
418447
if ($nativeType !== null) {
419448
return $nativeType;

src/Analyser/ConstantResolverFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public function create(): ConstantResolver
2727
$this->container->getParameter('dynamicConstantNames'),
2828
$this->container->getParameter('phpVersion'),
2929
$composerFactory,
30+
$this->container,
3031
);
3132
}
3233

src/Analyser/MutatingScope.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6209,6 +6209,11 @@ public function getConstantReflection(Type $typeWithConstant, string $constantNa
62096209
return $typeWithConstant->getConstant($constantName);
62106210
}
62116211

6212+
public function getConstantExplicitTypeFromConfig(string $constantName, Type $constantType): Type
6213+
{
6214+
return $this->constantResolver->resolveConstantType($constantName, $constantType);
6215+
}
6216+
62126217
/**
62136218
* @return array<string, ExpressionTypeHolder>
62146219
*/

src/Analyser/Scope.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ public function getMethodReflection(Type $typeWithMethod, string $methodName): ?
8181

8282
public function getConstantReflection(Type $typeWithConstant, string $constantName): ?ClassConstantReflection;
8383

84+
public function getConstantExplicitTypeFromConfig(string $constantName, Type $constantType): Type;
85+
8486
public function getIterableKeyType(Type $iteratee): Type;
8587

8688
public function getIterableValueType(Type $iteratee): Type;

src/DependencyInjection/ValidateIgnoredErrorsExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function loadConfiguration(): void
6969
ReflectionProviderStaticAccessor::registerInstance($reflectionProvider);
7070
PhpVersionStaticAccessor::registerInstance(new PhpVersion(PHP_VERSION_ID));
7171
$composerPhpVersionFactory = new ComposerPhpVersionFactory([]);
72-
$constantResolver = new ConstantResolver($reflectionProviderProvider, [], null, $composerPhpVersionFactory);
72+
$constantResolver = new ConstantResolver($reflectionProviderProvider, [], null, $composerPhpVersionFactory, null);
7373

7474
$phpDocParserConfig = new ParserConfig([]);
7575
$ignoredRegexValidator = new IgnoredRegexValidator(

src/Testing/PHPStanTestCase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public static function createScopeFactory(ReflectionProvider $reflectionProvider
140140

141141
$reflectionProviderProvider = new DirectReflectionProviderProvider($reflectionProvider);
142142
$composerPhpVersionFactory = $container->getByType(ComposerPhpVersionFactory::class);
143-
$constantResolver = new ConstantResolver($reflectionProviderProvider, $dynamicConstantNames, null, $composerPhpVersionFactory);
143+
$constantResolver = new ConstantResolver($reflectionProviderProvider, $dynamicConstantNames, null, $composerPhpVersionFactory, $container);
144144

145145
$initializerExprTypeResolver = new InitializerExprTypeResolver(
146146
$constantResolver,

src/Type/Php/DefineConstantTypeSpecifyingExtension.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,17 @@ public function specifyTypes(
5252
return new SpecifiedTypes([], []);
5353
}
5454

55+
$valueType = $scope->getType($node->getArgs()[1]->value);
56+
$finalType = $scope->getConstantExplicitTypeFromConfig(
57+
$constantName->getValue(),
58+
$valueType,
59+
);
60+
5561
return $this->typeSpecifier->create(
5662
new Node\Expr\ConstFetch(
5763
new Node\Name\FullyQualified($constantName->getValue()),
5864
),
59-
$scope->getType($node->getArgs()[1]->value),
65+
$finalType,
6066
TypeSpecifierContext::createTruthy(),
6167
$scope,
6268
)->setAlwaysOverwriteTypes();

tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8247,6 +8247,10 @@ public static function dataDynamicConstants(): array
82478247
'string',
82488248
'DynamicConstants\DynamicConstantClass::DYNAMIC_CONSTANT_IN_CLASS',
82498249
],
8250+
[
8251+
'string|null',
8252+
'DynamicConstants\DynamicConstantClass::DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES_IN_CLASS',
8253+
],
82508254
[
82518255
"'abc123def'",
82528256
'DynamicConstants\DynamicConstantClass::PURE_CONSTANT_IN_CLASS',
@@ -8256,13 +8260,17 @@ public static function dataDynamicConstants(): array
82568260
'DynamicConstants\NoDynamicConstantClass::DYNAMIC_CONSTANT_IN_CLASS',
82578261
],
82588262
[
8259-
'false',
8263+
'bool',
82608264
'GLOBAL_DYNAMIC_CONSTANT',
82618265
],
82628266
[
82638267
'123',
82648268
'GLOBAL_PURE_CONSTANT',
82658269
],
8270+
[
8271+
'string|null',
8272+
'GLOBAL_DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES',
8273+
],
82668274
];
82678275
}
82688276

@@ -8278,8 +8286,10 @@ public function testDynamicConstants(
82788286
$expression,
82798287
'die',
82808288
[
8281-
'DynamicConstants\\DynamicConstantClass::DYNAMIC_CONSTANT_IN_CLASS',
8282-
'GLOBAL_DYNAMIC_CONSTANT',
8289+
0 => 'DynamicConstants\\DynamicConstantClass::DYNAMIC_CONSTANT_IN_CLASS',
8290+
1 => 'GLOBAL_DYNAMIC_CONSTANT',
8291+
'DynamicConstants\\DynamicConstantClass::DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES_IN_CLASS' => 'string|null',
8292+
'GLOBAL_DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES' => 'string|null',
82838293
],
82848294
);
82858295
}

tests/PHPStan/Analyser/data/dynamic-constant.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
define('GLOBAL_PURE_CONSTANT', 123);
66
define('GLOBAL_DYNAMIC_CONSTANT', false);
7+
define('GLOBAL_DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES', null);
78

89
class DynamicConstantClass
910
{
1011
const DYNAMIC_CONSTANT_IN_CLASS = 'abcdef';
12+
const DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES_IN_CLASS = 'xyz';
1113
const PURE_CONSTANT_IN_CLASS = 'abc123def';
1214
}
1315

0 commit comments

Comments
 (0)