Skip to content

Commit 51488c9

Browse files
Add support for env processor
1 parent 173610b commit 51488c9

6 files changed

+118
-2
lines changed

Diff for: src/Type/Symfony/ParameterDynamicReturnTypeExtension.php

+49-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PhpParser\Node\Expr\MethodCall;
66
use PHPStan\Analyser\Scope;
7+
use PHPStan\PhpDoc\TypeStringResolver;
78
use PHPStan\Reflection\MethodReflection;
89
use PHPStan\Reflection\ParametersAcceptorSelector;
910
use PHPStan\ShouldNotHappenException;
@@ -22,8 +23,10 @@
2223
use PHPStan\Type\NullType;
2324
use PHPStan\Type\StringType;
2425
use PHPStan\Type\Type;
26+
use PHPStan\Type\TypeCombinator;
2527
use PHPStan\Type\TypeTraverser;
2628
use PHPStan\Type\UnionType;
29+
use Symfony\Component\DependencyInjection\EnvVarProcessor;
2730
use function in_array;
2831

2932
final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -44,13 +47,24 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy
4447
/** @var \PHPStan\Symfony\ParameterMap */
4548
private $parameterMap;
4649

47-
public function __construct(string $className, ?string $methodGet, ?string $methodHas, Configuration $configuration, ParameterMap $symfonyParameterMap)
50+
/** @var \PHPStan\PhpDoc\TypeStringResolver */
51+
private $typeStringResolver;
52+
53+
public function __construct(
54+
string $className,
55+
?string $methodGet,
56+
?string $methodHas,
57+
Configuration $configuration,
58+
ParameterMap $symfonyParameterMap,
59+
TypeStringResolver $typeStringResolver
60+
)
4861
{
4962
$this->className = $className;
5063
$this->methodGet = $methodGet;
5164
$this->methodHas = $methodHas;
5265
$this->constantHassers = $configuration->hasConstantHassers();
5366
$this->parameterMap = $symfonyParameterMap;
67+
$this->typeStringResolver = $typeStringResolver;
5468
}
5569

5670
public function getClass(): string
@@ -102,13 +116,46 @@ private function getGetTypeFromMethodCall(
102116
if ($parameterKey !== null) {
103117
$parameter = $this->parameterMap->getParameter($parameterKey);
104118
if ($parameter !== null) {
105-
return $this->generalizeType($scope->getTypeFromValue($parameter->getValue()));
119+
return $this->generalizeTypeFromValue($scope, $parameter->getValue());
106120
}
107121
}
108122

109123
return $returnType;
110124
}
111125

