| 
2 | 2 | 
 
  | 
3 | 3 | import com.intellij.openapi.project.Project;  | 
4 | 4 | import com.intellij.openapi.util.Key;  | 
5 |  | -import com.intellij.openapi.util.ModificationTracker;  | 
6 | 5 | import com.intellij.openapi.vfs.VfsUtil;  | 
7 | 6 | import com.intellij.openapi.vfs.VirtualFile;  | 
8 | 7 | import com.intellij.patterns.PlatformPatterns;  | 
 | 
14 | 13 | import com.intellij.util.Consumer;  | 
15 | 14 | import com.intellij.util.indexing.FileBasedIndex;  | 
16 | 15 | import com.jetbrains.php.PhpIndex;  | 
17 |  | -import com.jetbrains.php.lang.psi.elements.Field;  | 
18 |  | -import com.jetbrains.php.lang.psi.elements.Method;  | 
19 |  | -import com.jetbrains.php.lang.psi.elements.Parameter;  | 
20 |  | -import com.jetbrains.php.lang.psi.elements.PhpClass;  | 
 | 16 | +import com.jetbrains.php.lang.psi.PhpFile;  | 
 | 17 | +import com.jetbrains.php.lang.psi.elements.*;  | 
 | 18 | +import com.jetbrains.php.refactoring.PhpNamespaceBraceConverter;  | 
21 | 19 | import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlHelper;  | 
22 |  | -import fr.adrienbrault.idea.symfony2plugin.config.yaml.YamlElementPatternHelper;  | 
23 | 20 | import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.AttributeValueInterface;  | 
 | 21 | +import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.PhpKeyValueAttributeValue;  | 
24 | 22 | import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.XmlTagAttributeValue;  | 
25 | 23 | import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.YamlKeyValueAttributeValue;  | 
26 | 24 | import fr.adrienbrault.idea.symfony2plugin.dic.container.SerializableService;  | 
 | 
41 | 39 | import org.jetbrains.yaml.psi.*;  | 
42 | 40 | 
 
  | 
43 | 41 | import java.util.*;  | 
44 |  | -import java.util.function.Predicate;  | 
45 | 42 | import java.util.stream.Collectors;  | 
46 | 43 | import java.util.stream.Stream;  | 
47 | 44 | 
 
  | 
