Skip to content

Commit efc172e

Browse files
committed
feat: add dynamic parameter type extensions
1 parent b0cdf91 commit efc172e

16 files changed

+351
-2
lines changed

phpstan-baseline.neon

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,60 @@ parameters:
4242
count: 1
4343
path: src/Analyser/MutatingScope.php
4444

45+
-
46+
rawMessage: 'Call to method getFunctionParameterClosureTypeExtensions() of deprecated interface PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider.'
47+
identifier: method.deprecatedInterface
48+
count: 1
49+
path: src/Analyser/NodeScopeResolver.php
50+
51+
-
52+
rawMessage: 'Call to method getMethodParameterClosureTypeExtensions() of deprecated interface PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider.'
53+
identifier: method.deprecatedInterface
54+
count: 1
55+
path: src/Analyser/NodeScopeResolver.php
56+
57+
-
58+
rawMessage: 'Call to method getStaticMethodParameterClosureTypeExtensions() of deprecated interface PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider.'
59+
identifier: method.deprecatedInterface
60+
count: 1
61+
path: src/Analyser/NodeScopeResolver.php
62+
63+
-
64+
rawMessage: 'Call to method getTypeFromFunctionCall() of deprecated interface PHPStan\Type\FunctionParameterClosureTypeExtension.'
65+
identifier: method.deprecatedInterface
66+
count: 1
67+
path: src/Analyser/NodeScopeResolver.php
68+
69+
-
70+
rawMessage: 'Call to method getTypeFromMethodCall() of deprecated interface PHPStan\Type\MethodParameterClosureTypeExtension.'
71+
identifier: method.deprecatedInterface
72+
count: 1
73+
path: src/Analyser/NodeScopeResolver.php
74+
75+
-
76+
rawMessage: 'Call to method getTypeFromStaticMethodCall() of deprecated interface PHPStan\Type\StaticMethodParameterClosureTypeExtension.'
77+
identifier: method.deprecatedInterface
78+
count: 1
79+
path: src/Analyser/NodeScopeResolver.php
80+
81+
-
82+
rawMessage: 'Call to method isFunctionSupported() of deprecated interface PHPStan\Type\FunctionParameterClosureTypeExtension.'
83+
identifier: method.deprecatedInterface
84+
count: 1
85+
path: src/Analyser/NodeScopeResolver.php
86+
87+
-
88+
rawMessage: 'Call to method isMethodSupported() of deprecated interface PHPStan\Type\MethodParameterClosureTypeExtension.'
89+
identifier: method.deprecatedInterface
90+
count: 1
91+
path: src/Analyser/NodeScopeResolver.php
92+
93+
-
94+
rawMessage: 'Call to method isStaticMethodSupported() of deprecated interface PHPStan\Type\StaticMethodParameterClosureTypeExtension.'
95+
identifier: method.deprecatedInterface
96+
count: 1
97+
path: src/Analyser/NodeScopeResolver.php
98+
4599
-
46100
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.'
47101
identifier: phpstanApi.instanceofType
@@ -54,6 +108,18 @@ parameters:
54108
count: 1
55109
path: src/Analyser/NodeScopeResolver.php
56110

111+
-
112+
rawMessage: 'Parameter $parameterClosureTypeExtensionProvider of method PHPStan\Analyser\NodeScopeResolver::__construct() has typehint with deprecated interface PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider.'
113+
identifier: parameter.deprecatedInterface
114+
count: 1
115+
path: src/Analyser/NodeScopeResolver.php
116+
117+
-
118+
rawMessage: Property $parameterClosureTypeExtensionProvider references deprecated interface PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider in its type.
119+
identifier: property.deprecatedInterface
120+
count: 1
121+
path: src/Analyser/NodeScopeResolver.php
122+
57123
-
58124
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantBooleanType is error-prone and deprecated. Use Type::isTrue() or Type::isFalse() instead.'
59125
identifier: phpstanApi.instanceofType
@@ -216,6 +282,42 @@ parameters:
216282
count: 1
217283
path: src/DependencyInjection/NeonAdapter.php
218284