126+
/**
127+
* @param Scope $scope
128+
* @param array<mixed>|bool|float|int|string $value
129+
*/
130+
private function generalizeTypeFromValue(Scope $scope, $value): Type
131+
{
132+
if (is_array($value) && $value !== []) {
133+
return $this->generalizeType(
134+
new ArrayType(
135+
TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
136+
return $this->generalizeTypeFromValue($scope, $item);
137+
}, array_keys($value))),
138+
TypeCombinator::union(...array_map(function ($item) use ($scope): Type {
139+
return $this->generalizeTypeFromValue($scope, $item);
140+
}, array_values($value)))
141+
)
142+
);
143+
}
144+
145+
if (
146+
class_exists(EnvVarProcessor::class)
147+
&& is_string($value)
148+
&& preg_match('/%env\((.*)\:.*\)%/U', $value, $matches) === 1
149+
&& strlen($matches[0]) === strlen($value)
150+
) {
151+
$providedTypes = EnvVarProcessor::getProvidedTypes();
152+
153+
return $this->typeStringResolver->resolve($providedTypes[$matches[1]] ?? 'bool|int|float|string|array');
154+
}
155+
156+
return $this->generalizeType($scope->getTypeFromValue($value));
157+
}
158+
112159
private function generalizeType(Type $type): Type
113160
{
114161
return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {

Diff for: tests/Type/Symfony/container.xml

+13
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,28 @@
44
<parameter key="app.string">abcdef</parameter>
55
<parameter key="app.int">123</parameter>
66
<parameter key="app.int_as_string" type="string">123</parameter>
7+
<parameter key="app.int_as_processor">%env(int:APP_INT)%</parameter>
78
<parameter key="app.float">123.45</parameter>
89
<parameter key="app.float_as_string" type="string">123.45</parameter>
10+
<parameter key="app.float_as_processor">%env(float:APP_FLOAT)%</parameter>
911
<parameter key="app.boolean">true</parameter>
1012
<parameter key="app.boolean_as_string" type="string">true</parameter>
13+
<parameter key="app.boolean_as_processor">%env(bool:APP_BOOL)%</parameter>
1114
<parameter key="app.list" type="collection">
1215
<parameter>en</parameter>
1316
<parameter>es</parameter>
1417
<parameter>fr</parameter>
1518
</parameter>
19+
<parameter key="app.list_of_int" type="collection">
20+
<parameter>123</parameter>
21+
<parameter>456</parameter>
22+
<parameter>789</parameter>
23+
</parameter>
24+
<parameter key="app.list_of_int_as_processor" type="collection">
25+
<parameter>%env(int:APP_INT)%</parameter>
26+
<parameter>%env(int:APP_INT)%</parameter>
27+
<parameter>%env(int:APP_INT)%</parameter>
28+
</parameter>
1629
<parameter key="app.list_of_list" type="collection">
1730
<parameter type="collection">
1831
<parameter key="name">the name</parameter>

Diff for: tests/Type/Symfony/data/ExampleAbstractController.php

+11
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
3939
assertType("string", $container->getParameter('app.int_as_string'));
4040
assertType("string", $parameterBag->get('app.int_as_string'));
4141
assertType("string", $this->getParameter('app.int_as_string'));
42+
assertType('int', $container->getParameter('app.int_as_processor'));
43+
assertType('int', $parameterBag->get('app.int_as_processor'));
44+
assertType('int', $this->getParameter('app.int_as_processor'));
4245
assertType('float', $container->getParameter('app.float'));
4346
assertType('float', $parameterBag->get('app.float'));
4447
assertType('float', $this->getParameter('app.float'));
@@ -54,6 +57,12 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
5457
assertType("array<int, string>", $container->getParameter('app.list'));
5558
assertType("array<int, string>", $parameterBag->get('app.list'));
5659
assertType("array<int, string>", $this->getParameter('app.list'));
60+
assertType("array<int, int>", $container->getParameter('app.list_of_int'));
61+
assertType("array<int, int>", $parameterBag->get('app.list_of_int'));
62+
assertType("array<int, int>", $this->getParameter('app.list_of_int'));
63+
assertType("array<int, int>", $container->getParameter('app.list_of_int_as_processor'));
64+
assertType("array<int, int>", $parameterBag->get('app.list_of_int_as_processor'));
65+
assertType("array<int, int>", $this->getParameter('app.list_of_int_as_processor'));
5766
assertType("array<int, array<string, string>>", $container->getParameter('app.list_of_list'));
5867
assertType("array<int, array<string, string>>", $parameterBag->get('app.list_of_list'));
5968
assertType("array<int, array<string, string>>", $this->getParameter('app.list_of_list'));
@@ -77,6 +86,8 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
7786
assertType('true', $parameterBag->has('app.int'));
7887
assertType('true', $container->hasParameter('app.int_as_string'));
7988
assertType('true', $parameterBag->has('app.int_as_string'));
89+
assertType('true', $container->hasParameter('app.int_as_processor'));
90+
assertType('true', $parameterBag->has('app.int_as_processor'));
8091
assertType('true', $container->hasParameter('app.float'));
8192
assertType('true', $parameterBag->has('app.float'));
8293
assertType('true', $container->hasParameter('app.float_as_string'));

Diff for: tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php

+15
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
3838
assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string'));
3939
assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string'));
4040
assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string'));
41+
assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_processor'));
42+
assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_processor'));
43+
assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_processor'));
4144
assertType('array|bool|float|int|string|null', $container->getParameter('app.float'));
4245
assertType('array|bool|float|int|string|null', $parameterBag->get('app.float'));
4346
assertType('array|bool|float|int|string|null', $this->getParameter('app.float'));
4447
assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string'));
4548
assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string'));
4649
assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string'));
50+
assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_processor'));
51+
assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_processor'));
52+
assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_processor'));
4753
assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean'));
4854
assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean'));
4955
assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean'));
5056
assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string'));
5157
assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string'));
5258
assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string'));
59+
assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_processor'));
60+
assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_processor'));
61+
assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_processor'));
5362
assertType('array|bool|float|int|string|null', $container->getParameter('app.list'));
5463
assertType('array|bool|float|int|string|null', $parameterBag->get('app.list'));
5564
assertType('array|bool|float|int|string|null', $this->getParameter('app.list'));
@@ -74,14 +83,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
7483
assertType('bool', $parameterBag->has('app.int'));
7584
assertType('bool', $container->hasParameter('app.int_as_string'));
7685
assertType('bool', $parameterBag->has('app.int_as_string'));
86+
assertType('bool', $container->hasParameter('app.int_as_processor'));
87+
assertType('bool', $parameterBag->has('app.int_as_processor'));
7788
assertType('bool', $container->hasParameter('app.float'));
7889
assertType('bool', $parameterBag->has('app.float'));
7990
assertType('bool', $container->hasParameter('app.float_as_string'));
8091
assertType('bool', $parameterBag->has('app.float_as_string'));
92+
assertType('bool', $container->hasParameter('app.float_as_processor'));
93+
assertType('bool', $parameterBag->has('app.float_as_processor'));
8194
assertType('bool', $container->hasParameter('app.boolean'));
8295
assertType('bool', $parameterBag->has('app.boolean'));
8396
assertType('bool', $container->hasParameter('app.boolean_as_string'));
8497
assertType('bool', $parameterBag->has('app.boolean_as_string'));
98+
assertType('bool', $container->hasParameter('app.boolean_as_processor'));
99+
assertType('bool', $parameterBag->has('app.boolean_as_processor'));
85100
assertType('bool', $container->hasParameter('app.list'));
86101
assertType('bool', $parameterBag->has('app.list'));
87102
assertType('bool', $container->hasParameter('app.list_of_list'));

Diff for: tests/Type/Symfony/data/ExampleController.php

+15
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
3939
assertType("string", $container->getParameter('app.int_as_string'));
4040
assertType("string", $parameterBag->get('app.int_as_string'));
4141
assertType("string", $this->getParameter('app.int_as_string'));
42+
assertType('int', $container->getParameter('app.int_as_processor'));
43+
assertType('int', $parameterBag->get('app.int_as_processor'));
44+
assertType('int', $this->getParameter('app.int_as_processor'));
4245
assertType('float', $container->getParameter('app.float'));
4346
assertType('float', $parameterBag->get('app.float'));
4447
assertType('float', $this->getParameter('app.float'));
4548
assertType("string", $container->getParameter('app.float_as_string'));
4649
assertType("string", $parameterBag->get('app.float_as_string'));
4750
assertType("string", $this->getParameter('app.float_as_string'));
51+
assertType('float', $container->getParameter('app.float_as_processor'));
52+
assertType('float', $parameterBag->get('app.float_as_processor'));
53+
assertType('float', $this->getParameter('app.float_as_processor'));
4854
assertType('bool', $container->getParameter('app.boolean'));
4955
assertType('bool', $parameterBag->get('app.boolean'));
5056
assertType('bool', $this->getParameter('app.boolean'));
5157
assertType("string", $container->getParameter('app.boolean_as_string'));
5258
assertType("string", $parameterBag->get('app.boolean_as_string'));
5359
assertType("string", $this->getParameter('app.boolean_as_string'));
60+
assertType('bool', $container->getParameter('app.boolean_as_processor'));
61+
assertType('bool', $parameterBag->get('app.boolean_as_processor'));
62+
assertType('bool', $this->getParameter('app.boolean_as_processor'));
5463
assertType("array<int, string>", $container->getParameter('app.list'));
5564
assertType("array<int, string>", $parameterBag->get('app.list'));
5665
assertType("array<int, string>", $this->getParameter('app.list'));
@@ -75,14 +84,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
7584
assertType('true', $parameterBag->has('app.int'));
7685
assertType('true', $container->hasParameter('app.int_as_string'));
7786
assertType('true', $parameterBag->has('app.int_as_string'));
87+
assertType('true', $container->hasParameter('app.int_as_processor'));
88+
assertType('true', $parameterBag->has('app.int_as_processor'));
7889
assertType('true', $container->hasParameter('app.float'));
7990
assertType('true', $parameterBag->has('app.float'));
8091
assertType('true', $container->hasParameter('app.float_as_string'));
8192
assertType('true', $parameterBag->has('app.float_as_string'));
93+
assertType('true', $container->hasParameter('app.float_as_processor'));
94+
assertType('true', $parameterBag->has('app.float_as_processor'));
8295
assertType('true', $container->hasParameter('app.boolean'));
8396
assertType('true', $parameterBag->has('app.boolean'));
8497
assertType('true', $container->hasParameter('app.boolean_as_string'));
8598
assertType('true', $parameterBag->has('app.boolean_as_string'));
99+
assertType('true', $container->hasParameter('app.boolean_as_processor'));
100+
assertType('true', $parameterBag->has('app.boolean_as_processor'));
86101
assertType('true', $container->hasParameter('app.list'));
87102
assertType('true', $parameterBag->has('app.list'));
88103
assertType('true', $container->hasParameter('app.list_of_list'));

Diff for: tests/Type/Symfony/data/ExampleControllerWithoutContainer.php

+15
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
3838
assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string'));
3939
assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string'));
4040
assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string'));
41+
assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_processor'));
42+
assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_processor'));
43+
assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_processor'));
4144
assertType('array|bool|float|int|string|null', $container->getParameter('app.float'));
4245
assertType('array|bool|float|int|string|null', $parameterBag->get('app.float'));
4346
assertType('array|bool|float|int|string|null', $this->getParameter('app.float'));
4447
assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string'));
4548
assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string'));
4649
assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string'));
50+
assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_processor'));
51+
assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_processor'));
52+
assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_processor'));
4753
assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean'));
4854
assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean'));
4955
assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean'));
5056
assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string'));
5157
assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string'));
5258
assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string'));
59+
assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_processor'));
60+
assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_processor'));
61+
assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_processor'));
5362
assertType('array|bool|float|int|string|null', $container->getParameter('app.list'));
5463
assertType('array|bool|float|int|string|null', $parameterBag->get('app.list'));
5564
assertType('array|bool|float|int|string|null', $this->getParameter('app.list'));
@@ -74,14 +83,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
7483
assertType('bool', $parameterBag->has('app.int'));
7584
assertType('bool', $container->hasParameter('app.int_as_string'));
7685
assertType('bool', $parameterBag->has('app.int_as_string'));
86+
assertType('bool', $container->hasParameter('app.int_as_processor'));
87+
assertType('bool', $parameterBag->has('app.int_as_processor'));
7788
assertType('bool', $container->hasParameter('app.float'));
7889
assertType('bool', $parameterBag->has('app.float'));
7990
assertType('bool', $container->hasParameter('app.float_as_string'));
8091
assertType('bool', $parameterBag->has('app.float_as_string'));
92+
assertType('bool', $container->hasParameter('app.float_as_processor'));
93+
assertType('bool', $parameterBag->has('app.float_as_processor'));
8194
assertType('bool', $container->hasParameter('app.boolean'));
8295
assertType('bool', $parameterBag->has('app.boolean'));
8396
assertType('bool', $container->hasParameter('app.boolean_as_string'));
8497
assertType('bool', $parameterBag->has('app.boolean_as_string'));
98+
assertType('bool', $container->hasParameter('app.boolean_as_processor'));
99+
assertType('bool', $parameterBag->has('app.boolean_as_processor'));
85100
assertType('bool', $container->hasParameter('app.list'));
86101
assertType('bool', $parameterBag->has('app.list'));
87102
assertType('bool', $container->hasParameter('app.list_of_list'));

0 commit comments

Comments
 (0)