Skip to content

Commit cf0e1c6

Browse files
committed
Replace getClassInterface with hasClassOrInterface for improved caching and performance of class and interface lookups.
1 parent f451ece commit cf0e1c6

File tree

8 files changed

+43
-14
lines changed

8 files changed

+43
-14
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/action/NewFileActionUtil.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public static boolean isInGivenDirectoryScope(@NotNull AnActionEvent event, @Not
6666

6767
public static String guessCommandTemplateType(@NotNull Project project, @NotNull String namespace) {
6868
// Check if InvokableCommand is available (Symfony 7.3+)
69-
if (PhpElementsUtil.getClassInterface(project, "\\Symfony\\Component\\Console\\Command\\InvokableCommand") != null) {
69+
if (PhpElementsUtil.hasClassOrInterface(project, "\\Symfony\\Component\\Console\\Command\\InvokableCommand")) {
7070
String normalizedNamespace = "\\" + org.apache.commons.lang3.StringUtils.strip(namespace, "\\") + "\\";
7171
Collection<PhpClass> commandClasses = PhpIndexUtil.getPhpClassInsideNamespace(project, normalizedNamespace);
7272

@@ -90,7 +90,7 @@ public static String guessCommandTemplateType(@NotNull Project project, @NotNull
9090
}
9191
}
9292

93-
if (PhpElementsUtil.getClassInterface(project, "\\Symfony\\Component\\Console\\Attribute\\AsCommand") != null) {
93+
if (PhpElementsUtil.hasClassOrInterface(project, "\\Symfony\\Component\\Console\\Attribute\\AsCommand")) {
9494
return "command_attributes";
9595
}
9696

@@ -106,7 +106,7 @@ public static String guessCommandTemplateType(@NotNull Project project, @NotNull
106106
}
107107

108108
public static String guessControllerTemplateType(@NotNull Project project) {
109-
if (PhpElementsUtil.getClassInterface(project, "\\Symfony\\Component\\Routing\\Attribute\\Route") != null) {
109+
if (PhpElementsUtil.hasClassOrInterface(project, "\\Symfony\\Component\\Routing\\Attribute\\Route")) {
110110
return "controller_attributes";
111111
}
112112

src/main/java/fr/adrienbrault/idea/symfony2plugin/action/NewTwigExtensionAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void actionPerformed(@NotNull AnActionEvent event) {
8989
@NotNull
9090
private static String detectTemplate(@NotNull Project project) {
9191
// If attributes are available, use the new attribute-based template
92-
if (PhpElementsUtil.getClassInterface(project, "\\Twig\\Attribute\\AsTwigFunction") != null) {
92+
if (PhpElementsUtil.hasClassOrInterface(project, "\\Twig\\Attribute\\AsTwigFunction")) {
9393
return "twig_extension_function_attribute";
9494
}
9595

src/main/java/fr/adrienbrault/idea/symfony2plugin/completion/PhpAttributeCompletionContributor.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private Collection<LookupElement> getControllerCompletions(@NotNull Project proj
9595
Collection<LookupElement> lookupElements = new ArrayList<>();
9696

9797
// Add Route attribute completion
98-
if (PhpElementsUtil.getClassInterface(project, ROUTE_ATTRIBUTE_FQN) != null) {
98+
if (PhpElementsUtil.hasClassOrInterface(project, ROUTE_ATTRIBUTE_FQN)) {
9999
LookupElement routeLookupElement = LookupElementBuilder
100100
.create("#[Route]")
101101
.withIcon(Symfony2Icons.SYMFONY_ATTRIBUTE)
@@ -107,7 +107,7 @@ private Collection<LookupElement> getControllerCompletions(@NotNull Project proj
107107
}
108108

109109
// Add IsGranted attribute completion
110-
if (PhpElementsUtil.getClassInterface(project, IS_GRANTED_ATTRIBUTE_FQN) != null) {
110+
if (PhpElementsUtil.hasClassOrInterface(project, IS_GRANTED_ATTRIBUTE_FQN)) {
111111
LookupElement isGrantedLookupElement = LookupElementBuilder
112112
.create("#[IsGranted]")
113113
.withIcon(Symfony2Icons.SYMFONY_ATTRIBUTE)
@@ -119,7 +119,7 @@ private Collection<LookupElement> getControllerCompletions(@NotNull Project proj
119119
}
120120

121121
// Add Cache attribute completion
122-
if (PhpElementsUtil.getClassInterface(project, CACHE_ATTRIBUTE_FQN) != null) {
122+
if (PhpElementsUtil.hasClassOrInterface(project, CACHE_ATTRIBUTE_FQN)) {
123123
LookupElement cacheLookupElement = LookupElementBuilder
124124
.create("#[Cache]")
125125
.withIcon(Symfony2Icons.SYMFONY_ATTRIBUTE)
@@ -137,7 +137,7 @@ private Collection<LookupElement> getTwigExtensionCompletions(@NotNull Project p
137137
Collection<LookupElement> lookupElements = new ArrayList<>();
138138

139139
// Add AsTwigFilter attribute completion
140-
if (PhpElementsUtil.getClassInterface(project, AS_TWIG_FILTER_ATTRIBUTE_FQN) != null) {
140+
if (PhpElementsUtil.hasClassOrInterface(project, AS_TWIG_FILTER_ATTRIBUTE_FQN)) {
141141
LookupElement lookupElement = LookupElementBuilder
142142
.create("#[AsTwigFilter]")
143143
.withIcon(Symfony2Icons.SYMFONY_ATTRIBUTE)
@@ -149,7 +149,7 @@ private Collection<LookupElement> getTwigExtensionCompletions(@NotNull Project p
149149
}
150150

151151
// Add AsTwigFunction attribute completion
152-
if (PhpElementsUtil.getClassInterface(project, AS_TWIG_FUNCTION_ATTRIBUTE_FQN) != null) {
152+
if (PhpElementsUtil.hasClassOrInterface(project, AS_TWIG_FUNCTION_ATTRIBUTE_FQN)) {
153153
LookupElement lookupElement = LookupElementBuilder
154154
.create("#[AsTwigFunction]")
155155
.withIcon(Symfony2Icons.SYMFONY_ATTRIBUTE)
@@ -161,7 +161,7 @@ private Collection<LookupElement> getTwigExtensionCompletions(@NotNull Project p
161161
}
162162

163163
// Add AsTwigTest attribute completion
164-
if (PhpElementsUtil.getClassInterface(project, AS_TWIG_TEST_ATTRIBUTE_FQN) != null) {
164+
if (PhpElementsUtil.hasClassOrInterface(project, AS_TWIG_TEST_ATTRIBUTE_FQN)) {
165165
LookupElement lookupElement = LookupElementBuilder
166166
.create("#[AsTwigTest]")
167167
.withIcon(Symfony2Icons.SYMFONY_ATTRIBUTE)

src/main/java/fr/adrienbrault/idea/symfony2plugin/completion/ServicePropertyInsertUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public static List<String> getInjectionService(@NotNull Project project, @NotNul
6262
for (String property : propertyNameFind) {
6363
if (alias.containsKey(property.toLowerCase())) {
6464
String key = property.toLowerCase();
65-
if (!PhpIndex.getInstance(project).getAnyByFQN(alias.get(key)).isEmpty()) {
65+
if (PhpElementsUtil.hasClassOrInterface(project, alias.get(key))) {
6666
String fqn = alias.get(key);
6767
servicesMatch.put(fqn, new Match(fqn, 4));
6868
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/inspection/YamlClassInspection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private void invoke(@NotNull final PsiElement psiElement, @NotNull ProblemsHolde
6666

6767
if (YamlHelper.isValidParameterName(className)) {
6868
String resolvedParameter = ContainerCollectionResolver.resolveParameter(project, className);
69-
if (resolvedParameter != null && !PhpIndex.getInstance(project).getAnyByFQN(resolvedParameter).isEmpty()) {
69+
if (resolvedParameter != null && PhpElementsUtil.hasClassOrInterface(project, resolvedParameter)) {
7070
return;
7171
}
7272
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/intentions/php/AddRouteAttributeIntention.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull Psi
110110
return false;
111111
}
112112

113-
if (PhpElementsUtil.getClassInterface(project, ROUTE_ATTRIBUTE_CLASS) == null) {
113+
if (!PhpElementsUtil.hasClassOrInterface(project, ROUTE_ATTRIBUTE_CLASS)) {
114114
return false;
115115
}
116116

src/main/java/fr/adrienbrault/idea/symfony2plugin/intentions/php/CommandToInvokableIntention.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull Psi
163163
}
164164

165165
// check if feature exists
166-
if (PhpElementsUtil.getClassInterface(project, "Symfony\\Component\\Console\\Command\\InvokableCommand") == null) {
166+
if (!PhpElementsUtil.hasClassOrInterface(project, "\\Symfony\\Component\\Console\\Command\\InvokableCommand")) {
167167
return false;
168168
}
169169

src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.intellij.lang.ASTNode;
55
import com.intellij.openapi.editor.Document;
66
import com.intellij.openapi.project.Project;
7+
import com.intellij.openapi.util.Key;
78
import com.intellij.openapi.util.Ref;
89
import com.intellij.patterns.ElementPattern;
910
import com.intellij.patterns.PatternCondition;
@@ -12,6 +13,10 @@
1213
import com.intellij.psi.*;
1314
import com.intellij.psi.codeStyle.CodeStyleManager;
1415
import com.intellij.psi.formatter.FormatterUtil;
16+
import com.intellij.psi.util.CachedValue;
17+
import com.intellij.psi.util.CachedValueProvider;
18+
import com.intellij.psi.util.CachedValuesManager;
19+
import com.intellij.psi.util.PsiModificationTracker;
1520
import com.intellij.psi.util.PsiTreeUtil;
1621
import com.intellij.util.ProcessingContext;
1722
import com.intellij.util.Processor;
@@ -55,13 +60,19 @@
5560
import org.jetbrains.annotations.Nullable;
5661

5762
import java.util.*;
63+
import java.util.concurrent.ConcurrentHashMap;
5864
import java.util.stream.Collectors;
5965

6066
/**
6167
* @author Daniel Espendiller <daniel@espendiller.net>
6268
*/
6369
public class PhpElementsUtil {
6470

71+
/**
72+
* Cache for class/interface existence checks to avoid repeated PhpIndex queries
73+
*/
74+
private static final Key<CachedValue<Map<String, Boolean>>> CLASS_EXISTS_CACHE = new Key<>("SYMFONY_PHP_CLASS_EXISTS_CACHE");
75+
6576
/**
6677
* Only parameter on first index or named: "a('caret'), a(test: 'caret')"
6778
*/
@@ -859,6 +870,24 @@ static public PhpClass getClassInterface(Project project, @NotNull String classN
859870
return phpClasses.isEmpty() ? null : phpClasses.iterator().next();
860871
}
861872

873+
static public boolean hasClassOrInterface(@NotNull Project project, @NotNull String classFqnName) {
874+
// Get or create the cached map
875+
Map<String, Boolean> cache = CachedValuesManager.getManager(project).getCachedValue(
876+
project,
877+
CLASS_EXISTS_CACHE,
878+
() -> CachedValueProvider.Result.create(
879+
new ConcurrentHashMap<>(),
880+
PsiModificationTracker.MODIFICATION_COUNT
881+
),
882+
false
883+
);
884+
885+
// Check the cache first, compute and store if missing
886+
return cache.computeIfAbsent(classFqnName, fqn ->
887+
!PhpIndex.getInstance(project).getAnyByFQN(fqn).isEmpty()
888+
);
889+
}
890+
862891
/**
863892
* @param subjectClass eg DateTime
864893
* @param expectedClass eg DateTimeInterface

0 commit comments

Comments
 (0)