285+
-
286+
rawMessage: Access to constant on deprecated interface PHPStan\Type\FunctionParameterClosureTypeExtension.
287+
identifier: classConstant.deprecatedInterface
288+
count: 1
289+
path: src/DependencyInjection/ValidateServiceTagsExtension.php
290+
291+
-
292+
rawMessage: Access to constant on deprecated interface PHPStan\Type\MethodParameterClosureTypeExtension.
293+
identifier: classConstant.deprecatedInterface
294+
count: 1
295+
path: src/DependencyInjection/ValidateServiceTagsExtension.php
296+
297+
-
298+
rawMessage: Access to constant on deprecated interface PHPStan\Type\StaticMethodParameterClosureTypeExtension.
299+
identifier: classConstant.deprecatedInterface
300+
count: 1
301+
path: src/DependencyInjection/ValidateServiceTagsExtension.php
302+
303+
-
304+
rawMessage: Fetching class constant FUNCTION_TAG of deprecated class PHPStan\DependencyInjection\Type\LazyParameterClosureTypeExtensionProvider.
305+
identifier: classConstant.deprecatedClass
306+
count: 1
307+
path: src/DependencyInjection/ValidateServiceTagsExtension.php
308+
309+
-
310+
rawMessage: Fetching class constant METHOD_TAG of deprecated class PHPStan\DependencyInjection\Type\LazyParameterClosureTypeExtensionProvider.
311+
identifier: classConstant.deprecatedClass
312+
count: 1
313+
path: src/DependencyInjection/ValidateServiceTagsExtension.php
314+
315+
-
316+
rawMessage: Fetching class constant STATIC_METHOD_TAG of deprecated class PHPStan\DependencyInjection\Type\LazyParameterClosureTypeExtensionProvider.
317+
identifier: classConstant.deprecatedClass
318+
count: 1
319+
path: src/DependencyInjection/ValidateServiceTagsExtension.php
320+
219321
-
220322
rawMessage: Creating new ReflectionClass is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine. Use objects retrieved from ReflectionProvider instead.
221323
identifier: phpstanApi.runtimeReflection
@@ -771,6 +873,18 @@ parameters:
771873
count: 1
772874
path: src/Testing/PHPStanTestCase.php
773875

876+
-
877+
rawMessage: Access to constant on deprecated interface PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider.
878+
identifier: classConstant.deprecatedInterface
879+
count: 1
880+
path: src/Testing/RuleTestCase.php
881+
882+
-
883+
rawMessage: Access to constant on deprecated interface PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider.
884+
identifier: classConstant.deprecatedInterface
885+
count: 1
886+
path: src/Testing/TypeInferenceTestCase.php
887+
774888
-
775889
rawMessage: 'Doing instanceof PHPStan\Type\ConstantScalarType is error-prone and deprecated. Use Type::isConstantScalarValue() or Type::getConstantScalarTypes() or Type::getConstantScalarValues() instead.'
776890
identifier: phpstanApi.instanceofType
@@ -1629,6 +1743,12 @@ parameters:
16291743
count: 1
16301744
path: src/Type/Php/NumberFormatFunctionDynamicReturnTypeExtension.php
16311745

1746+
-
1747+
rawMessage: Class PHPStan\Type\Php\PregReplaceCallbackClosureTypeExtension implements deprecated interface PHPStan\Type\FunctionParameterClosureTypeExtension.
1748+
identifier: class.implementsDeprecatedInterface
1749+
count: 1
1750+
path: src/Type/Php/PregReplaceCallbackClosureTypeExtension.php
1751+
16321752
-
16331753
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.'
16341754
identifier: phpstanApi.instanceofType
@@ -1857,6 +1977,12 @@ parameters:
18571977
count: 2
18581978
path: src/Type/VoidType.php
18591979

1980+
-
1981+
rawMessage: Access to constant on deprecated interface PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider.
1982+
identifier: classConstant.deprecatedInterface
1983+
count: 1
1984+
path: tests/PHPStan/Analyser/AnalyserTest.php
1985+
18601986
-
18611987
rawMessage: 'Class PHPStan\Analyser\AnonymousClassNameRuleTest extends generic class PHPStan\Testing\RuleTestCase but does not specify its types: TRule'
18621988
identifier: missingType.generics

