package fr.adrienbrault.idea.symfony2plugin.util; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.jetbrains.php.PhpIndex; import com.jetbrains.php.lang.psi.elements.*; import com.jetbrains.php.lang.psi.resolve.types.PhpType; import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider3; import fr.adrienbrault.idea.symfony2plugin.Settings; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; import java.util.Set; /** * @author Daniel Espendiller <daniel@espendiller.net> */ public class EventDispatcherTypeProvider implements PhpTypeProvider3 { private static char TRIM_KEY = '\u0197'; @Override public char getKey() { return '\u0187'; } @Nullable @Override public PhpType getType(PsiElement e) { if (!Settings.getInstance(e.getProject()).pluginEnabled || !Settings.getInstance(e.getProject()).symfonyContainerTypeProvider) { return null; } // container calls are only on "get" methods if(!(e instanceof MethodReference) || !"dispatch".equals(((MethodReference) e).getName())) { return null; } PsiElement[] parameters = ((MethodReference) e).getParameters(); if(parameters.length < 2) { return null; } String refSignature = ((MethodReference) e).getSignature(); if(StringUtils.isBlank(refSignature)) { return null; } String signature = null; if(parameters[1] instanceof NewExpression) { // dispatch('foo', new FooEvent()); ClassReference classReference = ((NewExpression) parameters[1]).getClassReference(); if(classReference == null) { return null; } signature = classReference.getFQN(); if(StringUtils.isBlank(signature)) { return null; } } else if(parameters[1] instanceof Variable) { // $event = new FooEvent(); // dispatch('foo', $event); String firstVariableInstance = PhpElementsUtil.getFirstVariableTypeInScope((Variable) parameters[1]); if(firstVariableInstance == null) { return null; } signature = firstVariableInstance; } else { return null; } return new PhpType().add("#" + this.getKey() + refSignature + TRIM_KEY + signature); } @Override public Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project) { // get back our original call // since phpstorm 7.1.2 we need to validate this int endIndex = expression.lastIndexOf(TRIM_KEY); if(endIndex == -1) { return Collections.emptySet(); } String originalSignature = expression.substring(0, endIndex); String parameter = expression.substring(endIndex + 1); if(!parameter.startsWith("\\")) { return Collections.emptySet(); } PhpClass phpClass = PhpElementsUtil.getClass(project, parameter); if(phpClass == null) { return Collections.emptySet(); } // search for called method PhpIndex phpIndex = PhpIndex.getInstance(project); Collection<? extends PhpNamedElement> phpNamedElementCollections = PhpTypeProviderUtil.getTypeSignature(phpIndex, originalSignature); if(phpNamedElementCollections.size() == 0) { return Collections.emptySet(); } // get first matched item PhpNamedElement phpNamedElement = phpNamedElementCollections.iterator().next(); if(!(phpNamedElement instanceof Method)) { return phpNamedElementCollections; } PhpClass containingClass = ((Method) phpNamedElement).getContainingClass(); if(containingClass == null) { return phpNamedElementCollections; } parameter = PhpTypeProviderUtil.getResolvedParameter(phpIndex, parameter); if(parameter == null) { return phpNamedElementCollections; } // finally search the classes if(!PhpElementsUtil.isInstanceOf(containingClass, "\\Symfony\\Component\\EventDispatcher\\EventDispatcherInterface")) { return phpNamedElementCollections; } return Collections.singletonList(phpClass); } }