@@ -112,6 +109,11 @@ public static Collection<ServiceSerializable> getServicesInFile(@NotNull PsiFile  | 
112 | 109 |  serializableService.setIsDeprecated(serviceConsumer.attributes().getBoolean("deprecated"));  | 
113 | 110 |  }  | 
114 | 111 | 
 
  | 
 | 112 | + services.add(serializableService);  | 
 | 113 | + });  | 
 | 114 | + } else if (psiFile instanceof PhpFile) {  | 
 | 115 | + visitFile((PhpFile) psiFile, serviceConsumer -> {  | 
 | 116 | + SerializableService serializableService = createService(serviceConsumer);  | 
115 | 117 |  services.add(serializableService);  | 
116 | 118 |  });  | 
117 | 119 |  }  | 
@@ -239,6 +241,119 @@ public static void visitFile(@NotNull XmlFile psiFile, @NotNull Consumer<Service  | 
239 | 241 |  }  | 
240 | 242 |  }  | 
241 | 243 | 
 
  | 
 | 244 | + @Nullable  | 
 | 245 | + private static String getStringValueIndexSafe(@NotNull PsiElement psiElement) {  | 
 | 246 | + String serviceClass = null;  | 
 | 247 | + if (psiElement instanceof StringLiteralExpression) {  | 
 | 248 | + serviceClass = ((StringLiteralExpression) psiElement).getContents();  | 
 | 249 | + } else if(psiElement instanceof ClassConstantReference) {  | 
 | 250 | + serviceClass = PhpElementsUtil.getClassConstantPhpFqn((ClassConstantReference) psiElement);  | 
 | 251 | + }  | 
 | 252 | + | 
 | 253 | + return StringUtils.isNotBlank(serviceClass)  | 
 | 254 | + ? serviceClass  | 
 | 255 | + : null;  | 
 | 256 | + }  | 
 | 257 | + | 
 | 258 | + /**  | 
 | 259 | + * return static function (ContainerConfigurator $container) { ... }  | 
 | 260 | + */  | 
 | 261 | + @NotNull  | 
 | 262 | + private static Collection<Function> getPhpContainerConfiguratorFunctions(@NotNull PhpFile phpFile) {  | 
 | 263 | + Collection<Function> functions = new HashSet<>();  | 
 | 264 | + | 
 | 265 | + for (PhpNamespace phpNamespace : PhpNamespaceBraceConverter.getAllNamespaces(phpFile)) {  | 
 | 266 | + // its used for all service files:  | 
 | 267 | + // namespace \Symfony\Component\DependencyInjection\Loader\Configurator { ... }  | 
 | 268 | + String fqn = phpNamespace.getFQN();  | 
 | 269 | + if (!fqn.equals("\\Symfony\\Component\\DependencyInjection\\Loader\\Configurator")) {  | 
 | 270 | + continue;  | 
 | 271 | + }  | 
 | 272 | + | 
 | 273 | + for (PhpReturn phpReturn : PsiTreeUtil.collectElementsOfType(phpNamespace, PhpReturn.class)) {  | 
 | 274 | + for (Function function : PsiTreeUtil.collectElementsOfType(phpReturn, Function.class)) {  | 
 | 275 | + Parameter parameter = function.getParameter(0);  | 
 | 276 | + if (parameter == null) {  | 
 | 277 | + continue;  | 
 | 278 | + }  | 
 | 279 | + | 
 | 280 | + // \Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator  | 
 | 281 | + if(parameter.getLocalType().getTypes().stream().noneMatch(s -> s.contains("\\ContainerConfigurator"))) {  | 
 | 282 | + continue;  | 
 | 283 | + }  | 
 | 284 | + | 
 | 285 | + functions.add(function);  | 
 | 286 | + }  | 
 | 287 | + }  | 
 | 288 | + }  | 
 | 289 | + | 
 | 290 | + return functions;  | 
 | 291 | + }  | 
 | 292 | + | 
 | 293 | + /**  | 
 | 294 | + * Services as PHP definition  | 
 | 295 | + *  | 
 | 296 | + * "namespace Symfony\Component\DependencyInjection\Loader\Configurator;"  | 
 | 297 | + * "..."  | 
 | 298 | + * "return static function (ContainerConfigurator $container) { ... }"  | 
 | 299 | + */  | 
 | 300 | + public static void visitFile(@NotNull PhpFile phpFile, @NotNull Consumer<ServiceConsumer> consumer) {  | 
 | 301 | + for (Function function : getPhpContainerConfiguratorFunctions(phpFile)) {  | 
 | 302 | + // we only want "set" and "alias" methods  | 
 | 303 | + PsiElement[] methodReferences = PsiTreeUtil.collectElements(  | 
 | 304 | + function,  | 
 | 305 | + psiElement -> psiElement instanceof MethodReference && ("set".equals(((MethodReference) psiElement).getName()) || "alias".equals(((MethodReference) psiElement).getName()))  | 
 | 306 | + );  | 
 | 307 | + | 
 | 308 | + for (PsiElement psiElement : methodReferences) {  | 
 | 309 | + // ->set('translator.default', Translator::class)  | 
 | 310 | + if (psiElement instanceof MethodReference && "set".equals(((MethodReference) psiElement).getName())) {  | 
 | 311 | + PsiElement[] parameters = ((MethodReference) psiElement).getParameters();  | 
 | 312 | + String serviceName = null;  | 
 | 313 | + if (parameters.length >= 1) {  | 
 | 314 | + serviceName = getStringValueIndexSafe(parameters[0]);  | 
 | 315 | + }  | 
 | 316 | + | 
 | 317 | + if (StringUtils.isBlank(serviceName)) {  | 
 | 318 | + continue;  | 
 | 319 | + }  | 
 | 320 | + | 
 | 321 | + Map<String, String> keyValue = new HashMap<>();  | 
 | 322 | + if (parameters.length >= 2) {  | 
 | 323 | + String serviceClass = getStringValueIndexSafe(parameters[1]);  | 
 | 324 | + if (StringUtils.isNotBlank(serviceClass)) {  | 
 | 325 | + keyValue.put("class", serviceClass);  | 
 | 326 | + }  | 
 | 327 | + }  | 
 | 328 | + | 
 | 329 | + consumer.consume(new ServiceConsumer(psiElement, serviceName, new PhpKeyValueAttributeValue(psiElement, keyValue), ServiceFileDefaults.EMPTY));  | 
 | 330 | + }  | 
 | 331 | + | 
 | 332 | + // ->alias(TranslatorInterface::class, 'translator')  | 
 | 333 | + if (psiElement instanceof MethodReference && "alias".equals(((MethodReference) psiElement).getName())) {  | 
 | 334 | + PsiElement[] parameters = ((MethodReference) psiElement).getParameters();  | 
 | 335 | + String serviceName = null;  | 
 | 336 | + if (parameters.length >= 1) {  | 
 | 337 | + serviceName = getStringValueIndexSafe(parameters[0]);  | 
 | 338 | + }  | 
 | 339 | + | 
 | 340 | + if (StringUtils.isBlank(serviceName)) {  | 
 | 341 | + continue;  | 
 | 342 | + }  | 
 | 343 | + | 
 | 344 | + Map<String, String> keyValue = new HashMap<>();  | 
 | 345 | + if (parameters.length >= 2) {  | 
 | 346 | + String serviceClass = getStringValueIndexSafe(parameters[1]);  | 
 | 347 | + if (StringUtils.isNotBlank(serviceClass)) {  | 
 | 348 | + keyValue.put("alias", serviceClass);  | 
 | 349 | + }  | 
 | 350 | + }  | 
 | 351 | + | 
 | 352 | + consumer.consume(new ServiceConsumer(psiElement, serviceName, new PhpKeyValueAttributeValue(psiElement, keyValue), ServiceFileDefaults.EMPTY));  | 
 | 353 | + }  | 
 | 354 | + }  | 
 | 355 | + }  | 
 | 356 | + }  | 
242 | 357 | 
 
  | 
243 | 358 |  /**  | 
244 | 359 |  * Extract default values for services tag scope  | 
 | 
0 commit comments