Skip to content

Commit 681d648

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

File tree

2 files changed

+109
-56
lines changed

2 files changed

+109
-56
lines changed

src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php

Lines changed: 8 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,14 @@
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;
1916
use function sprintf;
2017

2118
/**
@@ -78,7 +75,7 @@ public function processNode(Node $node, Scope $scope): array
7875
$isContainerInterfaceType = $isContainerType->yes() || $isPsrContainerType->yes();
7976
if (
8077
$isContainerInterfaceType &&
81-
$this->isAutowireLocator($node, $scope, $serviceId)
78+
$this->isAutowireLocatorService($node, $scope, $serviceId)
8279
) {
8380
return [];
8481
}
@@ -107,61 +104,16 @@ private function isServiceSubscriber(Type $containerType, Scope $scope): Trinary
107104
return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType));
108105
}
109106

110-
private function isAutowireLocator(Node $node, Scope $scope, string $serviceId): bool
107+
private function isAutowireLocatorService(Node $node, Scope $scope, string $serviceId): bool
111108
{
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-
}
109+
$autowireLocatorServiceMapFactory = new AutowireLoaderServiceMapFactory($node, $scope);
110+
$autowireLocatorServiceMap = $autowireLocatorServiceMapFactory->create();
127111

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) {
112+
if (!$autowireLocatorServiceMap instanceof DefaultServiceMap) {
138113
return false;
139114
}
140115

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;
116+
return !is_null($autowireLocatorServiceMap->getService($serviceId));
165117
}
166118

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

0 commit comments

Comments
 (0)