Skip to content

Commit 9f494e6

Browse files
committed
Create ServiceMap from AutowireLoader property
This allows us to share the logic for other rules
1 parent 534b45d commit 9f494e6

File tree

2 files changed

+119
-56
lines changed

2 files changed

+119
-56
lines changed

src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php

+9-56
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@
55
use PhpParser\Node;
66
use PhpParser\Node\Expr\MethodCall;
77
use PHPStan\Analyser\Scope;
8-
use PHPStan\BetterReflection\Reflection\Adapter\FakeReflectionAttribute;
9-
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionAttribute;
10-
use PHPStan\Reflection\ClassReflection;
118
use PHPStan\Rules\Rule;
129
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\Symfony\AutowireLoaderServiceMapFactory;
11+
use PHPStan\Symfony\DefaultServiceMap;
1312
use PHPStan\Symfony\ServiceMap;
1413
use PHPStan\TrinaryLogic;
1514
use PHPStan\Type\ObjectType;
1615
use PHPStan\Type\Type;
17-
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
18-
use function class_exists;
16+
use function is_null;
1917
use function sprintf;
2018

2119
/**
@@ -78,7 +76,7 @@ public function processNode(Node $node, Scope $scope): array
7876
$isContainerInterfaceType = $isContainerType->yes() || $isPsrContainerType->yes();
7977
if (
8078
$isContainerInterfaceType &&
81-
$this->isAutowireLocator($node, $scope, $serviceId)
79+
$this->isAutowireLocatorService($node, $scope, $serviceId)
8280
) {
8381
return [];
8482
}
@@ -107,61 +105,16 @@ private function isServiceSubscriber(Type $containerType, Scope $scope): Trinary
107105
return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType));
108106
}
109107

110-
private function isAutowireLocator(Node $node, Scope $scope, string $serviceId): bool
108+
private function isAutowireLocatorService(Node $node, Scope $scope, string $serviceId): bool
111109
{
112-
if (!class_exists('Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator')) {
113-
return false;
114-
}
115-
116-
if (
117-
!$node instanceof MethodCall
118-
) {
119-
return false;
120-
}
121-
122-
$nodeParentProperty = $node->var;
123-
124-
if (!$nodeParentProperty instanceof Node\Expr\PropertyFetch) {
125-
return false;
126-
}
110+
$autowireLocatorServiceMapFactory = new AutowireLoaderServiceMapFactory($node, $scope);
111+
$autowireLocatorServiceMap = $autowireLocatorServiceMapFactory->create();
127112

128-
$nodeParentPropertyName = $nodeParentProperty->name;
129-
130-
if (!$nodeParentPropertyName instanceof Node\Identifier) {
131-
return false;
132-
}
133-
134-
$containerInterfacePropertyName = $nodeParentPropertyName->name;
135-
$scopeClassReflection = $scope->getClassReflection();
136-
137-
if (!$scopeClassReflection instanceof ClassReflection) {
113+
if (!$autowireLocatorServiceMap instanceof DefaultServiceMap) {
138114
return false;
139115
}
140116

141-
$containerInterfacePropertyReflection = $scopeClassReflection
142-
->getNativeProperty($containerInterfacePropertyName);
143-
$classPropertyReflection = $containerInterfacePropertyReflection->getNativeReflection();
144-
$autowireLocatorAttributes = $classPropertyReflection->getAttributes(AutowireLocator::class);
145-
146-
return $this->isAutowireLocatorService($autowireLocatorAttributes, $serviceId);
147-
}
148-
149-
/**
150-
* @param array<int, FakeReflectionAttribute|ReflectionAttribute> $autowireLocatorAttributes
151-
*/
152-
private function isAutowireLocatorService(array $autowireLocatorAttributes, string $serviceId): bool
153-
{
154-
foreach ($autowireLocatorAttributes as $autowireLocatorAttribute) {
155-
/** @var AutowireLocator $autowireLocatorInstance */
156-
$autowireLocator = $autowireLocatorAttribute->newInstance();
157-
$autowireLocatorServices = $autowireLocator->value->getValues();
158-
159-
if (array_key_exists($serviceId, $autowireLocatorServices)) {
160-
return true;
161-
}
162-
}
163-
164-
return false;
117+
return !is_null($autowireLocatorServiceMap->getService($serviceId));
165118
}
166119

