Skip to content

Commit e135cf5

Browse files
authored
Merge pull request #1877 from Haehnchen/feature/constants-routing
#1705 support constants for route name and path via first level self owning
2 parents ef48a69 + 0107a3a commit e135cf5

File tree

3 files changed

+113
-8
lines changed

3 files changed

+113
-8
lines changed

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

+74-7
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
import com.intellij.psi.util.PsiTreeUtil;
88
import com.jetbrains.php.lang.lexer.PhpTokenTypes;
99
import com.jetbrains.php.lang.psi.PhpPsiUtil;
10-
import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression;
11-
import com.jetbrains.php.lang.psi.elements.ParameterList;
12-
import com.jetbrains.php.lang.psi.elements.PhpAttribute;
13-
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
10+
import com.jetbrains.php.lang.psi.elements.*;
1411
import com.jetbrains.php.lang.psi.stubs.indexes.expectedArguments.PhpExpectedFunctionArgument;
1512
import org.apache.commons.lang.StringUtils;
1613
import org.jetbrains.annotations.NotNull;
@@ -34,6 +31,8 @@ public static String getAttributeValueByNameAsString(@NotNull PhpAttribute attri
3431
if (StringUtils.isNotBlank(contents)) {
3532
return contents;
3633
}
34+
} else if(nextSibling instanceof ClassConstantReference) {
35+
return resolveLocalValue(attribute, (ClassConstantReference) nextSibling);
3736
}
3837

3938
return null;
@@ -64,9 +63,45 @@ public static String getAttributeValueByNameAsStringWithDefaultParameterFallback
6463
for (PhpAttribute.PhpAttributeArgument argument : attribute.getArguments()) {
6564
PhpExpectedFunctionArgument argument1 = argument.getArgument();
6665
if (argument1.getArgumentIndex() == 0) {
67-
String value = PsiElementUtils.trimQuote(argument1.getValue());
68-
if (StringUtils.isNotBlank(value)) {
69-
return value;
66+
67+
// hint: reference is a complete fake object lazily created; it is not reflect the real element :(
68+
// PhpPsiElementFactory.createPhpPsiFromText => com.jetbrains.php.lang.psi.stubs.indexes.expectedArguments.PhpExpectedFunctionClassConstantArgument.createReference
69+
@NotNull PsiElement namedElement = argument1.createReference(attribute.getProject());
70+
71+
if (namedElement instanceof StringLiteralExpression) {
72+
// we can trust the string representation here, looks to be right implemented
73+
String contents = ((StringLiteralExpression) namedElement).getContents();
74+
if (StringUtils.isNotBlank(contents)) {
75+
return contents;
76+
}
77+
} else if(namedElement instanceof ClassConstantReference) {
78+
// not working: we are in a dummy and temporary out-of-scope object
79+
// resolveLocalValue(attribute, (ClassConstantReference) nextSibling);
80+
PhpExpression classReference = ((ClassConstantReference) namedElement).getClassReference();
81+
if (classReference instanceof ClassReference) {
82+
PsiElement phpAttributesList = attribute.getParent();
83+
if (phpAttributesList instanceof PhpAttributesList) {
84+
PsiElement method = phpAttributesList.getParent();
85+
if (method instanceof Method) {
86+
PsiElement phpClass = method.getParent();
87+
// instead of normal "$this", "self", ... we are getting here the class name :(
88+
// so to check the owning scope compare it via the class name
89+
if (phpClass instanceof PhpClass && ((PhpClass) phpClass).getFQN().equals(((ClassReference) classReference).getFQN())) {
90+
String fieldName = ((ClassConstantReference) namedElement).getName();
91+
Field ownFieldByName = ((PhpClass) phpClass).findOwnFieldByName(fieldName, true);
92+
if (ownFieldByName != null) {
93+
PsiElement defaultValue = ownFieldByName.getDefaultValue();
94+
if (defaultValue instanceof StringLiteralExpression) {
95+
String contents = ((StringLiteralExpression) defaultValue).getContents();
96+
if (StringUtils.isNotBlank(contents)) {
97+
return contents;
98+
}
99+
}
100+
}
101+
}
102+
}
103+
}
104+
}
70105
}
71106
}
72107
}
@@ -104,4 +139,36 @@ private static PsiElementPattern.Capture<PsiElement> getAttributeColonPattern(St
104139
PhpTokenTypes.opCOLON
105140
).afterLeaf(PlatformPatterns.psiElement().withElementType(PhpTokenTypes.IDENTIFIER).withText(name));
106141
}
142+
143+
@Nullable
144+
private static String resolveLocalValue(@NotNull PhpAttribute attribute, @NotNull ClassConstantReference nextSibling) {
145+
PhpExpression classReference = nextSibling.getClassReference();
146+
if (classReference != null) {
147+
String name = classReference.getName();
148+
if (name != null && (name.equals("self") || name.equals("static"))) {
149+
PsiElement phpAttributesList = attribute.getParent();
150+
if (phpAttributesList instanceof PhpAttributesList) {
151+
PsiElement method = phpAttributesList.getParent();
152+
if (method instanceof Method) {
153+
PsiElement phpClass = method.getParent();
154+
if (phpClass instanceof PhpClass) {
155+
String fieldName = nextSibling.getName();
156+
Field ownFieldByName = ((PhpClass) phpClass).findOwnFieldByName(fieldName, true);
157+
if (ownFieldByName != null) {
158+
PsiElement defaultValue = ownFieldByName.getDefaultValue();
159+
if (defaultValue instanceof StringLiteralExpression) {
160+
String contents = ((StringLiteralExpression) defaultValue).getContents();
161+
if (StringUtils.isNotBlank(contents)) {
162+
return contents;
163+
}
164+
}
165+
}
166+
}
167+
}
168+
}
169+
}
170+
}
171+
172+
return null;
173+
}
107174
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/RoutesStubIndexTest.java

