Skip to content

Commit

Permalink
collect type hints for methods of getSubscribedEvents #531, #529
Browse files Browse the repository at this point in the history
  • Loading branch information
Haehnchen committed Aug 9, 2015
1 parent 1521665 commit 4d6072f
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 9 deletions.
Expand Up @@ -33,7 +33,7 @@ public static List<EventDispatcherSubscribedEvent> getSubscribedEvents(@NotNull
continue;
}

Method method = PhpElementsUtil.getClassMethod(phpClass, "getSubscribedEvents");
Method method = phpClass.findMethodByName("getSubscribedEvents");
if(method != null) {
PhpReturn phpReturn = PsiTreeUtil.findChildOfType(method, PhpReturn.class);
if(phpReturn != null) {
Expand All @@ -52,17 +52,36 @@ private static void attachSubscriberEventNames(@NotNull List<EventDispatcherSubs
return;
}

String presentableFQN = phpClass.getPresentableFQN();
if(presentableFQN == null) {
return;
}

Iterable<ArrayHashElement> arrayHashElements = ((ArrayCreationExpression) array).getHashElements();
for(ArrayHashElement arrayHashElement: arrayHashElements) {
PsiElement arrayKey = arrayHashElement.getKey();

// support string and constants
if(arrayKey instanceof StringLiteralExpression) {
events.add(new EventDispatcherSubscribedEvent(((StringLiteralExpression) arrayKey).getContents(), phpClass.getPresentableFQN()));

// ['doh' => 'method']
events.add(new EventDispatcherSubscribedEvent(
((StringLiteralExpression) arrayKey).getContents(),
presentableFQN,
PhpElementsUtil.getStringValue(arrayHashElement.getValue())
));

} else if(arrayKey instanceof PhpReference) {
String resolvedString = PhpElementsUtil.getStringValue(arrayKey);
if(resolvedString != null) {
events.add(new EventDispatcherSubscribedEvent(resolvedString, phpClass.getPresentableFQN(), ((PhpReference) arrayKey).getSignature()));

// [FOO::BAR => 'method']
events.add(new EventDispatcherSubscribedEvent(
resolvedString,
presentableFQN,
PhpElementsUtil.getStringValue(arrayHashElement.getValue()),
((PhpReference) arrayKey).getSignature())
);
}

}
Expand Down Expand Up @@ -90,6 +109,7 @@ public static List<PsiElement> getEventPsiElements(@NotNull Project project, @No

List<PsiElement> psiElements = new ArrayList<PsiElement>();

// @TODO: remove
XmlEventParser xmlEventParser = ServiceXmlParserFactory.getInstance(project, XmlEventParser.class);
for(EventDispatcherSubscribedEvent event : xmlEventParser.getEventSubscribers(eventName)) {
PhpClass phpClass = PhpElementsUtil.getClass(project, event.getFqnClassName());
Expand Down
@@ -1,21 +1,27 @@
package fr.adrienbrault.idea.symfony2plugin.config.dic;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class EventDispatcherSubscribedEvent {

private String stringValue;
private String fqnClassName;

@Nullable
private final String methodName;

private String signature = null;
private String type = "EventSubscriber";

public EventDispatcherSubscribedEvent(String stringValue, String fqnClassName) {
public EventDispatcherSubscribedEvent(@NotNull String stringValue, @NotNull String fqnClassName, @Nullable String methodName) {
this.stringValue = stringValue;
this.fqnClassName = fqnClassName;
this.methodName = methodName;
}

public EventDispatcherSubscribedEvent(String stringValue, String fqnClassName, String signature) {
this(stringValue, fqnClassName);
public EventDispatcherSubscribedEvent(@NotNull String stringValue, @NotNull String fqnClassName, @Nullable String methodName, @NotNull String signature) {
this(stringValue, fqnClassName, methodName);
this.signature = signature;
}

Expand All @@ -42,4 +48,7 @@ public EventDispatcherSubscribedEvent setType(String type) {
return this;
}

}
@Nullable
public String getMethodName() {
return methodName;
}}
Expand Up @@ -32,7 +32,7 @@ public void parser(File file) {
Element node = (Element) nodeList.item(i);
this.list.put(node.getAttribute("event"), node.getAttribute("name"));
if(((Element) node.getParentNode()).hasAttribute("class")) {
this.events.add(new EventDispatcherSubscribedEvent(node.getAttribute("event"), ((Element) node.getParentNode()).getAttribute("class")).setType(node.getAttribute("name")));
this.events.add(new EventDispatcherSubscribedEvent(node.getAttribute("event"), ((Element) node.getParentNode()).getAttribute("class"), null).setType(node.getAttribute("name")));
}

}
Expand Down
Expand Up @@ -5,6 +5,8 @@
import com.intellij.psi.xml.XmlTag;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import fr.adrienbrault.idea.symfony2plugin.config.EventDispatcherSubscriberUtil;
import fr.adrienbrault.idea.symfony2plugin.config.dic.EventDispatcherSubscribedEvent;
import fr.adrienbrault.idea.symfony2plugin.stubs.ServiceIndexUtil;
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
import org.apache.commons.lang.StringUtils;
Expand Down Expand Up @@ -60,6 +62,21 @@ public static String getTaggedEventMethodParameter(Project project, String event

}

for (EventDispatcherSubscribedEvent event : EventDispatcherSubscriberUtil.getSubscribedEvent(project, eventName)) {
String methodName = event.getMethodName();
if(methodName == null) {
continue;
}

Method method = PhpElementsUtil.getClassMethod(project, event.getFqnClassName(), methodName);
if(method != null) {
String methodParameterClassHint = PhpElementsUtil.getMethodParameterTypeHint(method);
if(methodParameterClassHint != null) {
return methodParameterClassHint;
}
}
}

return null;
}

Expand Down
Expand Up @@ -219,7 +219,7 @@ static public Method getClassMethod(PhpClass phpClass, String methodName) {
}

@Nullable
static public Method getClassMethod(Project project, String phpClassName, String methodName) {
static public Method getClassMethod(@NotNull Project project, @NotNull String phpClassName, @NotNull String methodName) {

// we need here an each; because eg Command is non unique because phar file
for(PhpClass phpClass: PhpIndex.getInstance(project).getClassesByFQN(phpClassName)) {
Expand Down
@@ -0,0 +1,41 @@
package fr.adrienbrault.idea.symfony2plugin.tests.config;

import fr.adrienbrault.idea.symfony2plugin.config.EventDispatcherSubscriberUtil;
import fr.adrienbrault.idea.symfony2plugin.config.dic.EventDispatcherSubscribedEvent;
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;

import java.io.File;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class EventDispatcherSubscriberUtilTest extends SymfonyLightCodeInsightFixtureTestCase {

public void setUp() throws Exception {
super.setUp();
myFixture.copyFileToProject("EventSubscriber.php");
}

public String getTestDataPath() {
return new File(this.getClass().getResource("fixtures").getFile()).getAbsolutePath();
}

/**
* @see EventDispatcherSubscriberUtil#getSubscribedEvents
* @see EventDispatcherSubscriberUtil#attachSubscriberEventNames
*/
public void testGetSubscribedEvent() {
EventDispatcherSubscribedEvent event1 = EventDispatcherSubscriberUtil.getSubscribedEvent(getProject(), "pre.foo").get(0);
assertEquals("pre.foo", event1.getStringValue());
assertEquals("TestEventSubscriber", event1.getFqnClassName());
assertNull(event1.getSignature());
assertEquals("preFoo", event1.getMethodName());

EventDispatcherSubscribedEvent event2 = EventDispatcherSubscriberUtil.getSubscribedEvent(getProject(), "post.foo").get(0);
assertEquals("post.foo", event2.getStringValue());
assertEquals("TestEventSubscriber", event2.getFqnClassName());
assertEquals("#K#C\\Foo\\Bar.BAR", event2.getSignature());
assertEquals("postFoo", event2.getMethodName());
}

}
@@ -0,0 +1,34 @@
<?php

namespace Foo {
class Bar {
const BAR = 'post.foo';
}
}

namespace Symfony\Component\EventDispatcher {
interface EventSubscriberInterface
{
public static function getSubscribedEvents();
}
}

namespace {

use Foo\Bar;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class TestEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
'pre.foo' => 'preFoo',
Bar::BAR => 'postFoo'
);
}

public function preFoo() {}
public function postFoo() {}
}
}
@@ -0,0 +1,37 @@
package fr.adrienbrault.idea.symfony2plugin.tests.util;

import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
import fr.adrienbrault.idea.symfony2plugin.util.EventSubscriberUtil;

import java.io.File;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class EventSubscriberUtilTest extends SymfonyLightCodeInsightFixtureTestCase {

public void setUp() throws Exception {
super.setUp();
myFixture.copyFileToProject("EventSubscriber.php");
}

public String getTestDataPath() {
return new File(this.getClass().getResource("fixtures").getFile()).getAbsolutePath();
}

/**
* @see EventSubscriberUtil#getTaggedEventMethodParameter
*/
public void testGetTaggedEventMethodParameterWithInternals() {
assertEquals("\\Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent", EventSubscriberUtil.getTaggedEventMethodParameter(getProject(), "kernel.request"));
}

/**
* @see EventSubscriberUtil#getTaggedEventMethodParameter
*/
public void testGetTaggedEventMethodParameterWithSubscriberInterface() {
assertEquals("\\DateTime", EventSubscriberUtil.getTaggedEventMethodParameter(getProject(), "pre.foo"));
assertNull(EventSubscriberUtil.getTaggedEventMethodParameter(getProject(), "pre.foo_doh"));
}

}
@@ -0,0 +1,27 @@
<?php

namespace Symfony\Component\EventDispatcher {
interface EventSubscriberInterface
{
public static function getSubscribedEvents();
}
}

namespace {

use Foo\Bar;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class TestDateTimeEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
'pre.foo' => 'preFoo',
'kernel.request' => 'FOO',
);
}

public function preFoo(\DateTime $d) {}
}
}

0 comments on commit 4d6072f

Please sign in to comment.