src/Analyser/NodeScopeResolver.php

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
use PHPStan\DependencyInjection\AutowiredParameter;
6767
use PHPStan\DependencyInjection\AutowiredService;
6868
use PHPStan\DependencyInjection\Reflection\ClassReflectionExtensionRegistryProvider;
69+
use PHPStan\DependencyInjection\Type\DynamicParameterTypeExtensionProvider;
6970
use PHPStan\DependencyInjection\Type\DynamicThrowTypeExtensionProvider;
7071
use PHPStan\DependencyInjection\Type\ParameterClosureThisExtensionProvider;
7172
use PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider;
@@ -276,6 +277,7 @@ public function __construct(
276277
private readonly DynamicThrowTypeExtensionProvider $dynamicThrowTypeExtensionProvider,
277278
private readonly ReadWritePropertiesExtensionProvider $readWritePropertiesExtensionProvider,
278279
private readonly ParameterClosureThisExtensionProvider $parameterClosureThisExtensionProvider,
280+
private readonly DynamicParameterTypeExtensionProvider $dynamicParameterTypeExtensionProvider,
279281
private readonly ParameterClosureTypeExtensionProvider $parameterClosureTypeExtensionProvider,
280282
private readonly ScopeFactory $scopeFactory,
281283
#[AutowiredParameter]
@@ -5455,7 +5457,8 @@ private function processArgs(
54555457
}
54565458

54575459
if ($parameter !== null) {
5458-
$overwritingParameterType = $this->getParameterTypeFromParameterClosureTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass);
5460+
$overwritingParameterType = $this->getParameterTypeFromDynamicParameterTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass)
5461+
?? $this->getParameterTypeFromParameterClosureTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass);
54595462

54605463
if ($overwritingParameterType !== null) {
54615464
$parameterType = $overwritingParameterType;
@@ -5510,7 +5513,8 @@ private function processArgs(
55105513
}
55115514

55125515
if ($parameter !== null) {
5513-
$overwritingParameterType = $this->getParameterTypeFromParameterClosureTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass);
5516+
$overwritingParameterType = $this->getParameterTypeFromDynamicParameterTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass)
5517+
?? $this->getParameterTypeFromParameterClosureTypeExtension($callLike, $calleeReflection, $parameter, $scopeToPass);
55145518

