From b49fdc74d64106cc05579825d5a2e28052b1692b Mon Sep 17 00:00:00 2001 From: RafaelKr <14234815+RafaelKr@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:50:40 +0100 Subject: [PATCH 1/5] Basic support for simple Symfony #[AutowireLocator] attribute https://symfony.com/blog/new-in-symfony-6-4-autowirelocator-and-autowireiterator-attributes --- .../ContainerInterfacePrivateServiceRule.php | 116 ++++++++++++++++-- 1 file changed, 107 insertions(+), 9 deletions(-) diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index 996d3b77..fdfa7721 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -5,12 +5,19 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; +use PHPStan\BetterReflection\Reflection\Adapter\FakeReflectionAttribute; +use PHPStan\BetterReflection\Reflection\Adapter\ReflectionAttribute; +use PHPStan\Reflection\ClassReflection; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Symfony\ServiceDefinition; use PHPStan\Symfony\ServiceMap; use PHPStan\TrinaryLogic; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use Symfony\Component\DependencyInjection\Attribute\AutowireLocator; +use function class_exists; +use function get_class; use function sprintf; /** @@ -66,15 +73,29 @@ public function processNode(Node $node, Scope $scope): array } $serviceId = $this->serviceMap::getServiceIdFromNode($node->getArgs()[0]->value, $scope); - if ($serviceId !== null) { - $service = $this->serviceMap->getService($serviceId); - if ($service !== null && !$service->isPublic()) { - return [ - RuleErrorBuilder::message(sprintf('Service "%s" is private.', $serviceId)) - ->identifier('symfonyContainer.privateService') - ->build(), - ]; - } + if ($serviceId === null) { + return []; + } + + $service = $this->serviceMap->getService($serviceId); + if (!$service instanceof ServiceDefinition) { + return []; + } + + $isContainerInterfaceType = $isContainerType->yes() || $isPsrContainerType->yes(); + if ( + $isContainerInterfaceType && + $this->isAutowireLocator($node, $scope, $service) + ) { + return []; + } + + if (!$service->isPublic()) { + return [ + RuleErrorBuilder::message(sprintf('Service "%s" is private.', $serviceId)) + ->identifier('symfonyContainer.privateService') + ->build(), + ]; } return []; @@ -92,4 +113,81 @@ private function isServiceSubscriber(Type $containerType, Scope $scope): Trinary return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)); } + private function isAutowireLocator(Node $node, Scope $scope, ServiceDefinition $service): bool + { + if (!class_exists('Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator')) { + return false; + } + + if ( + !$node instanceof MethodCall + ) { + return false; + } + + $nodeParentProperty = $node->var; + + if (!$nodeParentProperty instanceof Node\Expr\PropertyFetch) { + return false; + } + + $nodeParentPropertyName = $nodeParentProperty->name; + + if (!$nodeParentPropertyName instanceof Node\Identifier) { + return false; + } + + $containerInterfacePropertyName = $nodeParentPropertyName->name; + $scopeClassReflection = $scope->getClassReflection(); + + if (!$scopeClassReflection instanceof ClassReflection) { + return false; + } + + $containerInterfacePropertyReflection = $scopeClassReflection + ->getNativeProperty($containerInterfacePropertyName); + $classPropertyReflection = $containerInterfacePropertyReflection->getNativeReflection(); + $autowireLocatorAttributes = $classPropertyReflection->getAttributes(AutowireLocator::class); + + return $this->isAutowireLocatorService($autowireLocatorAttributes, $service); + } + + /** + * @param array $autowireLocatorAttributes + */ + private function isAutowireLocatorService(array $autowireLocatorAttributes, ServiceDefinition $service): bool + { + foreach ($autowireLocatorAttributes as $autowireLocatorAttribute) { + foreach ($autowireLocatorAttribute->getArgumentsExpressions() as $autowireLocatorServices) { + if (!$autowireLocatorServices instanceof Node\Expr\Array_) { + continue; + } + + foreach ($autowireLocatorServices->items as $autowireLocatorServiceNode) { + /** @var Node\Expr\ArrayItem $autowireLocatorServiceNode */ + $autowireLocatorServiceExpr = $autowireLocatorServiceNode->value; + + switch (get_class($autowireLocatorServiceExpr)) { + case Node\Scalar\String_::class: + $autowireLocatorServiceClass = $autowireLocatorServiceExpr->value; + break; + case Node\Expr\ClassConstFetch::class: + $autowireLocatorServiceClass = $autowireLocatorServiceExpr->class instanceof Node\Name + ? $autowireLocatorServiceExpr->class->toString() + : null; + break; + default: + $autowireLocatorServiceClass = null; + } + + if ($service->getId() === $autowireLocatorServiceClass) { + return true; + } + } + } + } + + return false; + } + } From 534b45d6573350f6bf8886d25ec028f7d83455d6 Mon Sep 17 00:00:00 2001 From: RafaelKr <14234815+RafaelKr@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:19:45 +0100 Subject: [PATCH 2/5] Support aliases on #[AutowireLocator] attribute in ContainerInterfacePrivateServiceRule We now create an AutowireLocator instance and check if the service exists there. This also removes a lot of nested foreach loops and thus decreases the complexity of the isAutowireLocatorService method a lot. https://symfony.com/blog/new-in-symfony-6-4-autowirelocator-and-autowireiterator-attributes --- .../ContainerInterfacePrivateServiceRule.php | 50 +++++-------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index fdfa7721..a92c3cde 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -10,14 +10,12 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; -use PHPStan\Symfony\ServiceDefinition; use PHPStan\Symfony\ServiceMap; use PHPStan\TrinaryLogic; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use Symfony\Component\DependencyInjection\Attribute\AutowireLocator; use function class_exists; -use function get_class; use function sprintf; /** @@ -77,20 +75,16 @@ public function processNode(Node $node, Scope $scope): array return []; } - $service = $this->serviceMap->getService($serviceId); - if (!$service instanceof ServiceDefinition) { - return []; - } - $isContainerInterfaceType = $isContainerType->yes() || $isPsrContainerType->yes(); if ( $isContainerInterfaceType && - $this->isAutowireLocator($node, $scope, $service) + $this->isAutowireLocator($node, $scope, $serviceId) ) { return []; } - if (!$service->isPublic()) { + $service = $this->serviceMap->getService($serviceId); + if ($service !== null && !$service->isPublic()) { return [ RuleErrorBuilder::message(sprintf('Service "%s" is private.', $serviceId)) ->identifier('symfonyContainer.privateService') @@ -113,7 +107,7 @@ private function isServiceSubscriber(Type $containerType, Scope $scope): Trinary return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)); } - private function isAutowireLocator(Node $node, Scope $scope, ServiceDefinition $service): bool + private function isAutowireLocator(Node $node, Scope $scope, string $serviceId): bool { if (!class_exists('Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator')) { return false; @@ -149,41 +143,21 @@ private function isAutowireLocator(Node $node, Scope $scope, ServiceDefinition $ $classPropertyReflection = $containerInterfacePropertyReflection->getNativeReflection(); $autowireLocatorAttributes = $classPropertyReflection->getAttributes(AutowireLocator::class); - return $this->isAutowireLocatorService($autowireLocatorAttributes, $service); + return $this->isAutowireLocatorService($autowireLocatorAttributes, $serviceId); } /** * @param array $autowireLocatorAttributes */ - private function isAutowireLocatorService(array $autowireLocatorAttributes, ServiceDefinition $service): bool + private function isAutowireLocatorService(array $autowireLocatorAttributes, string $serviceId): bool { foreach ($autowireLocatorAttributes as $autowireLocatorAttribute) { - foreach ($autowireLocatorAttribute->getArgumentsExpressions() as $autowireLocatorServices) { - if (!$autowireLocatorServices instanceof Node\Expr\Array_) { - continue; - } - - foreach ($autowireLocatorServices->items as $autowireLocatorServiceNode) { - /** @var Node\Expr\ArrayItem $autowireLocatorServiceNode */ - $autowireLocatorServiceExpr = $autowireLocatorServiceNode->value; - - switch (get_class($autowireLocatorServiceExpr)) { - case Node\Scalar\String_::class: - $autowireLocatorServiceClass = $autowireLocatorServiceExpr->value; - break; - case Node\Expr\ClassConstFetch::class: - $autowireLocatorServiceClass = $autowireLocatorServiceExpr->class instanceof Node\Name - ? $autowireLocatorServiceExpr->class->toString() - : null; - break; - default: - $autowireLocatorServiceClass = null; - } - - if ($service->getId() === $autowireLocatorServiceClass) { - return true; - } - } + /** @var AutowireLocator $autowireLocatorInstance */ + $autowireLocator = $autowireLocatorAttribute->newInstance(); + $autowireLocatorServices = $autowireLocator->value->getValues(); + + if (array_key_exists($serviceId, $autowireLocatorServices)) { + return true; } } From f262e69381bb05869524b1f53c8d0b40d96ddba0 Mon Sep 17 00:00:00 2001 From: RafaelKr <14234815+RafaelKr@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:54:12 +0100 Subject: [PATCH 3/5] Create ServiceMap from AutowireLocator attribute instance This allows us to share the logic for other rules --- .../ContainerInterfacePrivateServiceRule.php | 65 ++--------- .../AutowireLocatorServiceMapFactory.php | 110 ++++++++++++++++++ 2 files changed, 119 insertions(+), 56 deletions(-) create mode 100644 src/Symfony/AutowireLocatorServiceMapFactory.php diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index a92c3cde..e0ffc1cb 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -5,17 +5,15 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; -use PHPStan\BetterReflection\Reflection\Adapter\FakeReflectionAttribute; -use PHPStan\BetterReflection\Reflection\Adapter\ReflectionAttribute; -use PHPStan\Reflection\ClassReflection; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Symfony\AutowireLocatorServiceMapFactory; +use PHPStan\Symfony\DefaultServiceMap; use PHPStan\Symfony\ServiceMap; use PHPStan\TrinaryLogic; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use Symfony\Component\DependencyInjection\Attribute\AutowireLocator; -use function class_exists; +use function is_null; use function sprintf; /** @@ -78,7 +76,7 @@ public function processNode(Node $node, Scope $scope): array $isContainerInterfaceType = $isContainerType->yes() || $isPsrContainerType->yes(); if ( $isContainerInterfaceType && - $this->isAutowireLocator($node, $scope, $serviceId) + $this->isAutowireLocatorService($node, $scope, $serviceId) ) { return []; } @@ -107,61 +105,16 @@ private function isServiceSubscriber(Type $containerType, Scope $scope): Trinary return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)); } - private function isAutowireLocator(Node $node, Scope $scope, string $serviceId): bool + private function isAutowireLocatorService(Node $node, Scope $scope, string $serviceId): bool { - if (!class_exists('Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator')) { - return false; - } - - if ( - !$node instanceof MethodCall - ) { - return false; - } - - $nodeParentProperty = $node->var; - - if (!$nodeParentProperty instanceof Node\Expr\PropertyFetch) { - return false; - } + $autowireLocatorServiceMapFactory = new AutowireLocatorServiceMapFactory($node, $scope); + $autowireLocatorServiceMap = $autowireLocatorServiceMapFactory->create(); - $nodeParentPropertyName = $nodeParentProperty->name; - - if (!$nodeParentPropertyName instanceof Node\Identifier) { - return false; - } - - $containerInterfacePropertyName = $nodeParentPropertyName->name; - $scopeClassReflection = $scope->getClassReflection(); - - if (!$scopeClassReflection instanceof ClassReflection) { + if (!$autowireLocatorServiceMap instanceof DefaultServiceMap) { return false; } - $containerInterfacePropertyReflection = $scopeClassReflection - ->getNativeProperty($containerInterfacePropertyName); - $classPropertyReflection = $containerInterfacePropertyReflection->getNativeReflection(); - $autowireLocatorAttributes = $classPropertyReflection->getAttributes(AutowireLocator::class); - - return $this->isAutowireLocatorService($autowireLocatorAttributes, $serviceId); - } - - /** - * @param array $autowireLocatorAttributes - */ - private function isAutowireLocatorService(array $autowireLocatorAttributes, string $serviceId): bool - { - foreach ($autowireLocatorAttributes as $autowireLocatorAttribute) { - /** @var AutowireLocator $autowireLocatorInstance */ - $autowireLocator = $autowireLocatorAttribute->newInstance(); - $autowireLocatorServices = $autowireLocator->value->getValues(); - - if (array_key_exists($serviceId, $autowireLocatorServices)) { - return true; - } - } - - return false; + return !is_null($autowireLocatorServiceMap->getService($serviceId)); } } diff --git a/src/Symfony/AutowireLocatorServiceMapFactory.php b/src/Symfony/AutowireLocatorServiceMapFactory.php new file mode 100644 index 00000000..839f621a --- /dev/null +++ b/src/Symfony/AutowireLocatorServiceMapFactory.php @@ -0,0 +1,110 @@ +node = $node; + $this->scope = $scope; + } + + public function create(): ServiceMap + { + if (!class_exists('Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator')) { + return new FakeServiceMap(); + } + + if (!$this->node instanceof MethodCall) { + return new FakeServiceMap(); + } + + $nodeParentProperty = $this->node->var; + + if (!$nodeParentProperty instanceof Node\Expr\PropertyFetch) { + return new FakeServiceMap(); + } + + $nodeParentPropertyName = $nodeParentProperty->name; + + if (!$nodeParentPropertyName instanceof Node\Identifier) { + return new FakeServiceMap(); + } + + $containerInterfacePropertyName = $nodeParentPropertyName->name; + $scopeClassReflection = $this->scope->getClassReflection(); + + if (!$scopeClassReflection instanceof ClassReflection) { + return new FakeServiceMap(); + } + + $containerInterfacePropertyReflection = $scopeClassReflection + ->getNativeProperty($containerInterfacePropertyName); + $classPropertyReflection = $containerInterfacePropertyReflection->getNativeReflection(); + $autowireLocatorAttributesReflection = $classPropertyReflection->getAttributes(AutowireLocator::class); + + if (count($autowireLocatorAttributesReflection) === 0) { + return new FakeServiceMap(); + } + + if (count($autowireLocatorAttributesReflection) > 1) { + throw new InvalidArgumentException(sprintf( + 'Only one AutowireLocator attribute is allowed on "%s::%s".', + $scopeClassReflection->getName(), + $containerInterfacePropertyName + )); + } + + $autowireLocatorAttributeReflection = $autowireLocatorAttributesReflection[0]; + /** @var AutowireLocator $autowireLocator */ + $autowireLocator = $autowireLocatorAttributeReflection->newInstance(); + $serviceLocatorArgument = $autowireLocator->value; + + if (!$serviceLocatorArgument instanceof ServiceLocatorArgument) { + return new FakeServiceMap(); + } + + /** @var Service[] $services */ + $services = []; + + /** @var TypedReference $service */ + foreach ($serviceLocatorArgument->getValues() as $id => $service) { + $class = $service->getType(); + $alias = $service->getName(); + + $services[$id] = new Service( + $id, + $class, + true, + false, + $alias + ); + } + + return new DefaultServiceMap($services); + } + +} From da0d4bdfec2700c78845a694364f426aad24c04e Mon Sep 17 00:00:00 2001 From: RafaelKr <14234815+RafaelKr@users.noreply.github.com> Date: Tue, 31 Dec 2024 17:30:46 +0100 Subject: [PATCH 4/5] Add #[AutowireLocator] attribute support for ContainerInterfaceUnknownServiceRule --- .../ContainerInterfaceUnknownServiceRule.php | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index fc7d9585..cb29dd33 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -6,8 +6,11 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; +use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Symfony\AutowireLocatorServiceMapFactory; +use PHPStan\Symfony\DefaultServiceMap; use PHPStan\Symfony\ServiceMap; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; @@ -66,19 +69,56 @@ public function processNode(Node $node, Scope $scope): array } $serviceId = $this->serviceMap::getServiceIdFromNode($node->getArgs()[0]->value, $scope); - 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 ($serviceId === null) { + return []; + } + + $isContainerInterfaceType = $isContainerType->yes() || $isPsrContainerType->yes(); + if ($isContainerInterfaceType) { + $autowireLoaderResult = $this->getAutowireLocatorResult($node, $scope, $serviceId); + + if ($autowireLoaderResult !== null) { + return $autowireLoaderResult; + } + } + + $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)) { + return [ + RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the container.', $serviceId)) + ->identifier('symfonyContainer.serviceNotFound') + ->build(), + ]; + } + + return []; + } + + /** + * @return list|null + */ + private function getAutowireLocatorResult(Node $node, Scope $scope, string $serviceId): ?array + { + $autowireLocatorServiceMapFactory = new AutowireLocatorServiceMapFactory($node, $scope); + $autowireLocatorServiceMap = $autowireLocatorServiceMapFactory->create(); + + // Our container has a valid AutowireLocator attribute, else we would get a FakeServiceMap. + if ($autowireLocatorServiceMap instanceof DefaultServiceMap) { + $autowireLocatorService = $autowireLocatorServiceMap->getService($serviceId); + + if ($autowireLocatorService === null) { return [ - RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the container.', $serviceId)) - ->identifier('symfonyContainer.serviceNotFound') + RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the AutowireLocator.', $serviceId)) + ->identifier('symfonyContainer.undefinedService') ->build(), ]; } + + return []; } - return []; + return null; } } From 469d7a0a916ea78d772079a5987b31dfb1dbb968 Mon Sep 17 00:00:00 2001 From: RafaelKr <14234815+RafaelKr@users.noreply.github.com> Date: Fri, 3 Jan 2025 21:18:29 +0100 Subject: [PATCH 5/5] Add tests for AutowireLocator --- ...ntainerInterfacePrivateServiceRuleTest.php | 34 +++++++++++++++++ ...ntainerInterfaceUnknownServiceRuleTest.php | 38 +++++++++++++++++++ .../ExampleAutowireLocatorEmptyService.php | 25 ++++++++++++ .../Symfony/ExampleAutowireLocatorService.php | 27 +++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 tests/Rules/Symfony/ExampleAutowireLocatorEmptyService.php create mode 100644 tests/Rules/Symfony/ExampleAutowireLocatorService.php diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php index 51513b09..c421cdd7 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php @@ -8,6 +8,7 @@ use PHPStan\Testing\RuleTestCase; use function class_exists; use function interface_exists; +use const PHP_VERSION_ID; /** * @extends RuleTestCase @@ -78,4 +79,37 @@ public function testGetPrivateServiceInServiceSubscriber(): void ); } + public function testGetPrivateServiceWithoutAutowireLocatorAttribute(): void + { + if (PHP_VERSION_ID < 80000) { + self::markTestSkipped('The test uses PHP Attributes which are available since PHP 8.0.'); + } + + $this->analyse( + [ + __DIR__ . '/ExampleAutowireLocatorEmptyService.php', + ], + [ + [ + 'Service "private" is private.', + 22, + ], + ] + ); + } + + public function testGetPrivateServiceViaAutowireLocatorAttribute(): void + { + if (PHP_VERSION_ID < 80000) { + self::markTestSkipped('The test uses PHP Attributes which are available since PHP 8.0.'); + } + + $this->analyse( + [ + __DIR__ . '/ExampleAutowireLocatorService.php', + ], + [] + ); + } + } diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index 4bd233e9..3d185aa0 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -9,6 +9,7 @@ use PHPStan\Testing\RuleTestCase; use function class_exists; use function interface_exists; +use const PHP_VERSION_ID; /** * @extends RuleTestCase @@ -72,6 +73,43 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void ); } + public function testGetPrivateServiceWithoutAutowireLocatorAttribute(): void + { + if (PHP_VERSION_ID < 80000) { + self::markTestSkipped('The test uses PHP Attributes which are available since PHP 8.0.'); + } + + $this->analyse( + [ + __DIR__ . '/ExampleAutowireLocatorEmptyService.php', + ], + [ + [ + 'Service "Foo" is not registered in the AutowireLocator.', + 21, + ], + [ + 'Service "private" is not registered in the AutowireLocator.', + 22, + ], + ] + ); + } + + public function testGetPrivateServiceViaAutowireLocatorAttribute(): void + { + if (PHP_VERSION_ID < 80000) { + self::markTestSkipped('The test uses PHP Attributes which are available since PHP 8.0.'); + } + + $this->analyse( + [ + __DIR__ . '/ExampleAutowireLocatorService.php', + ], + [] + ); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/Rules/Symfony/ExampleAutowireLocatorEmptyService.php b/tests/Rules/Symfony/ExampleAutowireLocatorEmptyService.php new file mode 100644 index 00000000..d59f30b2 --- /dev/null +++ b/tests/Rules/Symfony/ExampleAutowireLocatorEmptyService.php @@ -0,0 +1,25 @@ +locator = $locator; + } + + public function privateServiceInLocator(): void + { + $this->locator->get('Foo'); + $this->locator->get('private'); + } + +} diff --git a/tests/Rules/Symfony/ExampleAutowireLocatorService.php b/tests/Rules/Symfony/ExampleAutowireLocatorService.php new file mode 100644 index 00000000..e78d4859 --- /dev/null +++ b/tests/Rules/Symfony/ExampleAutowireLocatorService.php @@ -0,0 +1,27 @@ + 'Foo', + 'private' => 'Foo', + ])] + private ContainerInterface $locator + ) + { + } + + public function privateServiceInLocator(): void + { + $this->locator->get('Foo'); + $this->locator->get('private'); + } + +}