From a0da7dc17ffe9ea99ddefc5ba0b352d58e55f645 Mon Sep 17 00:00:00 2001 From: Grzegorz Korba Date: 2022年2月10日 00:13:08 +0100 Subject: [PATCH 1/5] Support for parameterised service class Fixes #226 --- .../ServiceDynamicReturnTypeExtension.php | 33 +++++++++++++++++-- tests/Type/Symfony/container.xml | 2 ++ .../data/ExampleAbstractController.php | 1 + tests/Type/Symfony/data/ExampleController.php | 1 + 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index 04608c17..b424e545 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -8,12 +8,17 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; use PHPStan\Symfony\Configuration; +use PHPStan\Symfony\ParameterMap; +use PHPStan\Symfony\ServiceDefinition; use PHPStan\Symfony\ServiceMap; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use function in_array; +use function is_scalar; +use function strpos; +use function trim; final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -27,11 +32,20 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType /** @var ServiceMap */ private $serviceMap; - public function __construct(string $className, Configuration $configuration, ServiceMap $symfonyServiceMap) + /** @var ParameterMap */ + private $parameterMap; + + public function __construct( + string $className, + Configuration $configuration, + ServiceMap $symfonyServiceMap, + ParameterMap $symfonyParameterMap + ) { $this->className = $className; $this->constantHassers = $configuration->hasConstantHassers(); $this->serviceMap = $symfonyServiceMap; + $this->parameterMap = $symfonyParameterMap; } public function getClass(): string @@ -70,7 +84,7 @@ private function getGetTypeFromMethodCall( if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); if ($service !== null && (!$service->isSynthetic() || $service->getClass() !== null)) { - return new ObjectType($service->getClass() ?? $serviceId); + return new ObjectType($this->determineServiceClass($service) ?? $serviceId); } } @@ -97,4 +111,19 @@ private function getHasTypeFromMethodCall( return $returnType; } + private function determineServiceClass(ServiceDefinition $service): ?string + { + $class = $service->getClass(); + + if ($class !== null && strpos($class, '%') === 0) { + $param = $this->parameterMap->getParameter(trim($class, '%')); + + if ($param !== null && is_scalar($param->getValue())) { + return (string) $param->getValue(); + } + } + + return $class; + } + } diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index e6f8f03b..ef07f554 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -1,6 +1,7 @@ + Foo abcdef 123 123 @@ -51,6 +52,7 @@ + diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index 6dc6ef7e..f2f51573 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -13,6 +13,7 @@ final class ExampleAbstractController extends AbstractController public function services(): void { assertType('Foo', $this->get('foo')); + assertType('Foo', $this->get('parameterised_foo')); assertType('Synthetic', $this->get('synthetic')); assertType('object', $this->get('bar')); assertType('object', $this->get(doFoo())); diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index 8cd0c93a..21f06c48 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -13,6 +13,7 @@ final class ExampleController extends Controller public function services(): void { assertType('Foo', $this->get('foo')); + assertType('Foo', $this->get('parameterised_foo')); assertType('Synthetic', $this->get('synthetic')); assertType('object', $this->get('bar')); assertType('object', $this->get(doFoo())); From 6cfde57be4325c3d9863e5a14feb2ecefe7b8480 Mon Sep 17 00:00:00 2001 From: Grzegorz Korba Date: 2022年2月10日 23:38:34 +0100 Subject: [PATCH 2/5] Use Symfony's parameter bag for resolving placeholders --- .../ServiceDynamicReturnTypeExtension.php | 25 +++++++++---------- tests/Type/Symfony/container.xml | 1 + .../data/ExampleAbstractController.php | 1 + tests/Type/Symfony/data/ExampleController.php | 1 + 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index b424e545..cd3d2a46 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -15,10 +15,8 @@ use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use function in_array; -use function is_scalar; -use function strpos; -use function trim; final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -32,8 +30,8 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType /** @var ServiceMap */ private $serviceMap; - /** @var ParameterMap */ - private $parameterMap; + /** @var ParameterBag */ + private $parameterBag; public function __construct( string $className, @@ -45,7 +43,7 @@ public function __construct( $this->className = $className; $this->constantHassers = $configuration->hasConstantHassers(); $this->serviceMap = $symfonyServiceMap; - $this->parameterMap = $symfonyParameterMap; + $this->parameterBag = $this->createParameterBag($symfonyParameterMap); } public function getClass(): string @@ -113,17 +111,18 @@ private function getHasTypeFromMethodCall( private function determineServiceClass(ServiceDefinition $service): ?string { - $class = $service->getClass(); + return $this->parameterBag->resolveValue($service->getClass()); + } - if ($class !== null && strpos($class, '%') === 0) { - $param = $this->parameterMap->getParameter(trim($class, '%')); + private function createParameterBag(ParameterMap $symfonyParameterMap): ParameterBag + { + $parameters = []; - if ($param !== null && is_scalar($param->getValue())) { - return (string) $param->getValue(); - } + foreach ($symfonyParameterMap->getParameters() as $parameterDefinition) { + $parameters[$parameterDefinition->getKey()] = $parameterDefinition->getValue(); } - return $class; + return new ParameterBag($parameters); } } diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index ef07f554..992289f2 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -53,6 +53,7 @@ + diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index f2f51573..6fdb281d 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -14,6 +14,7 @@ public function services(): void { assertType('Foo', $this->get('foo')); assertType('Foo', $this->get('parameterised_foo')); + assertType('Foo\Bar', $this->get('parameterised_bar')); assertType('Synthetic', $this->get('synthetic')); assertType('object', $this->get('bar')); assertType('object', $this->get(doFoo())); diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index 21f06c48..b09cc25d 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -14,6 +14,7 @@ public function services(): void { assertType('Foo', $this->get('foo')); assertType('Foo', $this->get('parameterised_foo')); + assertType('Foo\Bar', $this->get('parameterised_bar')); assertType('Synthetic', $this->get('synthetic')); assertType('object', $this->get('bar')); assertType('object', $this->get(doFoo())); From d10d1563e594468c5579674633e87b1f8a60e4b6 Mon Sep 17 00:00:00 2001 From: Grzegorz Korba Date: 2022年2月11日 00:05:32 +0100 Subject: [PATCH 3/5] Fix dependencies - Explicit requirement for `symfony/dependency-injection` - Do not use `psr/log` 1.1.2 (see: https://github.com/symfony/symfony/issues/44371#issuecomment-982885380) --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 08f5b685..b447069f 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ "phpstan/phpstan": "^1.4" }, "conflict": { + "psr/container": "1.1.2", "symfony/framework-bundle": "<3.0" }, "require-dev": { @@ -28,6 +29,7 @@ "phpunit/phpunit": "^9.5", "symfony/config": "^4.2 || ^5.0", "symfony/console": "^4.0 || ^5.0", + "symfony/dependency-injection": "^4.0 || ^5.0", "symfony/form": "^4.0 || ^5.0", "symfony/framework-bundle": "^4.4 || ^5.0", "symfony/http-foundation": "^5.1", From 1610b6469c63f5ca4e4b6af6ad0e3c73aff88c2a Mon Sep 17 00:00:00 2001 From: Grzegorz Korba Date: 2022年2月11日 17:14:30 +0100 Subject: [PATCH 4/5] Use `require-dev` instead of `conflicts` --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b447069f..1c1f0825 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,6 @@ "phpstan/phpstan": "^1.4" }, "conflict": { - "psr/container": "1.1.2", "symfony/framework-bundle": "<3.0" }, "require-dev": { @@ -27,6 +26,7 @@ "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", + "psr/container": "^1.1 <1.1.2", "symfony/config": "^4.2 || ^5.0", "symfony/console": "^4.0 || ^5.0", "symfony/dependency-injection": "^4.0 || ^5.0", From 46e535d81d8628add34fb8461ad46a2f5e397f42 Mon Sep 17 00:00:00 2001 From: Grzegorz Korba Date: 2022年2月14日 08:58:54 +0100 Subject: [PATCH 5/5] Restrict `psr/container` constraint Versions 1.1.0 and 1.1.2 does not work properly with Symfony DI (`Psr\Container\ContainerExceptionInterface` extends `\Throwable` there and it causes errors for DI's exception which also extends it). --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1c1f0825..5b5438ff 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", - "psr/container": "^1.1 <1.1.2", + "psr/container": "1.0 || 1.1.1", "symfony/config": "^4.2 || ^5.0", "symfony/console": "^4.0 || ^5.0", "symfony/dependency-injection": "^4.0 || ^5.0",

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