55155519
if ($overwritingParameterType !== null) {
55165520
$parameterType = $overwritingParameterType;
@@ -5649,6 +5653,46 @@ private function processArgs(
56495653
return new ExpressionResult($scope, $hasYield, $isAlwaysTerminating, $throwPoints, $impurePoints);
56505654
}
56515655

5656+
/**
5657+
* @param MethodReflection|FunctionReflection|null $calleeReflection
5658+
*/
5659+
private function getParameterTypeFromDynamicParameterTypeExtension(CallLike $callLike, $calleeReflection, ParameterReflection $parameter, MutatingScope $scope): ?Type
5660+
{
5661+
if ($callLike instanceof FuncCall && $calleeReflection instanceof FunctionReflection) {
5662+
foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicFunctionParameterTypeExtensions() as $dynamicFunctionParameterTypeExtension) {
5663+
if (!$dynamicFunctionParameterTypeExtension->isFunctionSupported($calleeReflection, $parameter)) {
5664+
continue;
5665+
}
5666+
$type = $dynamicFunctionParameterTypeExtension->getTypeFromFunctionCall($calleeReflection, $callLike, $parameter, $scope);
5667+
if ($type !== null) {
5668+
return $type;
5669+
}
5670+
}
5671+
} elseif ($callLike instanceof StaticCall && $calleeReflection instanceof MethodReflection) {
5672+
foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicStaticMethodParameterTypeExtensions() as $dynamicStaticMethodParameterTypeExtension) {
5673+
if (!$dynamicStaticMethodParameterTypeExtension->isStaticMethodSupported($calleeReflection, $parameter)) {
5674+
continue;
5675+
}
5676+
$type = $dynamicStaticMethodParameterTypeExtension->getTypeFromStaticMethodCall($calleeReflection, $callLike, $parameter, $scope);
5677+
if ($type !== null) {
5678+
return $type;
5679+
}
5680+
}
5681+
} elseif ($callLike instanceof MethodCall && $calleeReflection instanceof MethodReflection) {
5682+
foreach ($this->dynamicParameterTypeExtensionProvider->getDynamicMethodParameterTypeExtensions() as $dynamicMethodParameterTypeExtension) {
5683+
if (!$dynamicMethodParameterTypeExtension->isMethodSupported($calleeReflection, $parameter)) {
5684+
continue;
5685+
}
5686+
$type = $dynamicMethodParameterTypeExtension->getTypeFromMethodCall($calleeReflection, $callLike, $parameter, $scope);
5687+
if ($type !== null) {
5688+
return $type;
5689+
}
5690+
}
5691+
}
5692+
5693+
return null;
5694+
}
5695+
56525696
/**
56535697
* @param MethodReflection|FunctionReflection|null $calleeReflection
56545698
*/
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\DependencyInjection\Type;
4+
5+
use PHPStan\Type\DynamicFunctionParameterTypeExtension;
6+
use PHPStan\Type\DynamicMethodParameterTypeExtension;
7+
use PHPStan\Type\DynamicStaticMethodParameterTypeExtension;
8+
9+
interface DynamicParameterTypeExtensionProvider
10+
{
11+
12+
/** @return DynamicFunctionParameterTypeExtension[] */
13+
public function getDynamicFunctionParameterTypeExtensions(): array;
14+
15+
/** @return DynamicMethodParameterTypeExtension[] */
16+
public function getDynamicMethodParameterTypeExtensions(): array;
17+
18+
/** @return DynamicStaticMethodParameterTypeExtension[] */
19+
public function getDynamicStaticMethodParameterTypeExtensions(): array;
20+
21+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\DependencyInjection\Type;
4+
5+
use PHPStan\DependencyInjection\AutowiredService;
6+
use PHPStan\DependencyInjection\Container;
7+
8+
#[AutowiredService(as: DynamicParameterTypeExtensionProvider::class)]
9+
final class LazyDynamicParameterTypeExtensionProvider implements DynamicParameterTypeExtensionProvider
10+
{
11+
12+
public const FUNCTION_TAG = 'phpstan.dynamicFunctionParameterTypeExtension';
13+
public const METHOD_TAG = 'phpstan.dynamicMethodParameterTypeExtension';
14+
public const STATIC_METHOD_TAG = 'phpstan.dynamicStaticMethodParameterTypeExtension';
15+
16+
public function __construct(private Container $container)
17+
{
18+
}
19+
20+
public function getDynamicFunctionParameterTypeExtensions(): array
21+
{
22+
return $this->container->getServicesByTag(self::FUNCTION_TAG);
23+
}
24+
25+
public function getDynamicMethodParameterTypeExtensions(): array
26+
{
27+
return $this->container->getServicesByTag(self::METHOD_TAG);
28+
}
29+
30+
public function getDynamicStaticMethodParameterTypeExtensions(): array
31+
{
32+
return $this->container->getServicesByTag(self::STATIC_METHOD_TAG);
33+
}
34+
35+
}

src/DependencyInjection/Type/LazyParameterClosureTypeExtensionProvider.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
use PHPStan\DependencyInjection\AutowiredService;
66
use PHPStan\DependencyInjection\Container;
77

8+
/**
9+
* @deprecated
10+
* @see \PHPStan\DependencyInjection\Type\LazyDynamicParameterTypeExtensionProvider
11+
*/
812
#[AutowiredService(as: ParameterClosureTypeExtensionProvider::class)]
913
final class LazyParameterClosureTypeExtensionProvider implements ParameterClosureTypeExtensionProvider
1014
{

src/DependencyInjection/Type/ParameterClosureTypeExtensionProvider.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
use PHPStan\Type\MethodParameterClosureTypeExtension;
77
use PHPStan\Type\StaticMethodParameterClosureTypeExtension;
88

9+
/**
10+
* @deprecated
11+
* @see \PHPStan\DependencyInjection\Type\DynamicParameterTypeExtensionProvider
12+
*/
913
interface ParameterClosureTypeExtensionProvider
1014
{
1115

src/DependencyInjection/ValidateServiceTagsExtension.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Broker\BrokerFactory;
1212
use PHPStan\Collectors\Collector;
1313
use PHPStan\Collectors\RegistryFactory as CollectorRegistryFactory;
14+
use PHPStan\DependencyInjection\Type\LazyDynamicParameterTypeExtensionProvider;
1415
use PHPStan\DependencyInjection\Type\LazyDynamicThrowTypeExtensionProvider;
1516
use PHPStan\DependencyInjection\Type\LazyParameterClosureThisExtensionProvider;
1617
use PHPStan\DependencyInjection\Type\LazyParameterClosureTypeExtensionProvider;
@@ -42,10 +43,13 @@
4243
use PHPStan\Rules\RestrictedUsage\RestrictedPropertyUsageExtension;
4344
use PHPStan\Rules\Rule;
4445
use PHPStan\ShouldNotHappenException;
46+
use PHPStan\Type\DynamicFunctionParameterTypeExtension;
4547
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
4648
use PHPStan\Type\DynamicFunctionThrowTypeExtension;
49+
use PHPStan\Type\DynamicMethodParameterTypeExtension;
4750
use PHPStan\Type\DynamicMethodReturnTypeExtension;
4851
use PHPStan\Type\DynamicMethodThrowTypeExtension;
52+
use PHPStan\Type\DynamicStaticMethodParameterTypeExtension;
4953
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
5054
use PHPStan\Type\DynamicStaticMethodThrowTypeExtension;
5155
use PHPStan\Type\ExpressionTypeResolverExtension;
@@ -95,6 +99,9 @@ final class ValidateServiceTagsExtension extends CompilerExtension
9599
FunctionParameterClosureThisExtension::class => LazyParameterClosureThisExtensionProvider::FUNCTION_TAG,
96100
MethodParameterClosureThisExtension::class => LazyParameterClosureThisExtensionProvider::METHOD_TAG,
97101
StaticMethodParameterClosureThisExtension::class => LazyParameterClosureThisExtensionProvider::STATIC_METHOD_TAG,
102+
DynamicFunctionParameterTypeExtension::class => LazyDynamicParameterTypeExtensionProvider::FUNCTION_TAG,
103+
DynamicMethodParameterTypeExtension::class => LazyDynamicParameterTypeExtensionProvider::METHOD_TAG,
104+
DynamicStaticMethodParameterTypeExtension::class => LazyDynamicParameterTypeExtensionProvider::STATIC_METHOD_TAG,
98105
FunctionParameterClosureTypeExtension::class => LazyParameterClosureTypeExtensionProvider::FUNCTION_TAG,
99106
MethodParameterClosureTypeExtension::class => LazyParameterClosureTypeExtensionProvider::METHOD_TAG,
100107
StaticMethodParameterClosureTypeExtension::class => LazyParameterClosureTypeExtensionProvider::STATIC_METHOD_TAG,

src/Testing/RuleTestCase.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use PHPStan\Collectors\Collector;
1818
use PHPStan\Collectors\Registry as CollectorRegistry;
1919
use PHPStan\Dependency\DependencyResolver;
20+
use PHPStan\DependencyInjection\Type\DynamicParameterTypeExtensionProvider;
2021
use PHPStan\DependencyInjection\Type\DynamicThrowTypeExtensionProvider;
2122
use PHPStan\DependencyInjection\Type\ParameterClosureThisExtensionProvider;
2223
use PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider;
@@ -105,6 +106,7 @@ protected function createNodeScopeResolver(): NodeScopeResolver|GeneratorNodeSco
105106
self::getContainer()->getByType(DynamicThrowTypeExtensionProvider::class),
106107
$readWritePropertiesExtensions !== [] ? new DirectReadWritePropertiesExtensionProvider($readWritePropertiesExtensions) : self::getContainer()->getByType(ReadWritePropertiesExtensionProvider::class),
107108
self::getContainer()->getByType(ParameterClosureThisExtensionProvider::class),
109+
self::getContainer()->getByType(DynamicParameterTypeExtensionProvider::class),
108110
self::getContainer()->getByType(ParameterClosureTypeExtensionProvider::class),
109111
static::createScopeFactory(),
110112
$this->shouldPolluteScopeWithLoopInitialAssignments(),

src/Testing/TypeInferenceTestCase.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Analyser\NodeScopeResolver;
1212
use PHPStan\Analyser\Scope;
1313
use PHPStan\Analyser\ScopeContext;
14+
use PHPStan\DependencyInjection\Type\DynamicParameterTypeExtensionProvider;
1415
use PHPStan\DependencyInjection\Type\DynamicThrowTypeExtensionProvider;
1516
use PHPStan\DependencyInjection\Type\ParameterClosureThisExtensionProvider;
1617
use PHPStan\DependencyInjection\Type\ParameterClosureTypeExtensionProvider;
@@ -80,6 +81,7 @@ protected static function createNodeScopeResolver(): NodeScopeResolver|Generator
8081
self::getContainer()->getByType(DynamicThrowTypeExtensionProvider::class),
8182
self::getContainer()->getByType(ReadWritePropertiesExtensionProvider::class),
8283
self::getContainer()->getByType(ParameterClosureThisExtensionProvider::class),
84+
self::getContainer()->getByType(DynamicParameterTypeExtensionProvider::class),
8385
self::getContainer()->getByType(ParameterClosureTypeExtensionProvider::class),
8486
self::createScopeFactory(),
8587
self::getContainer()->getParameter('polluteScopeWithLoopInitialAssignments'),

0 commit comments

Comments
 (0)