From 2bf43f017eec39152bd95e6db6f5f04eae244a58 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sat, 1 May 2021 18:14:31 +0200 Subject: [PATCH 1/7] feat: create Parameter, ParameterMap, ParameterMapFactory, etc... --- extension.neon | 7 + src/Symfony/DefaultParameterMap.php | 37 +++++ src/Symfony/FakeParameterMap.php | 21 +++ src/Symfony/Parameter.php | 35 +++++ src/Symfony/ParameterDefinition.php | 15 ++ src/Symfony/ParameterMap.php | 15 ++ src/Symfony/ParameterMapFactory.php | 10 ++ src/Symfony/XmlParameterMapFactory.php | 104 +++++++++++++ tests/Symfony/DefaultParameterMapTest.php | 145 ++++++++++++++++++ tests/Symfony/container.xml | 32 ++++ .../Symfony/containers/bugfix%2Fcontainer.xml | 32 ++++ 11 files changed, 453 insertions(+) create mode 100644 src/Symfony/DefaultParameterMap.php create mode 100644 src/Symfony/FakeParameterMap.php create mode 100644 src/Symfony/Parameter.php create mode 100644 src/Symfony/ParameterDefinition.php create mode 100644 src/Symfony/ParameterMap.php create mode 100644 src/Symfony/ParameterMapFactory.php create mode 100644 src/Symfony/XmlParameterMapFactory.php create mode 100644 tests/Symfony/DefaultParameterMapTest.php diff --git a/extension.neon b/extension.neon index a8be8071..2d978ea6 100644 --- a/extension.neon +++ b/extension.neon @@ -66,6 +66,13 @@ services: - factory: @symfony.serviceMapFactory::create() + # parameter map + symfony.parameterMapFactory: + class: PHPStan\Symfony\ParameterMapFactory + factory: PHPStan\Symfony\XmlParameterMapFactory(%symfony.container_xml_path%) + - + factory: @symfony.parameterMapFactory::create() + # ControllerTrait::get()/has() return type - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constant_hassers%) diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php new file mode 100644 index 00000000..cc406ac3 --- /dev/null +++ b/src/Symfony/DefaultParameterMap.php @@ -0,0 +1,37 @@ +parameters = $parameters; + } + + /** + * @return \PHPStan\Symfony\ParameterDefinition[] + */ + public function getParameters(): array + { + return $this->parameters; + } + + public function getParameter(string $key): ?ParameterDefinition + { + return $this->parameters[$key] ?? null; + } + +} diff --git a/src/Symfony/FakeParameterMap.php b/src/Symfony/FakeParameterMap.php new file mode 100644 index 00000000..110cc4f4 --- /dev/null +++ b/src/Symfony/FakeParameterMap.php @@ -0,0 +1,21 @@ +|bool|float|int|string */ + private $value; + + /** + * @param array|bool|float|int|string $value + */ + public function __construct( + string $key, + $value + ) + { + $this->key = $key; + $this->value = $value; + } + + public function getKey(): string + { + return $this->key; + } + + public function getValue() + { + return $this->value; + } + +} diff --git a/src/Symfony/ParameterDefinition.php b/src/Symfony/ParameterDefinition.php new file mode 100644 index 00000000..e1aa2eaa --- /dev/null +++ b/src/Symfony/ParameterDefinition.php @@ -0,0 +1,15 @@ +|bool|float|int|string + */ + public function getValue(); + +} diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php new file mode 100644 index 00000000..0a9d9c5b --- /dev/null +++ b/src/Symfony/ParameterMap.php @@ -0,0 +1,15 @@ +containerXml = $containerXml; + } + + public function create(): ParameterMap + { + if ($this->containerXml === null) { + return new FakeParameterMap(); + } + + $fileContents = file_get_contents($this->containerXml); + if ($fileContents === false) { + throw new XmlContainerNotExistsException(sprintf('Container %s does not exist', $this->containerXml)); + } + + $xml = @simplexml_load_string($fileContents); + if ($xml === false) { + throw new XmlContainerNotExistsException(sprintf('Container %s cannot be parsed', $this->containerXml)); + } + + /** @var \PHPStan\Symfony\Parameter[] $parameters */ + $parameters = []; + foreach ($xml->parameters->parameter as $def) { + /** @var \SimpleXMLElement $attrs */ + $attrs = $def->attributes(); + + $parameter = new Parameter( + (string) $attrs->key, + $this->getNodeValue($def) + ); + + $parameters[$parameter->getKey()] = $parameter; + } + + return new DefaultParameterMap($parameters); + } + + /** + * @return array|bool|float|int|string + */ + private function getNodeValue(\SimpleXMLElement $def) + { + /** @var \SimpleXMLElement $attrs */ + $attrs = $def->attributes(); + + $value = null; + switch ((string) $attrs->type) { + case 'collection': + $value = []; + foreach ($def->children() as $child) { + /** @var \SimpleXMLElement $attrs */ + $childAttrs = $child->attributes(); + + if (isset($childAttrs->key)) { + $value[(string) $childAttrs->key] = $this->getNodeValue($child); + } else { + $value[] = $this->getNodeValue($child); + } + } + break; + + case 'string': + $value = (string) $def; + break; + + case 'binary': + if (false === $value = base64_decode((string) $def, true)) { + throw new \InvalidArgumentException(sprintf('Tag "<%s>" with type="binary" is not a valid base64 encoded string.', (string) $attrs->key)); + } + + break; + + default: + $value = (string) $def; + + if (is_numeric($value)) { + if (false !== strpos($value, '.')) { + $value = (float) $value; + } else { + $value = (int) $value; + } + } else if ($value === 'true') { + $value = true; + } else if ($value === 'false') { + $value = false; + } + } + + return $value; + } +} diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php new file mode 100644 index 00000000..560e6fb2 --- /dev/null +++ b/tests/Symfony/DefaultParameterMapTest.php @@ -0,0 +1,145 @@ +create()->getParameter($key)); + } + + public function testGetParameterEscapedPath(): void + { + $factory = new XmlParameterMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml'); + $serviceMap = $factory->create(); + + self::assertNotNull($serviceMap->getParameter('app.string')); + } + + /** + * @return \Iterator + */ + public function getParameterProvider(): Iterator + { + yield [ + 'unknown', + function (?Parameter $parameter): void { + self::assertNull($parameter); + }, + ]; + yield [ + 'app.string', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.string', $parameter->getKey()); + self::assertSame('abcdef', $parameter->getValue()); + }, + ]; + yield [ + 'app.int', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.int', $parameter->getKey()); + self::assertSame(123, $parameter->getValue()); + }, + ]; + yield [ + 'app.int_as_string', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.int_as_string', $parameter->getKey()); + self::assertSame('123', $parameter->getValue()); + }, + ]; + yield [ + 'app.float', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.float', $parameter->getKey()); + self::assertSame(123.45, $parameter->getValue()); + }, + ]; + yield [ + 'app.float_as_string', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.float_as_string', $parameter->getKey()); + self::assertSame('123.45', $parameter->getValue()); + }, + ]; + yield [ + 'app.boolean', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.boolean', $parameter->getKey()); + self::assertSame(true, $parameter->getValue()); + }, + ]; + yield [ + 'app.boolean_as_string', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.boolean_as_string', $parameter->getKey()); + self::assertSame('true', $parameter->getValue()); + }, + ]; + yield [ + 'app.list', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.list', $parameter->getKey()); + self::assertEquals(['en', 'es', 'fr'], $parameter->getValue()); + }, + ]; + yield [ + 'app.list_of_list', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.list_of_list', $parameter->getKey()); + self::assertEquals([ + ['name' => 'the name', 'value' => 'the value'], + ['name' => 'another name', 'value' => 'another value'], + ], $parameter->getValue()); + + }, + ]; + yield [ + 'app.map', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.map', $parameter->getKey()); + self::assertEquals([ + 'a' => 'value of a', + 'b' => 'value of b', + 'c' => 'value of c', + ], $parameter->getValue()); + }, + ]; + yield [ + 'app.binary', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.binary', $parameter->getKey()); + self::assertSame('This is a Bell char ', $parameter->getValue()); + }, + ]; + yield [ + 'app.constant', + function (?Parameter $parameter): void { + self::assertNotNull($parameter); + self::assertSame('app.constant', $parameter->getKey()); + self::assertSame('Y-m-d\TH:i:sP', $parameter->getValue()); + }, + ]; + } + +} diff --git a/tests/Symfony/container.xml b/tests/Symfony/container.xml index 8c8fbd55..5bed7715 100644 --- a/tests/Symfony/container.xml +++ b/tests/Symfony/container.xml @@ -1,5 +1,37 @@ + + abcdef + 123 + 123 + 123.45 + 123.45 + true + true + + en + es + fr + + + + the name + the value + + + another name + another value + + + + value of a + value of b + value of c + + VGhpcyBpcyBhIEJlbGwgY2hhciAH + Y-m-d\TH:i:sP + + diff --git a/tests/Symfony/containers/bugfix%2Fcontainer.xml b/tests/Symfony/containers/bugfix%2Fcontainer.xml index 8c8fbd55..5bed7715 100644 --- a/tests/Symfony/containers/bugfix%2Fcontainer.xml +++ b/tests/Symfony/containers/bugfix%2Fcontainer.xml @@ -1,5 +1,37 @@ + + abcdef + 123 + 123 + 123.45 + 123.45 + true + true + + en + es + fr + + + + the name + the value + + + another name + another value + + + + value of a + value of b + value of c + + VGhpcyBpcyBhIEJlbGwgY2hhciAH + Y-m-d\TH:i:sP + + From b90dcac80d44515443aea96bacdb580855038e9d Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sat, 1 May 2021 20:33:28 +0200 Subject: [PATCH 2/7] feat: implement ParameterDynamicReturnTypeExtension --- README.md | 6 +- extension.neon | 13 ++ src/Symfony/DefaultParameterMap.php | 6 + src/Symfony/FakeParameterMap.php | 7 ++ src/Symfony/ParameterMap.php | 5 + .../ParameterDynamicReturnTypeExtension.php | 113 ++++++++++++++++++ tests/Symfony/DefaultParameterMapTest.php | 2 +- tests/Type/Symfony/ExtensionTest.php | 4 + .../Symfony/ExtensionTestWithoutContainer.php | 11 +- tests/Type/Symfony/container.xml | 32 +++++ .../data/ExampleAbstractController.php | 69 +++++++++++ ...mpleAbstractControllerWithoutContainer.php | 68 +++++++++++ tests/Type/Symfony/data/ExampleController.php | 45 +++++++ .../ExampleControllerWithoutContainer.php | 45 +++++++ 14 files changed, 420 insertions(+), 6 deletions(-) create mode 100644 src/Type/Symfony/ParameterDynamicReturnTypeExtension.php create mode 100644 tests/Type/Symfony/data/ExampleAbstractController.php create mode 100644 tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php diff --git a/README.md b/README.md index 4cb627e4..5464cb9a 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ This extension provides following features: * Provides correct return type for `ContainerInterface::get()` and `::has()` methods. * Provides correct return type for `Controller::get()` and `::has()` methods. +* Provides correct return type for `AbstractController::get()` and `::has()` methods. +* Provides correct return type for `ParameterBagInterface::get()` and `::has()` methods. +* Provides correct return type for `Controller::getParameter()` method. +* Provides correct return type for `AbstractController::getParameter()` method. * 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. @@ -57,7 +61,7 @@ You have to provide a path to `srcDevDebugProjectContainer.xml` or similar XML f parameters: symfony: container_xml_path: var/cache/dev/srcDevDebugProjectContainer.xml - # or with Symfony 4.2+ + # or with Symfony 4.2+ container_xml_path: var/cache/dev/srcApp_KernelDevDebugContainer.xml # or with Symfony 5+ container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml diff --git a/extension.neon b/extension.neon index 2d978ea6..736a9c8b 100644 --- a/extension.neon +++ b/extension.neon @@ -225,3 +225,16 @@ services: - class: PHPStan\Type\Symfony\KernelInterfaceDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # ParameterBagInterface::get()/has() return type + - + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has', %symfony.constant_hassers%) + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # (Abstract)Controller::getParameter() return type + - + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null, %symfony.constant_hassers%) + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + - + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constant_hassers%) + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php index cc406ac3..a58ed9fc 100644 --- a/src/Symfony/DefaultParameterMap.php +++ b/src/Symfony/DefaultParameterMap.php @@ -34,4 +34,10 @@ public function getParameter(string $key): ?ParameterDefinition return $this->parameters[$key] ?? null; } + public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string + { + $strings = TypeUtils::getConstantStrings($scope->getType($node)); + return count($strings) === 1 ? $strings[0]->getValue() : null; + } + } diff --git a/src/Symfony/FakeParameterMap.php b/src/Symfony/FakeParameterMap.php index 110cc4f4..ed2f9406 100644 --- a/src/Symfony/FakeParameterMap.php +++ b/src/Symfony/FakeParameterMap.php @@ -2,6 +2,9 @@ namespace PHPStan\Symfony; +use PhpParser\Node\Expr; +use PHPStan\Analyser\Scope; + final class FakeParameterMap implements ParameterMap { @@ -18,4 +21,8 @@ public function getParameter(string $key): ?ParameterDefinition return null; } + public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string + { + return null; + } } diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php index 0a9d9c5b..fc14fb2c 100644 --- a/src/Symfony/ParameterMap.php +++ b/src/Symfony/ParameterMap.php @@ -2,6 +2,9 @@ namespace PHPStan\Symfony; +use PhpParser\Node\Expr; +use PHPStan\Analyser\Scope; + interface ParameterMap { @@ -12,4 +15,6 @@ public function getParameters(): array; public function getParameter(string $key): ?ParameterDefinition; + public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string; + } diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php new file mode 100644 index 00000000..45683829 --- /dev/null +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -0,0 +1,113 @@ +className = $className; + $this->methodGet = $methodGet; + $this->methodHas = $methodHas; + $this->constantHassers = $constantHassers; + $this->parameterMap = $symfonyParameterMap; + } + + public function getClass(): string + { + return $this->className; + } + + public function isMethodSupported(MethodReflection $methodReflection): bool + { + $methods = array_filter([$this->methodGet, $this->methodHas], function(?string $method): bool { + return $method !== null; + }); + + return in_array($methodReflection->getName(), $methods, true); + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + switch ($methodReflection->getName()) { + case $this->methodGet: + return $this->getGetTypeFromMethodCall($methodReflection, $methodCall, $scope); + case $this->methodHas: + return $this->getHasTypeFromMethodCall($methodReflection, $methodCall, $scope); + } + throw new ShouldNotHappenException(); + } + + private function getGetTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + if (!isset($methodCall->args[0])) { + return $returnType; + } + + $parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->args[0]->value, $scope); + if ($parameterKey !== null) { + $parameter = $this->parameterMap->getParameter($parameterKey); + if ($parameter !== null) { + return $scope->getTypeFromValue($parameter->getValue()); + } + } + + return $returnType; + } + + private function getHasTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + if (!isset($methodCall->args[0]) || !$this->constantHassers) { + return $returnType; + } + + $parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->args[0]->value, $scope); + if ($parameterKey !== null) { + $parameter = $this->parameterMap->getParameter($parameterKey); + return new ConstantBooleanType($parameter !== null); + } + + return $returnType; + } + +} diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php index 560e6fb2..5f5ff834 100644 --- a/tests/Symfony/DefaultParameterMapTest.php +++ b/tests/Symfony/DefaultParameterMapTest.php @@ -81,7 +81,7 @@ function (?Parameter $parameter): void { function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.boolean', $parameter->getKey()); - self::assertSame(true, $parameter->getValue()); + self::assertTrue($parameter->getValue()); }, ]; yield [ diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 2c6a0762..a2de90ec 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -37,6 +37,10 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php'); } + if (class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleAbstractController.php'); + } + yield from $this->gatherAssertTypes(__DIR__ . '/data/serializer.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php'); } diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php index 85b52315..92a3d39e 100644 --- a/tests/Type/Symfony/ExtensionTestWithoutContainer.php +++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php @@ -10,7 +10,13 @@ class ExtensionTestWithoutContainer extends TypeInferenceTestCase /** @return mixed[] */ public function dataFileAsserts(): iterable { - yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleControllerWithoutContainer.php'); + if (class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php'); + } + + if (class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleAbstractController.php'); + } } /** @@ -25,9 +31,6 @@ public function testFileAsserts( ...$args ): void { - if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) { - self::markTestSkipped('Needs class Symfony\Bundle\FrameworkBundle\Controller\Controller'); - } $this->assertFileAsserts($assertType, $file, ...$args); } diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index 978519d4..af82fd19 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -1,5 +1,37 @@ + + abcdef + 123 + 123 + 123.45 + 123.45 + true + true + + en + es + fr + + + + the name + the value + + + another name + another value + + + + value of a + value of b + value of c + + VGhpcyBpcyBhIEJlbGwgY2hhciAH + Y-m-d\TH:i:sP + + diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php new file mode 100644 index 00000000..ee1cbd13 --- /dev/null +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -0,0 +1,69 @@ +get('foo')); + assertType('object', $this->get('bar')); + assertType('object', $this->get(doFoo())); + assertType('object', $this->get()); + + assertType('true', $this->has('foo')); + assertType('false', $this->has('bar')); + assertType('bool', $this->has(doFoo())); + assertType('bool', $this->has()); + } + + public function parameters(ParameterBagInterface $parameterBag): void + { + assertType('mixed', $parameterBag->get('unknown')); + //assertType('mixed', $this->getParameter('unknown')); + assertType("'abcdef'", $parameterBag->get('app.string')); + //assertType("'abcdef'", $this->getParameter('app.string')); + assertType('123', $parameterBag->get('app.int')); + //assertType('123', $this->getParameter('app.int')); + assertType("'123'", $parameterBag->get('app.int_as_string')); + //assertType("'123'", $this->getParameter('app.int_as_string')); + assertType('123.45', $parameterBag->get('app.float')); + //assertType('123.45', $this->getParameter('app.float')); + assertType("'123.45'", $parameterBag->get('app.float_as_string')); + //assertType("'123.45'", $this->getParameter('app.float_as_string')); + assertType('true', $parameterBag->get('app.boolean')); + //assertType('true', $this->getParameter('app.boolean')); + assertType("'true'", $parameterBag->get('app.boolean_as_string')); + //assertType("'true'", $this->getParameter('app.boolean_as_string')); + assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list')); + //assertType("array('en', 'es', 'fr')", $this->getParameter('app.list')); + assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list')); + //assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list')); + assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map')); + //assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map')); + assertType("'This is a Bell char '", $parameterBag->get('app.binary')); + //assertType("'This is a Bell char '", $this->getParameter('app.binary')); + assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant')); + //assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant')); + + assertType('false', $parameterBag->has('unknown')); + assertType('true', $parameterBag->has('app.string')); + assertType('true', $parameterBag->has('app.int')); + assertType('true', $parameterBag->has('app.int_as_string')); + assertType('true', $parameterBag->has('app.float')); + assertType('true', $parameterBag->has('app.float_as_string')); + assertType('true', $parameterBag->has('app.boolean')); + assertType('true', $parameterBag->has('app.boolean_as_string')); + assertType('true', $parameterBag->has('app.list')); + assertType('true', $parameterBag->has('app.list_of_list')); + assertType('true', $parameterBag->has('app.map')); + assertType('true', $parameterBag->has('app.binary')); + assertType('true', $parameterBag->has('app.constant')); + } + +} diff --git a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php new file mode 100644 index 00000000..47c4af4b --- /dev/null +++ b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php @@ -0,0 +1,68 @@ +get('foo')); + assertType('object', $this->get('bar')); + assertType('object', $this->get(doFoo())); + assertType('object', $this->get()); + + assertType('bool', $this->has('foo')); + assertType('bool', $this->has('bar')); + assertType('bool', $this->has(doFoo())); + assertType('bool', $this->has()); + } + + public function parameters(ParameterBagInterface $parameterBag): void + { + assertType('array|bool|float|int|string|null', $parameterBag->get('unknown')); + assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.string')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.string')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.int')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.int')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.float')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.float')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.list')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.list')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.map')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.map')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.binary')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.binary')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.constant')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.constant')); + + assertType('bool', $parameterBag->has('unknown')); + assertType('bool', $parameterBag->has('app.string')); + assertType('bool', $parameterBag->has('app.int')); + assertType('bool', $parameterBag->has('app.int_as_string')); + assertType('bool', $parameterBag->has('app.float')); + assertType('bool', $parameterBag->has('app.float_as_string')); + assertType('bool', $parameterBag->has('app.boolean')); + assertType('bool', $parameterBag->has('app.boolean_as_string')); + assertType('bool', $parameterBag->has('app.list')); + assertType('bool', $parameterBag->has('app.list_of_list')); + assertType('bool', $parameterBag->has('app.map')); + assertType('bool', $parameterBag->has('app.binary')); + assertType('bool', $parameterBag->has('app.constant')); + } + +} diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index ce22ac07..3b83faa5 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -3,6 +3,7 @@ namespace PHPStan\Type\Symfony; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use function PHPStan\Testing\assertType; final class ExampleController extends Controller @@ -21,4 +22,48 @@ public function services(): void assertType('bool', $this->has()); } + public function parameters(ParameterBagInterface $parameterBag): void + { + assertType('mixed', $parameterBag->get('unknown')); + //assertType('mixed', $this->getParameter('unknown')); + assertType("'abcdef'", $parameterBag->get('app.string')); + //assertType("'abcdef'", $this->getParameter('app.string')); + assertType('123', $parameterBag->get('app.int')); + //assertType('123', $this->getParameter('app.int')); + assertType("'123'", $parameterBag->get('app.int_as_string')); + //assertType("'123'", $this->getParameter('app.int_as_string')); + assertType('123.45', $parameterBag->get('app.float')); + //assertType('123.45', $this->getParameter('app.float')); + assertType("'123.45'", $parameterBag->get('app.float_as_string')); + //assertType("'123.45'", $this->getParameter('app.float_as_string')); + assertType('true', $parameterBag->get('app.boolean')); + //assertType('true', $this->getParameter('app.boolean')); + assertType("'true'", $parameterBag->get('app.boolean_as_string')); + //assertType("'true'", $this->getParameter('app.boolean_as_string')); + assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list')); + //assertType("array('en', 'es', 'fr')", $this->getParameter('app.list')); + assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list')); + //assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list')); + assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map')); + //assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map')); + assertType('This is a Bell char ', $parameterBag->get('app.binary')); + //assertType('This is a Bell char ', $this->getParameter('app.binary')); + assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant')); + //assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant')); + + assertType('false', $parameterBag->has('unknown')); + assertType('true', $parameterBag->has('app.string')); + assertType('true', $parameterBag->has('app.int')); + assertType('true', $parameterBag->has('app.int_as_string')); + assertType('true', $parameterBag->has('app.float')); + assertType('true', $parameterBag->has('app.float_as_string')); + assertType('true', $parameterBag->has('app.boolean')); + assertType('true', $parameterBag->has('app.boolean_as_string')); + assertType('true', $parameterBag->has('app.list')); + assertType('true', $parameterBag->has('app.list_of_list')); + assertType('true', $parameterBag->has('app.map')); + assertType('true', $parameterBag->has('app.binary')); + assertType('true', $parameterBag->has('app.constant')); + } + } diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php index f127d976..9bb509fd 100644 --- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php +++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php @@ -3,6 +3,7 @@ namespace PHPStan\Type\Symfony; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; final class ExampleControllerWithoutContainer extends Controller { @@ -20,4 +21,48 @@ public function services(): void assertType('bool', $this->has()); } + public function parameters(ParameterBagInterface $parameterBag): void + { + assertType('array|bool|float|int|string|null', $parameterBag->get('unknown')); + assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.string')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.string')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.int')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.int')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.float')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.float')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.list')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.list')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.map')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.map')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.binary')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.binary')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.constant')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.constant')); + + assertType('bool', $parameterBag->has('unknown')); + assertType('bool', $parameterBag->has('app.string')); + assertType('bool', $parameterBag->has('app.int')); + assertType('bool', $parameterBag->has('app.int_as_string')); + assertType('bool', $parameterBag->has('app.float')); + assertType('bool', $parameterBag->has('app.float_as_string')); + assertType('bool', $parameterBag->has('app.boolean')); + assertType('bool', $parameterBag->has('app.boolean_as_string')); + assertType('bool', $parameterBag->has('app.list')); + assertType('bool', $parameterBag->has('app.list_of_list')); + assertType('bool', $parameterBag->has('app.map')); + assertType('bool', $parameterBag->has('app.binary')); + assertType('bool', $parameterBag->has('app.constant')); + } + } From 0a28ed9e9eacac86092d25ae3129f5c65c8d903a Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sat, 1 May 2021 20:40:14 +0200 Subject: [PATCH 3/7] feat: also supports ContainerInterface::(get|has)Parameter --- README.md | 1 + extension.neon | 5 +++ .../data/ExampleAbstractController.php | 29 +++++++++++++++- ...mpleAbstractControllerWithoutContainer.php | 29 +++++++++++++++- tests/Type/Symfony/data/ExampleController.php | 33 +++++++++++++++++-- .../ExampleControllerWithoutContainer.php | 26 +++++++++++++++ 6 files changed, 118 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5464cb9a..b853b3fa 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ This extension provides following features: * Provides correct return type for `ContainerInterface::get()` and `::has()` methods. * Provides correct return type for `Controller::get()` and `::has()` methods. * Provides correct return type for `AbstractController::get()` and `::has()` methods. +* Provides correct return type for `ContainerInterface::getParameter()` and `::hasParameter()` methods. * Provides correct return type for `ParameterBagInterface::get()` and `::has()` methods. * Provides correct return type for `Controller::getParameter()` method. * Provides correct return type for `AbstractController::getParameter()` method. diff --git a/extension.neon b/extension.neon index 736a9c8b..7873e2b5 100644 --- a/extension.neon +++ b/extension.neon @@ -231,6 +231,11 @@ services: factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has', %symfony.constant_hassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + # ContainerInterface::getParameter()/hasParameter() return type + - + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter', %symfony.constant_hassers%) + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + # (Abstract)Controller::getParameter() return type - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null, %symfony.constant_hassers%) diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index ee1cbd13..06f166ce 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -3,6 +3,7 @@ namespace PHPStan\Type\Symfony; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use function PHPStan\Testing\assertType; @@ -22,47 +23,73 @@ public function services(): void assertType('bool', $this->has()); } - public function parameters(ParameterBagInterface $parameterBag): void + public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void { + assertType('mixed', $container->getParameter('unknown')); assertType('mixed', $parameterBag->get('unknown')); //assertType('mixed', $this->getParameter('unknown')); + assertType("'abcdef'", $container->getParameter('app.string')); assertType("'abcdef'", $parameterBag->get('app.string')); //assertType("'abcdef'", $this->getParameter('app.string')); + assertType('123', $container->getParameter('app.int')); assertType('123', $parameterBag->get('app.int')); //assertType('123', $this->getParameter('app.int')); + assertType("'123'", $container->getParameter('app.int_as_string')); assertType("'123'", $parameterBag->get('app.int_as_string')); //assertType("'123'", $this->getParameter('app.int_as_string')); + assertType('123.45', $container->getParameter('app.float')); assertType('123.45', $parameterBag->get('app.float')); //assertType('123.45', $this->getParameter('app.float')); + assertType("'123.45'", $container->getParameter('app.float_as_string')); assertType("'123.45'", $parameterBag->get('app.float_as_string')); //assertType("'123.45'", $this->getParameter('app.float_as_string')); + assertType('true', $container->getParameter('app.boolean')); assertType('true', $parameterBag->get('app.boolean')); //assertType('true', $this->getParameter('app.boolean')); + assertType("'true'", $container->getParameter('app.boolean_as_string')); assertType("'true'", $parameterBag->get('app.boolean_as_string')); //assertType("'true'", $this->getParameter('app.boolean_as_string')); + assertType("array('en', 'es', 'fr')", $container->getParameter('app.list')); assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list')); //assertType("array('en', 'es', 'fr')", $this->getParameter('app.list')); + assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $container->getParameter('app.list_of_list')); assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list')); //assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list')); + assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $container->getParameter('app.map')); assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map')); //assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map')); + assertType("'This is a Bell char '", $container->getParameter('app.binary')); assertType("'This is a Bell char '", $parameterBag->get('app.binary')); //assertType("'This is a Bell char '", $this->getParameter('app.binary')); + assertType("'Y-m-d\\\\TH:i:sP'", $container->getParameter('app.constant')); assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant')); //assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant')); + assertType('false', $container->hasParameter('unknown')); assertType('false', $parameterBag->has('unknown')); + assertType('true', $container->hasParameter('app.string')); assertType('true', $parameterBag->has('app.string')); + assertType('true', $container->hasParameter('app.int')); assertType('true', $parameterBag->has('app.int')); + assertType('true', $container->hasParameter('app.int_as_string')); assertType('true', $parameterBag->has('app.int_as_string')); + assertType('true', $container->hasParameter('app.float')); assertType('true', $parameterBag->has('app.float')); + assertType('true', $container->hasParameter('app.float_as_string')); assertType('true', $parameterBag->has('app.float_as_string')); + assertType('true', $container->hasParameter('app.boolean')); assertType('true', $parameterBag->has('app.boolean')); + assertType('true', $container->hasParameter('app.boolean_as_string')); assertType('true', $parameterBag->has('app.boolean_as_string')); + assertType('true', $container->hasParameter('app.list')); assertType('true', $parameterBag->has('app.list')); + assertType('true', $container->hasParameter('app.list_of_list')); assertType('true', $parameterBag->has('app.list_of_list')); + assertType('true', $container->hasParameter('app.map')); assertType('true', $parameterBag->has('app.map')); + assertType('true', $container->hasParameter('app.binary')); assertType('true', $parameterBag->has('app.binary')); + assertType('true', $container->hasParameter('app.constant')); assertType('true', $parameterBag->has('app.constant')); } diff --git a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php index 47c4af4b..28773829 100644 --- a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php +++ b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php @@ -3,6 +3,7 @@ namespace PHPStan\Type\Symfony; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; final class ExampleAbstractControllerWithoutContainer extends AbstractController @@ -21,47 +22,73 @@ public function services(): void assertType('bool', $this->has()); } - public function parameters(ParameterBagInterface $parameterBag): void + public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void { + assertType('array|bool|float|int|string|null', $container->getParameter('unknown')); assertType('array|bool|float|int|string|null', $parameterBag->get('unknown')); assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.int')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.int')); assertType('array|bool|float|int|string|null', $this->getParameter('app.int')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.float')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.float')); assertType('array|bool|float|int|string|null', $this->getParameter('app.float')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean')); assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.list')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.list')); assertType('array|bool|float|int|string|null', $this->getParameter('app.list')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.list_of_list')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list')); assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.map')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.map')); assertType('array|bool|float|int|string|null', $this->getParameter('app.map')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.binary')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.binary')); assertType('array|bool|float|int|string|null', $this->getParameter('app.binary')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.constant')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.constant')); assertType('array|bool|float|int|string|null', $this->getParameter('app.constant')); + assertType('bool', $container->hasParameter('unknown')); assertType('bool', $parameterBag->has('unknown')); + assertType('bool', $container->hasParameter('app.string')); assertType('bool', $parameterBag->has('app.string')); + assertType('bool', $container->hasParameter('app.int')); assertType('bool', $parameterBag->has('app.int')); + assertType('bool', $container->hasParameter('app.int_as_string')); assertType('bool', $parameterBag->has('app.int_as_string')); + assertType('bool', $container->hasParameter('app.float')); assertType('bool', $parameterBag->has('app.float')); + assertType('bool', $container->hasParameter('app.float_as_string')); assertType('bool', $parameterBag->has('app.float_as_string')); + assertType('bool', $container->hasParameter('app.boolean')); assertType('bool', $parameterBag->has('app.boolean')); + assertType('bool', $container->hasParameter('app.boolean_as_string')); assertType('bool', $parameterBag->has('app.boolean_as_string')); + assertType('bool', $container->hasParameter('app.list')); assertType('bool', $parameterBag->has('app.list')); + assertType('bool', $container->hasParameter('app.list_of_list')); assertType('bool', $parameterBag->has('app.list_of_list')); + assertType('bool', $container->hasParameter('app.map')); assertType('bool', $parameterBag->has('app.map')); + assertType('bool', $container->hasParameter('app.binary')); assertType('bool', $parameterBag->has('app.binary')); + assertType('bool', $container->hasParameter('app.constant')); assertType('bool', $parameterBag->has('app.constant')); } diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index 3b83faa5..b442df8c 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -3,6 +3,7 @@ namespace PHPStan\Type\Symfony; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use function PHPStan\Testing\assertType; @@ -22,47 +23,73 @@ public function services(): void assertType('bool', $this->has()); } - public function parameters(ParameterBagInterface $parameterBag): void + public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void { + assertType('mixed', $container->getParameter('unknown')); assertType('mixed', $parameterBag->get('unknown')); //assertType('mixed', $this->getParameter('unknown')); + assertType("'abcdef'", $container->getParameter('app.string')); assertType("'abcdef'", $parameterBag->get('app.string')); //assertType("'abcdef'", $this->getParameter('app.string')); + assertType('123', $container->getParameter('app.int')); assertType('123', $parameterBag->get('app.int')); //assertType('123', $this->getParameter('app.int')); + assertType("'123'", $container->getParameter('app.int_as_string')); assertType("'123'", $parameterBag->get('app.int_as_string')); //assertType("'123'", $this->getParameter('app.int_as_string')); + assertType('123.45', $container->getParameter('app.float')); assertType('123.45', $parameterBag->get('app.float')); //assertType('123.45', $this->getParameter('app.float')); + assertType("'123.45'", $container->getParameter('app.float_as_string')); assertType("'123.45'", $parameterBag->get('app.float_as_string')); //assertType("'123.45'", $this->getParameter('app.float_as_string')); + assertType('true', $container->getParameter('app.boolean')); assertType('true', $parameterBag->get('app.boolean')); //assertType('true', $this->getParameter('app.boolean')); + assertType("'true'", $container->getParameter('app.boolean_as_string')); assertType("'true'", $parameterBag->get('app.boolean_as_string')); //assertType("'true'", $this->getParameter('app.boolean_as_string')); + assertType("array('en', 'es', 'fr')", $container->getParameter('app.list')); assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list')); //assertType("array('en', 'es', 'fr')", $this->getParameter('app.list')); + assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $container->getParameter('app.list_of_list')); assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list')); //assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list')); + assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $container->getParameter('app.map')); assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map')); //assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map')); - assertType('This is a Bell char ', $parameterBag->get('app.binary')); - //assertType('This is a Bell char ', $this->getParameter('app.binary')); + assertType("'This is a Bell char '", $container->getParameter('app.binary')); + assertType("'This is a Bell char '", $parameterBag->get('app.binary')); + //assertType("'This is a Bell char '", $this->getParameter('app.binary')); + assertType("'Y-m-d\\\\TH:i:sP'", $container->getParameter('app.constant')); assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant')); //assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant')); + assertType('false', $container->hasParameter('unknown')); assertType('false', $parameterBag->has('unknown')); + assertType('true', $container->hasParameter('app.string')); assertType('true', $parameterBag->has('app.string')); + assertType('true', $container->hasParameter('app.int')); assertType('true', $parameterBag->has('app.int')); + assertType('true', $container->hasParameter('app.int_as_string')); assertType('true', $parameterBag->has('app.int_as_string')); + assertType('true', $container->hasParameter('app.float')); assertType('true', $parameterBag->has('app.float')); + assertType('true', $container->hasParameter('app.float_as_string')); assertType('true', $parameterBag->has('app.float_as_string')); + assertType('true', $container->hasParameter('app.boolean')); assertType('true', $parameterBag->has('app.boolean')); + assertType('true', $container->hasParameter('app.boolean_as_string')); assertType('true', $parameterBag->has('app.boolean_as_string')); + assertType('true', $container->hasParameter('app.list')); assertType('true', $parameterBag->has('app.list')); + assertType('true', $container->hasParameter('app.list_of_list')); assertType('true', $parameterBag->has('app.list_of_list')); + assertType('true', $container->hasParameter('app.map')); assertType('true', $parameterBag->has('app.map')); + assertType('true', $container->hasParameter('app.binary')); assertType('true', $parameterBag->has('app.binary')); + assertType('true', $container->hasParameter('app.constant')); assertType('true', $parameterBag->has('app.constant')); } diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php index 9bb509fd..aee9cd25 100644 --- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php +++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php @@ -23,45 +23,71 @@ public function services(): void public function parameters(ParameterBagInterface $parameterBag): void { + assertType('array|bool|float|int|string|null', $container->getParameter('unknown')); assertType('array|bool|float|int|string|null', $parameterBag->get('unknown')); assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.int')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.int')); assertType('array|bool|float|int|string|null', $this->getParameter('app.int')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.float')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.float')); assertType('array|bool|float|int|string|null', $this->getParameter('app.float')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean')); assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.list')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.list')); assertType('array|bool|float|int|string|null', $this->getParameter('app.list')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.list_of_list')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list')); assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.map')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.map')); assertType('array|bool|float|int|string|null', $this->getParameter('app.map')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.binary')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.binary')); assertType('array|bool|float|int|string|null', $this->getParameter('app.binary')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.constant')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.constant')); assertType('array|bool|float|int|string|null', $this->getParameter('app.constant')); + assertType('bool', $container->hasParameter('unknown')); assertType('bool', $parameterBag->has('unknown')); + assertType('bool', $container->hasParameter('app.string')); assertType('bool', $parameterBag->has('app.string')); + assertType('bool', $container->hasParameter('app.int')); assertType('bool', $parameterBag->has('app.int')); + assertType('bool', $container->hasParameter('app.int_as_string')); assertType('bool', $parameterBag->has('app.int_as_string')); + assertType('bool', $container->hasParameter('app.float')); assertType('bool', $parameterBag->has('app.float')); + assertType('bool', $container->hasParameter('app.float_as_string')); assertType('bool', $parameterBag->has('app.float_as_string')); + assertType('bool', $container->hasParameter('app.boolean')); assertType('bool', $parameterBag->has('app.boolean')); + assertType('bool', $container->hasParameter('app.boolean_as_string')); assertType('bool', $parameterBag->has('app.boolean_as_string')); + assertType('bool', $container->hasParameter('app.list')); assertType('bool', $parameterBag->has('app.list')); + assertType('bool', $container->hasParameter('app.list_of_list')); assertType('bool', $parameterBag->has('app.list_of_list')); + assertType('bool', $container->hasParameter('app.map')); assertType('bool', $parameterBag->has('app.map')); + assertType('bool', $container->hasParameter('app.binary')); assertType('bool', $parameterBag->has('app.binary')); + assertType('bool', $container->hasParameter('app.constant')); assertType('bool', $parameterBag->has('app.constant')); } From f6fdc5f64fddfb4a000299fc81d8e40037e2f34e Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sat, 1 May 2021 21:08:36 +0200 Subject: [PATCH 4/7] chore: styles --- src/Symfony/FakeParameterMap.php | 1 + src/Symfony/Parameter.php | 5 +++++ src/Symfony/XmlParameterMapFactory.php | 16 +++++++++------- .../ParameterDynamicReturnTypeExtension.php | 5 +---- tests/Symfony/DefaultParameterMapTest.php | 1 - .../Symfony/ExtensionTestWithoutContainer.php | 6 ++++-- .../Symfony/data/ExampleAbstractController.php | 4 ++-- tests/Type/Symfony/data/ExampleController.php | 6 +++--- .../data/ExampleControllerWithoutContainer.php | 3 ++- 9 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/Symfony/FakeParameterMap.php b/src/Symfony/FakeParameterMap.php index ed2f9406..7e723411 100644 --- a/src/Symfony/FakeParameterMap.php +++ b/src/Symfony/FakeParameterMap.php @@ -25,4 +25,5 @@ public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?strin { return null; } + } diff --git a/src/Symfony/Parameter.php b/src/Symfony/Parameter.php index 68ed75e5..1df679bc 100644 --- a/src/Symfony/Parameter.php +++ b/src/Symfony/Parameter.php @@ -7,10 +7,12 @@ final class Parameter implements ParameterDefinition /** @var string */ private $key; + /** @var array|bool|float|int|string */ private $value; /** + * @param string $key * @param array|bool|float|int|string $value */ public function __construct( @@ -27,6 +29,9 @@ public function getKey(): string return $this->key; } + /** + * @return array|bool|float|int|string + */ public function getValue() { return $this->value; diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php index aba4e41f..6a7575f4 100644 --- a/src/Symfony/XmlParameterMapFactory.php +++ b/src/Symfony/XmlParameterMapFactory.php @@ -1,4 +1,4 @@ -children() as $child) { - /** @var \SimpleXMLElement $attrs */ + /** @var \SimpleXMLElement $childAttrs */ $childAttrs = $child->attributes(); if (isset($childAttrs->key)) { @@ -77,8 +77,9 @@ private function getNodeValue(\SimpleXMLElement $def) break; case 'binary': - if (false === $value = base64_decode((string) $def, true)) { - throw new \InvalidArgumentException(sprintf('Tag "<%s>" with type="binary" is not a valid base64 encoded string.', (string) $attrs->key)); + $value = base64_decode((string) $def, true); + if ($value === false) { + throw new \InvalidArgumentException(sprintf('Parameter "%s" of binary type is not valid base64 encoded string.', (string) $attrs->key)); } break; @@ -87,18 +88,19 @@ private function getNodeValue(\SimpleXMLElement $def) $value = (string) $def; if (is_numeric($value)) { - if (false !== strpos($value, '.')) { + if (strpos($value, '.') !== false) { $value = (float) $value; } else { $value = (int) $value; } - } else if ($value === 'true') { + } elseif ($value === 'true') { $value = true; - } else if ($value === 'false') { + } elseif ($value === 'false') { $value = false; } } return $value; } + } diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 45683829..f3eb57e3 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -9,9 +9,7 @@ use PHPStan\ShouldNotHappenException; use PHPStan\Symfony\ParameterMap; use PHPStan\Type\Constant\ConstantBooleanType; -use PHPStan\Type\ConstantTypeHelper; use PHPStan\Type\DynamicMethodReturnTypeExtension; -use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use function in_array; @@ -33,7 +31,6 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy /** @var \PHPStan\Symfony\ParameterMap */ private $parameterMap; - public function __construct(string $className, ?string $methodGet, ?string $methodHas, bool $constantHassers, ParameterMap $symfonyParameterMap) { $this->className = $className; @@ -50,7 +47,7 @@ public function getClass(): string public function isMethodSupported(MethodReflection $methodReflection): bool { - $methods = array_filter([$this->methodGet, $this->methodHas], function(?string $method): bool { + $methods = array_filter([$this->methodGet, $this->methodHas], function (?string $method): bool { return $method !== null; }); diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php index 5f5ff834..0b97d078 100644 --- a/tests/Symfony/DefaultParameterMapTest.php +++ b/tests/Symfony/DefaultParameterMapTest.php @@ -109,7 +109,6 @@ function (?Parameter $parameter): void { ['name' => 'the name', 'value' => 'the value'], ['name' => 'another name', 'value' => 'another value'], ], $parameter->getValue()); - }, ]; yield [ diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php index 92a3d39e..f892d919 100644 --- a/tests/Type/Symfony/ExtensionTestWithoutContainer.php +++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php @@ -14,9 +14,11 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php'); } - if (class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) { - yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleAbstractController.php'); + if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) { + return; } + + yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleAbstractController.php'); } /** diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index 06f166ce..a6c0b104 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -25,9 +25,9 @@ public function services(): void public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void { - assertType('mixed', $container->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $container->getParameter('unknown')); assertType('mixed', $parameterBag->get('unknown')); - //assertType('mixed', $this->getParameter('unknown')); + //assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); assertType("'abcdef'", $container->getParameter('app.string')); assertType("'abcdef'", $parameterBag->get('app.string')); //assertType("'abcdef'", $this->getParameter('app.string')); diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index b442df8c..62f75b04 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -25,9 +25,9 @@ public function services(): void public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void { - assertType('mixed', $container->getParameter('unknown')); - assertType('mixed', $parameterBag->get('unknown')); - //assertType('mixed', $this->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $container->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $parameterBag->get('unknown')); + //assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); assertType("'abcdef'", $container->getParameter('app.string')); assertType("'abcdef'", $parameterBag->get('app.string')); //assertType("'abcdef'", $this->getParameter('app.string')); diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php index aee9cd25..2adfd7d5 100644 --- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php +++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php @@ -3,6 +3,7 @@ namespace PHPStan\Type\Symfony; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; final class ExampleControllerWithoutContainer extends Controller @@ -21,7 +22,7 @@ public function services(): void assertType('bool', $this->has()); } - public function parameters(ParameterBagInterface $parameterBag): void + public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void { assertType('array|bool|float|int|string|null', $container->getParameter('unknown')); assertType('array|bool|float|int|string|null', $parameterBag->get('unknown')); From 8a438485cf67ba9be159d1c01c8694aa57f4d0ee Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sat, 1 May 2021 21:18:56 +0200 Subject: [PATCH 5/7] uncomment failing assertTypes --- .../data/ExampleAbstractController.php | 26 +++++++++---------- tests/Type/Symfony/data/ExampleController.php | 26 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index a6c0b104..be8e461f 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -27,43 +27,43 @@ public function parameters(ContainerInterface $container, ParameterBagInterface { assertType('array|bool|float|int|string|null', $container->getParameter('unknown')); assertType('mixed', $parameterBag->get('unknown')); - //assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); assertType("'abcdef'", $container->getParameter('app.string')); assertType("'abcdef'", $parameterBag->get('app.string')); - //assertType("'abcdef'", $this->getParameter('app.string')); + assertType("'abcdef'", $this->getParameter('app.string')); assertType('123', $container->getParameter('app.int')); assertType('123', $parameterBag->get('app.int')); - //assertType('123', $this->getParameter('app.int')); + assertType('123', $this->getParameter('app.int')); assertType("'123'", $container->getParameter('app.int_as_string')); assertType("'123'", $parameterBag->get('app.int_as_string')); - //assertType("'123'", $this->getParameter('app.int_as_string')); + assertType("'123'", $this->getParameter('app.int_as_string')); assertType('123.45', $container->getParameter('app.float')); assertType('123.45', $parameterBag->get('app.float')); - //assertType('123.45', $this->getParameter('app.float')); + assertType('123.45', $this->getParameter('app.float')); assertType("'123.45'", $container->getParameter('app.float_as_string')); assertType("'123.45'", $parameterBag->get('app.float_as_string')); - //assertType("'123.45'", $this->getParameter('app.float_as_string')); + assertType("'123.45'", $this->getParameter('app.float_as_string')); assertType('true', $container->getParameter('app.boolean')); assertType('true', $parameterBag->get('app.boolean')); - //assertType('true', $this->getParameter('app.boolean')); + assertType('true', $this->getParameter('app.boolean')); assertType("'true'", $container->getParameter('app.boolean_as_string')); assertType("'true'", $parameterBag->get('app.boolean_as_string')); - //assertType("'true'", $this->getParameter('app.boolean_as_string')); + assertType("'true'", $this->getParameter('app.boolean_as_string')); assertType("array('en', 'es', 'fr')", $container->getParameter('app.list')); assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list')); - //assertType("array('en', 'es', 'fr')", $this->getParameter('app.list')); + assertType("array('en', 'es', 'fr')", $this->getParameter('app.list')); assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $container->getParameter('app.list_of_list')); assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list')); - //assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list')); + assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list')); assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $container->getParameter('app.map')); assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map')); - //assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map')); + assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map')); assertType("'This is a Bell char '", $container->getParameter('app.binary')); assertType("'This is a Bell char '", $parameterBag->get('app.binary')); - //assertType("'This is a Bell char '", $this->getParameter('app.binary')); + assertType("'This is a Bell char '", $this->getParameter('app.binary')); assertType("'Y-m-d\\\\TH:i:sP'", $container->getParameter('app.constant')); assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant')); - //assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant')); + assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant')); assertType('false', $container->hasParameter('unknown')); assertType('false', $parameterBag->has('unknown')); diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index 62f75b04..db761227 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -27,43 +27,43 @@ public function parameters(ContainerInterface $container, ParameterBagInterface { assertType('array|bool|float|int|string|null', $container->getParameter('unknown')); assertType('array|bool|float|int|string|null', $parameterBag->get('unknown')); - //assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); + assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); assertType("'abcdef'", $container->getParameter('app.string')); assertType("'abcdef'", $parameterBag->get('app.string')); - //assertType("'abcdef'", $this->getParameter('app.string')); + assertType("'abcdef'", $this->getParameter('app.string')); assertType('123', $container->getParameter('app.int')); assertType('123', $parameterBag->get('app.int')); - //assertType('123', $this->getParameter('app.int')); + assertType('123', $this->getParameter('app.int')); assertType("'123'", $container->getParameter('app.int_as_string')); assertType("'123'", $parameterBag->get('app.int_as_string')); - //assertType("'123'", $this->getParameter('app.int_as_string')); + assertType("'123'", $this->getParameter('app.int_as_string')); assertType('123.45', $container->getParameter('app.float')); assertType('123.45', $parameterBag->get('app.float')); - //assertType('123.45', $this->getParameter('app.float')); + assertType('123.45', $this->getParameter('app.float')); assertType("'123.45'", $container->getParameter('app.float_as_string')); assertType("'123.45'", $parameterBag->get('app.float_as_string')); - //assertType("'123.45'", $this->getParameter('app.float_as_string')); + assertType("'123.45'", $this->getParameter('app.float_as_string')); assertType('true', $container->getParameter('app.boolean')); assertType('true', $parameterBag->get('app.boolean')); - //assertType('true', $this->getParameter('app.boolean')); + assertType('true', $this->getParameter('app.boolean')); assertType("'true'", $container->getParameter('app.boolean_as_string')); assertType("'true'", $parameterBag->get('app.boolean_as_string')); - //assertType("'true'", $this->getParameter('app.boolean_as_string')); + assertType("'true'", $this->getParameter('app.boolean_as_string')); assertType("array('en', 'es', 'fr')", $container->getParameter('app.list')); assertType("array('en', 'es', 'fr')", $parameterBag->get('app.list')); - //assertType("array('en', 'es', 'fr')", $this->getParameter('app.list')); + assertType("array('en', 'es', 'fr')", $this->getParameter('app.list')); assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $container->getParameter('app.list_of_list')); assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $parameterBag->get('app.list_of_list')); - //assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list')); + assertType("array(array('name' => 'the name', 'value' => 'the value'), array('name' => 'another name', 'value' => 'another value'))", $this->getParameter('app.list_of_list')); assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $container->getParameter('app.map')); assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $parameterBag->get('app.map')); - //assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map')); + assertType("array('a' => 'value of a', 'b' => 'value of b', 'c' => 'value of c')", $this->getParameter('app.map')); assertType("'This is a Bell char '", $container->getParameter('app.binary')); assertType("'This is a Bell char '", $parameterBag->get('app.binary')); - //assertType("'This is a Bell char '", $this->getParameter('app.binary')); + assertType("'This is a Bell char '", $this->getParameter('app.binary')); assertType("'Y-m-d\\\\TH:i:sP'", $container->getParameter('app.constant')); assertType("'Y-m-d\\\\TH:i:sP'", $parameterBag->get('app.constant')); - //assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant')); + assertType("'Y-m-d\\\\TH:i:sP'", $this->getParameter('app.constant')); assertType('false', $container->hasParameter('unknown')); assertType('false', $parameterBag->has('unknown')); From c2d3f798e84fc9949f60ff28f45f09dc9d6cebe4 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sat, 1 May 2021 22:31:31 +0200 Subject: [PATCH 6/7] fix: prefer create the default type manually instead --- composer.json | 4 ++-- .../ParameterDynamicReturnTypeExtension.php | 19 ++++++++++++++++++- .../data/ExampleAbstractController.php | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 81c70f48..4e01cb6b 100644 --- a/composer.json +++ b/composer.json @@ -26,9 +26,9 @@ "phpstan/phpstan-phpunit": "^0.12.16", "phpstan/phpstan-strict-rules": "^0.12.5", "phpunit/phpunit": "^7.5.20", - "symfony/console": "^4.0 || ^5.0", "symfony/config": "^4.2 || ^5.0", - "symfony/framework-bundle": "^4.0 || ^5.0", + "symfony/console": "^4.0 || ^5.0", + "symfony/framework-bundle": "^4.4 || ^5.0", "symfony/http-foundation": "^4.0 || ^5.0", "symfony/messenger": "^4.2 || ^5.0", "symfony/serializer": "^4.0 || ^5.0" diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index f3eb57e3..adce58fb 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -8,9 +8,17 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; use PHPStan\Symfony\ParameterMap; +use PHPStan\Type\ArrayType; +use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; +use PHPStan\Type\FloatType; +use PHPStan\Type\IntegerType; +use PHPStan\Type\MixedType; +use PHPStan\Type\NullType; +use PHPStan\Type\StringType; use PHPStan\Type\Type; +use PHPStan\Type\UnionType; use function in_array; final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -71,7 +79,16 @@ private function getGetTypeFromMethodCall( Scope $scope ): Type { - $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + // 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([ + new ArrayType(new MixedType(), new MixedType()), + new BooleanType(), + new FloatType(), + new IntegerType(), + new StringType(), + new NullType(), + ]); if (!isset($methodCall->args[0])) { return $returnType; } diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index be8e461f..2bff7543 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -26,7 +26,7 @@ public function services(): void public function parameters(ContainerInterface $container, ParameterBagInterface $parameterBag): void { assertType('array|bool|float|int|string|null', $container->getParameter('unknown')); - assertType('mixed', $parameterBag->get('unknown')); + assertType('array|bool|float|int|string|null', $parameterBag->get('unknown')); assertType('array|bool|float|int|string|null', $this->getParameter('unknown')); assertType("'abcdef'", $container->getParameter('app.string')); assertType("'abcdef'", $parameterBag->get('app.string')); From a586e2495c2622d5494999e85c77ddd5573efaad Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 2 May 2021 10:51:45 +0200 Subject: [PATCH 7/7] Fixed style issue --- .../Symfony/ExtensionTestWithoutContainer.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php index f892d919..997b7e02 100644 --- a/tests/Type/Symfony/ExtensionTestWithoutContainer.php +++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php @@ -8,12 +8,18 @@ class ExtensionTestWithoutContainer extends TypeInferenceTestCase { /** @return mixed[] */ - public function dataFileAsserts(): iterable + public function dataExampleController(): iterable { - if (class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) { - yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php'); + if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) { + return; } + yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php'); + } + + /** @return mixed[] */ + public function dataAbstractController(): iterable + { if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) { return; } @@ -22,7 +28,8 @@ public function dataFileAsserts(): iterable } /** - * @dataProvider dataFileAsserts + * @dataProvider dataExampleController + * @dataProvider dataAbstractController * @param string $assertType * @param string $file * @param mixed ...$args

AltStyle によって変換されたページ (->オリジナル) /