Skip to content

Commit 941c104

Browse files
authored
Merge pull request #1902 from Haehnchen/feature/1366-subscriber
#1366 mark public method callbacks inside getSubscribedEvents as used code
2 parents 8c7bee1 + 448edf4 commit 941c104

File tree

3 files changed

+101
-6
lines changed

3 files changed

+101
-6
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/codeInsight/SymfonyImplicitUsageProvider.java

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
44
import com.intellij.psi.PsiElement;
5-
import com.jetbrains.php.lang.psi.elements.Method;
6-
import com.jetbrains.php.lang.psi.elements.PhpAttribute;
7-
import com.jetbrains.php.lang.psi.elements.PhpClass;
8-
import com.jetbrains.php.lang.psi.elements.PhpModifier;
5+
import com.intellij.psi.util.PsiTreeUtil;
6+
import com.jetbrains.php.lang.parser.PhpElementTypes;
7+
import com.jetbrains.php.lang.psi.elements.*;
98
import de.espend.idea.php.annotation.dict.PhpDocCommentAnnotation;
109
import de.espend.idea.php.annotation.util.AnnotationUtil;
1110
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper;
1211
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
1312
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
13+
import org.apache.commons.lang.StringUtils;
1414
import org.jetbrains.annotations.NotNull;
1515

1616
import java.util.Collection;
@@ -27,10 +27,12 @@ public class SymfonyImplicitUsageProvider implements ImplicitUsageProvider {
2727
@Override
2828
public boolean isImplicitUsage(@NotNull PsiElement element) {
2929
if (element instanceof Method && ((Method) element).getAccess() == PhpModifier.Access.PUBLIC) {
30-
return isMethodARoute((Method) element);
30+
return isMethodARoute((Method) element)
31+
|| isSubscribedEvent((Method) element);
3132
} else if (element instanceof PhpClass) {
3233
return isRouteClass((PhpClass) element)
33-
|| isCommandAndService((PhpClass) element);
34+
|| isCommandAndService((PhpClass) element)
35+
|| isSubscribedEvent((PhpClass) element);
3436
}
3537

3638
return false;
@@ -73,4 +75,45 @@ private boolean isMethodARoute(@NotNull Method method) {
7375

7476
return RouteHelper.isRouteExistingForMethod(method);
7577
}
78+
79+
private boolean isSubscribedEvent(@NotNull PhpClass phpClass) {
80+
return phpClass.getMethods()
81+
.stream()
82+
.filter(method -> method.getAccess() == PhpModifier.Access.PUBLIC)
83+
.anyMatch(this::isSubscribedEvent);
84+
}
85+
86+
private boolean isSubscribedEvent(@NotNull Method method) {
87+
PhpClass containingClass = method.getContainingClass();
88+
if (containingClass == null || !PhpElementsUtil.isInstanceOf(containingClass, "\\Symfony\\Component\\EventDispatcher\\EventSubscriberInterface")) {
89+
return false;
90+
}
91+
92+
Method subscribedEvents = containingClass.findMethodByName("getSubscribedEvents");
93+
if (subscribedEvents == null) {
94+
return false;
95+
}
96+
97+
for (PhpReturn aReturn : PsiTreeUtil.collectElementsOfType(subscribedEvents, PhpReturn.class)) {
98+
PsiElement[] psiElements = PsiTreeUtil.collectElements(aReturn, element -> {
99+
if (!(element instanceof StringLiteralExpression)) {
100+
return false;
101+
}
102+
103+
PsiElement parent = element.getParent();
104+
return parent != null && parent.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE && parent.getChildren().length == 1;
105+
});
106+
107+
for (PsiElement psiElement : psiElements) {
108+
if (psiElement instanceof StringLiteralExpression) {
109+
String contents = ((StringLiteralExpression) psiElement).getContents();
110+
if (StringUtils.isNotBlank(contents) && contents.equalsIgnoreCase(method.getName())) {
111+
return true;
112+
}
113+
}
114+
}
115+
}
116+
117+
return false;
118+
}
76119
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/codeInsight/SymfonyImplicitUsageProviderTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,50 @@ public void testCommandRegisteredNotAsServiceIsUntouched() {
137137
assertFalse(new SymfonyImplicitUsageProvider().isImplicitUsage(firstClassFromFile));
138138
}
139139

140+
public void testEventSubscriberGetSubscribedEventsArray() {
141+
PsiFile psiFile = myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
142+
"<?php\n" +
143+
"\n" +
144+
"namespace App\\EventSubscriber;\n" +
145+
"\n" +
146+
"use Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;\n" +
147+
"use Symfony\\Component\\HttpKernel\\KernelEvents;\n" +
148+
"\n" +
149+
"class ExceptionSubscriber implements EventSubscriberInterface\n" +
150+
"{\n" +
151+
" public static function getSubscribedEvents()\n" +
152+
" {\n" +
153+
" return [\n" +
154+
" KernelEvents::EXCEPTION => [\n" +
155+
" ['processException', 10],\n" +
156+
" ['notifyException', -10],\n" +
157+
" ],\n" +
158+
" ];\n" +
159+
" return [\n" +
160+
" 'keyString' => 'logException'\n" +
161+
" ];" +
162+
" }\n" +
163+
"\n" +
164+
" public function processException() {}\n" +
165+
" public function logException() {}\n" +
166+
" public function notifyException() {}\n" +
167+
" public function notifyExceptionUnknown() {}\n" +
168+
" public function keyString() {}\n" +
169+
"}"
170+
);
171+
172+
PhpClass phpClass = PhpElementsUtil.getFirstClassFromFile((PhpFile) psiFile.getContainingFile());
173+
174+
assertTrue(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass.findOwnMethodByName("processException")));
175+
assertTrue(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass.findOwnMethodByName("logException")));
176+
assertTrue(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass.findOwnMethodByName("notifyException")));
177+
178+
assertFalse(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass.findOwnMethodByName("notifyExceptionUnknown")));
179+
assertFalse(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass.findOwnMethodByName("keyString")));
180+
181+
assertTrue(new SymfonyImplicitUsageProvider().isImplicitUsage(phpClass));
182+
}
183+
140184
private PhpClass createPhpControllerClassWithRouteContent(@NotNull String content) {
141185
return createPhpControllerClassWithRouteContent("\\App\\Controller\\FooController", content);
142186
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/codeInsight/fixtures/classes.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,12 @@ class Route {}
1313
class Command
1414
{
1515
}
16+
}
17+
18+
namespace Symfony\Component\EventDispatcher
19+
{
20+
interface EventSubscriberInterface
21+
{
22+
public static function getSubscribedEvents();
23+
}
1624
}

0 commit comments

Comments
 (0)