+9
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,15 @@ public void testThatPhp8AttributesMethodsAreInIndex() {
178178

179179
RouteInterface route5 = getFirstValue("prefix_home_default_parameter");
180180
assertEquals("/foo-attributes-default/edit/{id}", route5.getPath());
181+
182+
RouteInterface route6 = getFirstValue("attributes-names-foobar-const");
183+
assertEquals("/attributes-path-foobar", route6.getPath());
184+
185+
RouteInterface route7 = getFirstValue("attributes-default-as-const");
186+
assertEquals("/attributes-path-foobar", route7.getPath());
187+
188+
RouteInterface route8 = getFirstValue("attributesWithoutNameWithConstantsInMethods");
189+
assertEquals(0, route8.getMethods().size());
181190
}
182191

183192
@NotNull

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/fixtures/RoutesStubIndex.php

+30-1
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,15 @@ public function fooAction()
159159

160160
namespace AppBundle\My\Controller
161161
{
162+
163+
use Symfony\Component\HttpFoundation\Request;
162164
use Symfony\Component\Routing\Annotation\Route;
163165

164166
class DefaultController
165167
{
168+
private const FOOBAR = 'attributes-names-foobar-const';
169+
private const FOOBAR_URL = '/attributes-path-foobar';
170+
166171
/**
167172
* @Route("/", name="framework_extra_bundle_route")
168173
*/
@@ -180,10 +185,25 @@ public function attributesWithoutName()
180185
{
181186
}
182187

188+
#[Route('/attributesWithoutNameWithConstantsInMethods', name: 'attributesWithoutNameWithConstantsInMethods', methods: [Request::METHOD_GET])]
189+
public function attributesWithoutNameWithConstantsInMethods()
190+
{
191+
}
192+
183193
#[Route(path: '/attributes-path', name: 'attributes-names')]
184194
public function attributesPath()
185195
{
186196
}
197+
198+
#[Route(path: self::FOOBAR_URL, name: self::FOOBAR)]
199+
public function attributesPathFoo1()
200+
{
201+
}
202+
203+
#[Route(self::FOOBAR_URL, name: "attributes-default-as-const")]
204+
public function attributesPathFoo2()
205+
{
206+
}
187207
}
188208
}
189209

@@ -234,4 +254,13 @@ public function __construct(
234254
) {
235255
}
236256
}
237-
}
257+
}
258+
259+
namespace Symfony\Component\HttpFoundation
260+
{
261+
class Request
262+
{
263+
public const METHOD_GET = 'GET';
264+
}
265+
}
266+

0 commit comments

Comments
 (0)