Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public static boolean isInGivenDirectoryScope(@NotNull AnActionEvent event, @Not

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

Expand All @@ -90,7 +90,7 @@ public static String guessCommandTemplateType(@NotNull Project project, @NotNull
}
}

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

Expand All @@ -106,7 +106,7 @@ public static String guessCommandTemplateType(@NotNull Project project, @NotNull
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void actionPerformed(@NotNull AnActionEvent event) {
@NotNull
private static String detectTemplate(@NotNull Project project) {
// If attributes are available, use the new attribute-based template
if (PhpElementsUtil.getClassInterface(project, "\\Twig\\Attribute\\AsTwigFunction") != null) {
if (PhpElementsUtil.hasClassOrInterface(project, "\\Twig\\Attribute\\AsTwigFunction")) {
return "twig_extension_function_attribute";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private Collection<LookupElement> getControllerCompletions(@NotNull Project proj
Collection<LookupElement> lookupElements = new ArrayList<>();

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

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

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

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

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

// Add AsTwigTest attribute completion
if (PhpElementsUtil.getClassInterface(project, AS_TWIG_TEST_ATTRIBUTE_FQN) != null) {
if (PhpElementsUtil.hasClassOrInterface(project, AS_TWIG_TEST_ATTRIBUTE_FQN)) {
LookupElement lookupElement = LookupElementBuilder
.create("#[AsTwigTest]")
.withIcon(Symfony2Icons.SYMFONY_ATTRIBUTE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static List<String> getInjectionService(@NotNull Project project, @NotNul
for (String property : propertyNameFind) {
if (alias.containsKey(property.toLowerCase())) {
String key = property.toLowerCase();
if (!PhpIndex.getInstance(project).getAnyByFQN(alias.get(key)).isEmpty()) {
if (PhpElementsUtil.hasClassOrInterface(project, alias.get(key))) {
String fqn = alias.get(key);
servicesMatch.put(fqn, new Match(fqn, 4));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private void invoke(@NotNull final PsiElement psiElement, @NotNull ProblemsHolde

if (YamlHelper.isValidParameterName(className)) {
String resolvedParameter = ContainerCollectionResolver.resolveParameter(project, className);
if (resolvedParameter != null && !PhpIndex.getInstance(project).getAnyByFQN(resolvedParameter).isEmpty()) {
if (resolvedParameter != null && PhpElementsUtil.hasClassOrInterface(project, resolvedParameter)) {
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull Psi
return false;
}

if (PhpElementsUtil.getClassInterface(project, ROUTE_ATTRIBUTE_CLASS) == null) {
if (!PhpElementsUtil.hasClassOrInterface(project, ROUTE_ATTRIBUTE_CLASS)) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull Psi
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PatternCondition;
Expand All @@ -12,6 +13,10 @@
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.formatter.FormatterUtil;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import com.intellij.util.Processor;
Expand Down Expand Up @@ -55,13 +60,19 @@
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class PhpElementsUtil {

/**
* Cache for class/interface existence checks to avoid repeated PhpIndex queries
*/
private static final Key<CachedValue<Map<String, Boolean>>> CLASS_EXISTS_CACHE = new Key<>("SYMFONY_PHP_CLASS_EXISTS_CACHE");

/**
* Only parameter on first index or named: "a('caret'), a(test: 'caret')"
*/
Expand Down Expand Up @@ -859,6 +870,24 @@ static public PhpClass getClassInterface(Project project, @NotNull String classN
return phpClasses.isEmpty() ? null : phpClasses.iterator().next();
}

static public boolean hasClassOrInterface(@NotNull Project project, @NotNull String classFqnName) {
// Get or create the cached map
Map<String, Boolean> cache = CachedValuesManager.getManager(project).getCachedValue(
project,
CLASS_EXISTS_CACHE,
() -> CachedValueProvider.Result.create(
new ConcurrentHashMap<>(),
PsiModificationTracker.MODIFICATION_COUNT
),
false
);

// Check the cache first, compute and store if missing
return cache.computeIfAbsent(classFqnName, fqn ->
!PhpIndex.getInstance(project).getAnyByFQN(fqn).isEmpty()
);
}

/**
* @param subjectClass eg DateTime
* @param expectedClass eg DateTimeInterface
Expand Down