167120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
use InvalidArgumentException;
6+
use PhpParser\Node;
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Reflection\ClassReflection;
10+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
11+
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
12+
use Symfony\Component\DependencyInjection\TypedReference;
13+
use function class_exists;
14+
use function count;
15+
use function sprintf;
16+
17+
final class AutowireLoaderServiceMapFactory implements ServiceMapFactory
18+
{
19+
20+
/** @var Node */
21+
private $node;
22+
23+
/** @var Scope */
24+
private $scope;
25+
26+
public function __construct(
27+
Node $node,
28+
Scope $scope
29+
)
30+
{
31+
$this->node = $node;
32+
$this->scope = $scope;
33+
}
34+
35+
public function create(): ServiceMap
36+
{
37+
if (!class_exists('Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator')) {
38+
return new FakeServiceMap();
39+
}
40+
41+
if (!$this->node instanceof MethodCall) {
42+
return new FakeServiceMap();
43+
}
44+
45+
$nodeParentProperty = $this->node->var;
46+
47+
if (!$nodeParentProperty instanceof Node\Expr\PropertyFetch) {
48+
return new FakeServiceMap();
49+
}
50+
51+
$nodeParentPropertyName = $nodeParentProperty->name;
52+
53+
if (!$nodeParentPropertyName instanceof Node\Identifier) {
54+
return new FakeServiceMap();
55+
}
56+
57+
$containerInterfacePropertyName = $nodeParentPropertyName->name;
58+
$scopeClassReflection = $this->scope->getClassReflection();
59+
60+
if (!$scopeClassReflection instanceof ClassReflection) {
61+
return new FakeServiceMap();
62+
}
63+
64+
$containerInterfacePropertyReflection = $scopeClassReflection
65+
->getNativeProperty($containerInterfacePropertyName);
66+
$classPropertyReflection = $containerInterfacePropertyReflection->getNativeReflection();
67+
$autowireLocatorAttributesReflection = $classPropertyReflection->getAttributes(AutowireLocator::class);
68+
69+
if (count($autowireLocatorAttributesReflection) === 0) {
70+
return new FakeServiceMap();
71+
}
72+
73+
if (count($autowireLocatorAttributesReflection) > 1) {
74+
throw new InvalidArgumentException(sprintf(
75+
'Only one AutowireLocator attribute is allowed on "%s::%s".',
76+
$scopeClassReflection->getName(),
77+
$containerInterfacePropertyName
78+
));
79+
}
80+
81+
$autowireLocatorAttributeReflection = $autowireLocatorAttributesReflection[0];
82+
/** @var AutowireLocator $autowireLocator */
83+
$autowireLocator = $autowireLocatorAttributeReflection->newInstance();
84+
$serviceLocatorArgument = $autowireLocator->value;
85+
86+
if (!$serviceLocatorArgument instanceof ServiceLocatorArgument) {
87+
return new FakeServiceMap();
88+
}
89+
90+
/** @var Service[] $services */
91+
$services = [];
92+
93+
/** @var TypedReference $service */
94+
foreach ($serviceLocatorArgument->getValues() as $id => $service) {
95+
$class = $service->getType();
96+
$alias = $service->getName();
97+
98+
$services[$id] = new Service(
99+
$id,
100+
$class,
101+
true,
102+
false,
103+
$alias
104+
);
105+
}
106+
107+
return new DefaultServiceMap($services);
108+
}
109+
110+
}

0 commit comments

Comments
 (0)