Skip to content

Commit 7480e9d

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

File tree

2 files changed

+113
-56
lines changed

2 files changed

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

0 commit comments

Comments
 (0)