|
5 | 5 | use PhpParser\Node\Expr;
|
6 | 6 | use PhpParser\Node\Expr\ClassConstFetch;
|
7 | 7 | use PhpParser\Node\Scalar\String_;
|
| 8 | +use PhpParser\Node\Stmt\Class_; |
8 | 9 | use PhpParser\Node\Stmt\ClassLike;
|
| 10 | +use PhpParser\Node\Stmt\ClassMethod; |
| 11 | +use PhpParser\Node\Stmt\Return_; |
9 | 12 | use Psalm\Codebase;
|
10 | 13 | use Psalm\CodeLocation;
|
11 | 14 | use Psalm\Context;
|
|
19 | 22 | use Psalm\SymfonyPsalmPlugin\Issue\PrivateService;
|
20 | 23 | use Psalm\SymfonyPsalmPlugin\Issue\ServiceNotFound;
|
21 | 24 | use Psalm\SymfonyPsalmPlugin\Symfony\ContainerMeta;
|
| 25 | +use Psalm\SymfonyPsalmPlugin\Symfony\Service; |
22 | 26 | use Psalm\Type\Atomic\TNamedObject;
|
23 | 27 | use Psalm\Type\Union;
|
24 | 28 |
|
@@ -58,7 +62,7 @@ public static function afterMethodCallAnalysis(
|
58 | 62 | if (!self::isContainerMethod($declaring_method_id, 'get')) {
|
59 | 63 | if (self::isContainerMethod($declaring_method_id, 'getparameter')) {
|
60 | 64 | $argument = $expr->args[0]->value;
|
61 |
| - if ($argument instanceof String_ && !self::followsNamingConvention($argument->value)) { |
| 65 | + if ($argument instanceof String_ && !self::followsNamingConvention($argument->value) && false === strpos($argument->value, '\\')) { |
62 | 66 | IssueBuffer::accepts(
|
63 | 67 | new NamingConventionViolation(new CodeLocation($statements_source, $argument)),
|
64 | 68 | $statements_source->getSuppressedIssues()
|
@@ -90,7 +94,7 @@ public static function afterMethodCallAnalysis(
|
90 | 94 |
|
91 | 95 | $service = self::$containerMeta->get($serviceId);
|
92 | 96 | if ($service) {
|
93 |
| - if (!self::followsNamingConvention($serviceId) && !class_exists($service->getClassName())) { |
| 97 | + if (!self::followsNamingConvention($serviceId) && false === strpos($serviceId, '\\')) { |
94 | 98 | IssueBuffer::accepts(
|
95 | 99 | new NamingConventionViolation(new CodeLocation($statements_source, $expr->args[0]->value)),
|
96 | 100 | $statements_source->getSuppressedIssues()
|
@@ -130,14 +134,46 @@ public static function afterClassLikeVisit(
|
130 | 134 | Codebase $codebase,
|
131 | 135 | array &$file_replacements = []
|
132 | 136 | ) {
|
| 137 | + $fileStorage = $codebase->file_storage_provider->get($statements_source->getFilePath()); |
| 138 | + |
133 | 139 | if (\in_array($storage->name, ContainerHandler::GET_CLASSLIKES)) {
|
134 | 140 | if (self::$containerMeta) {
|
135 |
| - $file_path = $statements_source->getFilePath(); |
136 |
| - $file_storage = $codebase->file_storage_provider->get($file_path); |
137 |
| - |
138 | 141 | foreach (self::$containerMeta->getClassNames() as $className) {
|
139 | 142 | $codebase->queueClassLikeForScanning($className);
|
140 |
| - $file_storage->referenced_classlikes[strtolower($className)] = $className; |
| 143 | + $fileStorage->referenced_classlikes[strtolower($className)] = $className; |
| 144 | + } |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + // see https://symfony.com/doc/current/service_container/service_subscribers_locators.html |
| 149 | + if (self::$containerMeta && $stmt instanceof Class_ && isset($storage->class_implements['symfony\contracts\service\servicesubscriberinterface'])) { |
| 150 | + foreach ($stmt->stmts as $classStmt) { |
| 151 | + if ($classStmt instanceof ClassMethod && 'getSubscribedServices' === $classStmt->name->name && $classStmt->stmts) { |
| 152 | + foreach ($classStmt->stmts as $methodStmt) { |
| 153 | + if ($methodStmt instanceof Return_ && ($return = $methodStmt->expr) && $return instanceof Expr\Array_) { |
| 154 | + foreach ($return->items as $arrayItem) { |
| 155 | + if ($arrayItem instanceof Expr\ArrayItem) { |
| 156 | + $value = $arrayItem->value; |
| 157 | + if (!$value instanceof Expr\ClassConstFetch) { |
| 158 | + continue; |
| 159 | + } |
| 160 | + |
| 161 | + /** @var string $className */ |
| 162 | + $className = $value->class->getAttribute('resolvedName'); |
| 163 | + |
| 164 | + $key = $arrayItem->key; |
| 165 | + $serviceId = $key instanceof String_ ? $key->value : $className; |
| 166 | + |
| 167 | + $service = new Service($serviceId, $className); |
| 168 | + $service->setIsPublic(true); |
| 169 | + self::$containerMeta->add($service); |
| 170 | + |
| 171 | + $codebase->queueClassLikeForScanning($className); |
| 172 | + $fileStorage->referenced_classlikes[strtolower($className)] = $className; |
| 173 | + } |
| 174 | + } |
| 175 | + } |
| 176 | + } |
141 | 177 | }
|
142 | 178 | }
|
143 | 179 | }
|
|
0 commit comments