Skip to content

Commit

Permalink
Try to support union of strings as parameter name
Browse files Browse the repository at this point in the history
  • Loading branch information
VincentLanglet authored May 28, 2022
1 parent 2063d60 commit 30f12ae
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 19 deletions.
10 changes: 7 additions & 3 deletions src/Symfony/DefaultParameterMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

use PhpParser\Node\Expr;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Type;
use PHPStan\Type\TypeUtils;
use function count;
use function array_map;

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

public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
public static function getParameterKeysFromNode(Expr $node, Scope $scope): array
{
$strings = TypeUtils::getConstantStrings($scope->getType($node));
return count($strings) === 1 ? $strings[0]->getValue() : null;

return array_map(static function (Type $type) {
return $type->getValue();
}, $strings);
}

}
4 changes: 2 additions & 2 deletions src/Symfony/FakeParameterMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public function getParameter(string $key): ?ParameterDefinition
return null;
}

public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
public static function getParameterKeysFromNode(Expr $node, Scope $scope): array
{
return null;
return [];
}

}
5 changes: 4 additions & 1 deletion src/Symfony/ParameterMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public function getParameters(): array;

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

public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string;
/**
* @return array<string>
*/
public static function getParameterKeysFromNode(Expr $node, Scope $scope): array;

}
46 changes: 33 additions & 13 deletions src/Type/Symfony/ParameterDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private function getGetTypeFromMethodCall(
{
// We don't use the method's return type because this won't work properly with lowest and
// highest versions of Symfony ("mixed" for lowest, "array|bool|float|integer|string|null" for highest).
$returnType = new UnionType([
$defaultReturnType = new UnionType([
new ArrayType(new MixedType(), new MixedType()),
new BooleanType(),
new FloatType(),
Expand All @@ -122,18 +122,25 @@ private function getGetTypeFromMethodCall(
new NullType(),
]);
if (!isset($methodCall->getArgs()[0])) {
return $returnType;
return $defaultReturnType;
}

$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope);
if ($parameterKey !== null) {
$parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope);
if ($parameterKeys === []) {
return $defaultReturnType;
}

$returnTypes = [];
foreach ($parameterKeys as $parameterKey) {
$parameter = $this->parameterMap->getParameter($parameterKey);
if ($parameter !== null) {
return $this->generalizeTypeFromValue($scope, $parameter->getValue());
if ($parameter === null) {
return $defaultReturnType;
}

$returnTypes[] = $this->generalizeTypeFromValue($scope, $parameter->getValue());
}

return $returnType;
return TypeCombinator::union(...$returnTypes);
}

/**
Expand Down Expand Up @@ -211,18 +218,31 @@ private function getHasTypeFromMethodCall(
Scope $scope
): Type
{
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
$defaultReturnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) {
return $returnType;
return $defaultReturnType;
}

$parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope);
if ($parameterKey !== null) {
$parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope);
if ($parameterKeys === []) {
return $defaultReturnType;
}

$has = null;
foreach ($parameterKeys as $parameterKey) {
$parameter = $this->parameterMap->getParameter($parameterKey);
return new ConstantBooleanType($parameter !== null);

if ($has === null) {
$has = $parameter !== null;
} elseif (
($has === true && $parameter === null)
|| ($has === false && $parameter !== null)
) {
return $defaultReturnType;
}
}

return $returnType;
return new ConstantBooleanType($has);
}

}
21 changes: 21 additions & 0 deletions tests/Type/Symfony/data/ExampleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface
assertType('true', $parameterBag->has('app.binary'));
assertType('true', $container->hasParameter('app.constant'));
assertType('true', $parameterBag->has('app.constant'));

$key = rand(0, 1) ? 'app.string' : 'app.int';
assertType("int|string", $container->getParameter($key));
assertType("int|string", $parameterBag->get($key));
assertType("int|string", $this->getParameter($key));
assertType('true', $container->hasParameter($key));
assertType('true', $parameterBag->has($key));

$key = rand(0, 1) ? 'app.string' : 'app.foo';
assertType("array|bool|float|int|string|null", $container->getParameter($key));
assertType("array|bool|float|int|string|null", $parameterBag->get($key));
assertType("array|bool|float|int|string|null", $this->getParameter($key));
assertType('bool', $container->hasParameter($key));
assertType('bool', $parameterBag->has($key));

$key = rand(0, 1) ? 'app.bar' : 'app.foo';
assertType("array|bool|float|int|string|null", $container->getParameter($key));
assertType("array|bool|float|int|string|null", $parameterBag->get($key));
assertType("array|bool|float|int|string|null", $this->getParameter($key));
assertType('false', $container->hasParameter($key));
assertType('false', $parameterBag->has($key));
}

}

0 comments on commit 30f12ae

Please sign in to comment.