Skip to content

Commit 30f12ae

Browse files
Try to support union of strings as parameter name
1 parent 2063d60 commit 30f12ae

File tree

5 files changed

+67
-19
lines changed

5 files changed

+67
-19
lines changed

src/Symfony/DefaultParameterMap.php

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
use PhpParser\Node\Expr;
66
use PHPStan\Analyser\Scope;
7+
use PHPStan\Type\Type;
78
use PHPStan\Type\TypeUtils;
8-
use function count;
9+
use function array_map;
910

1011
final class DefaultParameterMap implements ParameterMap
1112
{
@@ -34,10 +35,13 @@ public function getParameter(string $key): ?ParameterDefinition
3435
return $this->parameters[$key] ?? null;
3536
}
3637

37-
public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
38+
public static function getParameterKeysFromNode(Expr $node, Scope $scope): array
3839
{
3940
$strings = TypeUtils::getConstantStrings($scope->getType($node));
40-
return count($strings) === 1 ? $strings[0]->getValue() : null;
41+
42+
return array_map(static function (Type $type) {
43+
return $type->getValue();
44+
}, $strings);
4145
}
4246

4347
}

src/Symfony/FakeParameterMap.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ public function getParameter(string $key): ?ParameterDefinition
2121
return null;
2222
}
2323

24-
public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
24+
public static function getParameterKeysFromNode(Expr $node, Scope $scope): array
2525
{
26-
return null;
26+
return [];
2727
}
2828

2929
}

src/Symfony/ParameterMap.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public function getParameters(): array;
1515

1616
public function getParameter(string $key): ?ParameterDefinition;
1717

18-
public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string;
18+
/**
19+
* @return array<string>
20+
*/
21+
public static function getParameterKeysFromNode(Expr $node, Scope $scope): array;
1922

2023
}

src/Type/Symfony/ParameterDynamicReturnTypeExtension.php

+33-13
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ private function getGetTypeFromMethodCall(
113113
{
114114
// We don't use the method's return type because this won't work properly with lowest and
115115
// highest versions of Symfony ("mixed" for lowest, "array|bool|float|integer|string|null" for highest).
116-
$returnType = new UnionType([
116+
$defaultReturnType = new UnionType([
117117
new ArrayType(new MixedType(), new MixedType()),
118118
new BooleanType(),
119119
new FloatType(),
@@ -122,18 +122,25 @@ private function getGetTypeFromMethodCall(
122122
new NullType(),
123123
]);
124124
if (!isset($methodCall->getArgs()[0])) {
125-
return $returnType;
125+
return $defaultReturnType;
126126
}
127127

128-
$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope);
129-
if ($parameterKey !== null) {
128+
$parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope);
129+
if ($parameterKeys === []) {
130+
return $defaultReturnType;
131+
}
132+
133+
$returnTypes = [];
134+
foreach ($parameterKeys as $parameterKey) {
130135
$parameter = $this->parameterMap->getParameter($parameterKey);
131-
if ($parameter !== null) {
132-
return $this->generalizeTypeFromValue($scope, $parameter->getValue());
136+
if ($parameter === null) {
137+
return $defaultReturnType;
133138
}
139+
140+
$returnTypes[] = $this->generalizeTypeFromValue($scope, $parameter->getValue());
134141
}
135142

136-
return $returnType;
143+
return TypeCombinator::union(...$returnTypes);
137144
}
138145

139146
/**
@@ -211,18 +218,31 @@ private function getHasTypeFromMethodCall(
211218
Scope $scope
212219
): Type
213220
{
214-
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
221+
$defaultReturnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
215222
if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) {
216-
return $returnType;
223+
return $defaultReturnType;
217224
}
218225

219-
$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope);
220-
if ($parameterKey !== null) {
226+
$parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope);
227+
if ($parameterKeys === []) {
228+
return $defaultReturnType;
229+
}
230+
231+
$has = null;
232+
foreach ($parameterKeys as $parameterKey) {
221233
$parameter = $this->parameterMap->getParameter($parameterKey);
222-
return new ConstantBooleanType($parameter !== null);
234+
235+
if ($has === null) {
236+
$has = $parameter !== null;
237+
} elseif (
238+
($has === true && $parameter === null)
239+
|| ($has === false && $parameter !== null)
240+
) {
241+
return $defaultReturnType;
242+
}
223243
}
224244

225-
return $returnType;
245+
return new ConstantBooleanType($has);
226246
}
227247

228248
}

tests/Type/Symfony/data/ExampleController.php

+21
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
116116
assertType('true', $parameterBag->has('app.binary'));
117117
assertType('true', $container->hasParameter('app.constant'));
118118
assertType('true', $parameterBag->has('app.constant'));
119+
120+
$key = rand(0, 1) ? 'app.string' : 'app.int';
121+
assertType("int|string", $container->getParameter($key));
122+
assertType("int|string", $parameterBag->get($key));
123+
assertType("int|string", $this->getParameter($key));
124+
assertType('true', $container->hasParameter($key));
125+
assertType('true', $parameterBag->has($key));
126+
127+
$key = rand(0, 1) ? 'app.string' : 'app.foo';
128+
assertType("array|bool|float|int|string|null", $container->getParameter($key));
129+
assertType("array|bool|float|int|string|null", $parameterBag->get($key));
130+
assertType("array|bool|float|int|string|null", $this->getParameter($key));
131+
assertType('bool', $container->hasParameter($key));
132+
assertType('bool', $parameterBag->has($key));
133+
134+
$key = rand(0, 1) ? 'app.bar' : 'app.foo';
135+
assertType("array|bool|float|int|string|null", $container->getParameter($key));
136+
assertType("array|bool|float|int|string|null", $parameterBag->get($key));
137+
assertType("array|bool|float|int|string|null", $this->getParameter($key));
138+
assertType('false', $container->hasParameter($key));
139+
assertType('false', $parameterBag->has($key));
119140
}
120141

121142
}

0 commit comments

Comments
 (0)