From 8a062af1cb7e9609d7b2a85d9b16060b1a35cee7 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Tue, 13 Apr 2021 11:49:23 +0200 Subject: [PATCH] Add support for InputBag::all (Symfony 5+) --- README.md | 2 ++ phpstan.neon | 2 +- .../InputBagDynamicReturnTypeExtension.php | 34 ++++++++++++++++++- ...InputBagDynamicReturnTypeExtensionTest.php | 10 +++--- .../{input_bag_get.php => input_bag.php} | 4 ++- 5 files changed, 45 insertions(+), 7 deletions(-) rename tests/Type/Symfony/{input_bag_get.php => input_bag.php} (75%) diff --git a/README.md b/README.md index 02bfb1e2..0081f2ad 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ This extension provides following features: * Provides correct return type for `Request::getContent()` method based on the `$asResource` parameter. * Provides correct return type for `HeaderBag::get()` method based on the `$first` parameter. * Provides correct return type for `Envelope::all()` method based on the `$stampFqcn` parameter. +* Provides correct return type for `InputBag::get()` method based on the `$default` parameter. +* Provides correct return type for `InputBag::all()` method based on the `$key` parameter. * Provides correct return types for `TreeBuilder` and `NodeDefinition` objects. * Notifies you when you try to get an unregistered service from the container. * Notifies you when you try to get a private service from the container. diff --git a/phpstan.neon b/phpstan.neon index f1ef1861..bc33def1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -13,7 +13,7 @@ parameters: - tests/*/console_application_loader.php - tests/*/envelope_all.php - tests/*/header_bag_get.php - - tests/*/input_bag_get.php + - tests/*/input_bag.php - tests/*/kernel_interface.php - tests/*/request_get_content.php - tests/*/request_get_session.php diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php index 747a2312..51e5ff19 100644 --- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php @@ -6,10 +6,14 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\ShouldNotHappenException; +use PHPStan\Type\ArrayType; use PHPStan\Type\DynamicMethodReturnTypeExtension; +use PHPStan\Type\MixedType; use PHPStan\Type\NullType; use PHPStan\Type\StringType; use PHPStan\Type\Type; +use PHPStan\Type\UnionType; final class InputBagDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -21,7 +25,7 @@ public function getClass(): string public function isMethodSupported(MethodReflection $methodReflection): bool { - return $methodReflection->getName() === 'get'; + return in_array($methodReflection->getName(), ['get', 'all'], true); } public function getTypeFromMethodCall( @@ -29,6 +33,23 @@ public function getTypeFromMethodCall( MethodCall $methodCall, Scope $scope ): Type + { + if ($methodReflection->getName() === 'get') { + return $this->getGetTypeFromMethodCall($methodReflection, $methodCall, $scope); + } + + if ($methodReflection->getName() === 'all') { + return $this->getAllTypeFromMethodCall($methodCall); + } + + throw new ShouldNotHappenException(); + } + + private function getGetTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type { if (isset($methodCall->args[1])) { $argType = $scope->getType($methodCall->args[1]->value); @@ -43,4 +64,15 @@ public function getTypeFromMethodCall( return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); } + private function getAllTypeFromMethodCall( + MethodCall $methodCall + ): Type + { + if (isset($methodCall->args[0])) { + return new ArrayType(new MixedType(), new StringType()); + } + + return new ArrayType(new StringType(), new UnionType([new StringType(), new ArrayType(new MixedType(), new StringType())])); + } + } diff --git a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php index b80b02ad..f1a2a520 100644 --- a/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php +++ b/tests/Type/Symfony/InputBagDynamicReturnTypeExtensionTest.php @@ -8,16 +8,16 @@ final class InputBagDynamicReturnTypeExtensionTest extends ExtensionTestCase { /** - * @dataProvider getProvider + * @dataProvider inputBagProvider */ - public function testGet(string $expression, string $type): void + public function testInputBag(string $expression, string $type): void { if (!class_exists('Symfony\\Component\\HttpFoundation\\InputBag')) { self::markTestSkipped('The test needs Symfony\Component\HttpFoundation\InputBag class.'); } $this->processFile( - __DIR__ . '/input_bag_get.php', + __DIR__ . '/input_bag.php', $expression, $type, [new InputBagDynamicReturnTypeExtension()] @@ -27,12 +27,14 @@ public function testGet(string $expression, string $type): void /** * @return \Iterator */ - public function getProvider(): Iterator + public function inputBagProvider(): Iterator { yield ['$test1', 'string|null']; yield ['$test2', 'string|null']; yield ['$test3', 'string']; yield ['$test4', 'string']; + yield ['$test5', 'array|string>']; + yield ['$test6', 'array']; } } diff --git a/tests/Type/Symfony/input_bag_get.php b/tests/Type/Symfony/input_bag.php similarity index 75% rename from tests/Type/Symfony/input_bag_get.php rename to tests/Type/Symfony/input_bag.php index 756ff2af..7a36d61f 100644 --- a/tests/Type/Symfony/input_bag_get.php +++ b/tests/Type/Symfony/input_bag.php @@ -1,10 +1,12 @@ 'bar']); +$bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar', 'bar' => ['x']]); $test1 = $bag->get('foo'); $test2 = $bag->get('foo', null); $test3 = $bag->get('foo', ''); $test4 = $bag->get('foo', 'baz'); +$test5 = $bag->all(); +$test6 = $bag->all('bar'); die;