diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index 417f51f5..9cb851ff 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -9,6 +9,7 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; use PHPStan\Symfony\ServiceMap; +use PHPStan\TrinaryLogic; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; use function sprintf; @@ -72,7 +73,11 @@ public function processNode(Node $node, Scope $scope): array if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); $serviceIdType = $scope->getType($node->getArgs()[0]->value); - if ($service === null && !$scope->getType(Helper::createMarkerNode($node->var, $serviceIdType, $this->printer))->equals($serviceIdType)) { + if ( + $service === null && + !$this->isContainerCallInServiceSubscriber($isContainerType, $isPsrContainerType, $scope) && + !$scope->getType(Helper::createMarkerNode($node->var, $serviceIdType, $this->printer))->equals($serviceIdType) + ) { return [sprintf('Service "%s" is not registered in the container.', $serviceId)]; } } @@ -80,4 +85,18 @@ public function processNode(Node $node, Scope $scope): array return []; } + private function isContainerCallInServiceSubscriber(TrinaryLogic $isContainerType, TrinaryLogic $isPsrContainerType, Scope $scope): bool + { + $scopeClassReflection = $scope->getClassReflection(); + return $scopeClassReflection !== null && + ( + $scopeClassReflection->implementsInterface('Symfony\Contracts\Service\ServiceSubscriberInterface') || + $scopeClassReflection->implementsInterface('Symfony\Component\DependencyInjection\ServiceSubscriberInterface') + ) && + ( + $isContainerType->yes() || + $isPsrContainerType->yes() + ); + } + } diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index 4bd233e9..b0e22922 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -41,8 +41,8 @@ public function testGetPrivateService(): void public function testGetPrivateServiceInAbstractController(): void { - if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) { - self::markTestSkipped(); + if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) { + self::markTestSkipped('The test needs Symfony\Bundle\FrameworkBundle\Controller\AbstractController class.'); } $this->analyse( @@ -58,7 +58,7 @@ public function testGetPrivateServiceInAbstractController(): void ); } - public function testGetPrivateServiceInLegacyServiceSubscriber(): void + public function testGetPrivateServiceInServiceSubscriber(): void { if (!interface_exists('Symfony\Contracts\Service\ServiceSubscriberInterface')) { self::markTestSkipped('The test needs Symfony\Contracts\Service\ServiceSubscriberInterface class.'); @@ -72,6 +72,34 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void ); } + public function testGetPrivateServiceInServiceSubscriberWithAnotherKey(): void + { + if (!interface_exists('Symfony\Contracts\Service\ServiceSubscriberInterface')) { + self::markTestSkipped('The test needs Symfony\Contracts\Service\ServiceSubscriberInterface class.'); + } + + $this->analyse( + [ + __DIR__ . '/ExampleServiceSubscriberWithLocatorKey.php', + ], + [] + ); + } + + public function testGetPrivateServiceInLegacyServiceSubscriberWithAnotherKey(): void + { + if (!interface_exists('Symfony\Component\DependencyInjection\ServiceSubscriberInterface')) { + self::markTestSkipped('The test needs Symfony\Component\DependencyInjection\ServiceSubscriberInterface class.'); + } + + $this->analyse( + [ + __DIR__ . '/ExampleLegacyServiceSubscriberWithLocatorKey.php', + ], + [] + ); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/Rules/Symfony/ExampleLegacyServiceSubscriberWithLocatorKey.php b/tests/Rules/Symfony/ExampleLegacyServiceSubscriberWithLocatorKey.php new file mode 100644 index 00000000..f4fb5b40 --- /dev/null +++ b/tests/Rules/Symfony/ExampleLegacyServiceSubscriberWithLocatorKey.php @@ -0,0 +1,37 @@ +locator = $locator; + } + + public function privateAliasService(): void + { + $this->locator->get('private_alias'); + } + + public function publicAliasService(): void + { + $this->locator->get('public_alias'); + } + + /** + * @return string[] + */ + public static function getSubscribedServices(): array + { + return []; + } + +} diff --git a/tests/Rules/Symfony/ExampleServiceSubscriberWithLocatorKey.php b/tests/Rules/Symfony/ExampleServiceSubscriberWithLocatorKey.php new file mode 100644 index 00000000..2fc4517e --- /dev/null +++ b/tests/Rules/Symfony/ExampleServiceSubscriberWithLocatorKey.php @@ -0,0 +1,37 @@ +locator = $locator; + } + + public function privateAliasService(): void + { + $this->locator->get('private_alias'); + } + + public function publicAliasService(): void + { + $this->locator->get('public_alias'); + } + + /** + * @return string[] + */ + public static function getSubscribedServices(): array + { + return []; + } + +} diff --git a/tests/Rules/Symfony/container.xml b/tests/Rules/Symfony/container.xml index f3261e0a..63aa2d87 100644 --- a/tests/Rules/Symfony/container.xml +++ b/tests/Rules/Symfony/container.xml @@ -8,5 +8,11 @@ + + + + + +