Skip to content

provide support for compiled Symfony 4 / 5 route names #1438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 5, 2020
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 @@ -30,6 +30,7 @@ public class Settings implements PersistentStateComponent<Settings> {
"var/cache/dev/appDevDebugProjectContainer.xml",
"var/cache/dev/srcDevDebugProjectContainer.xml",
"var/cache/dev/srcApp_KernelDevDebugContainer.xml",
"var/cache/dev/App_KernelDevDebugContainer.xml" // Symfony => 4 + flex
};

// Default Symfony 2, 3 and 4 paths
Expand All @@ -38,6 +39,7 @@ public class Settings implements PersistentStateComponent<Settings> {
"var/cache/dev/appDevUrlGenerator.php",
"var/cache/dev/appDevDebugProjectContainerUrlGenerator.php",
"var/cache/dev/srcDevDebugProjectContainerUrlGenerator.php",
"var/cache/dev/url_matching_routes.php", // Symfony >= 4
};

public static String DEFAULT_TRANSLATION_PATH = "app/cache/dev/translations";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,6 @@ public static ControllerClassOnShortcutReturn getControllerClassOnShortcut(@NotN
return null;
}

private static <E> ArrayList<E> makeCollection(Iterable<E> iter) {
ArrayList<E> list = new ArrayList<>();
for (E item : iter) {
list.add(item);
}
return list;
}

private static String getPath(Project project, String path) {
if (!FileUtil.isAbsolute(path)) { // Project relative path
path = project.getBasePath() + "/" + path;
Expand Down Expand Up @@ -344,7 +336,6 @@ public static Map<String, Route> getRoutesInsideUrlGeneratorFile(@NotNull Projec
return getRoutesInsideUrlGeneratorFile(psiFile);
}


/**
* Temporary or remote files dont support "isInstanceOf", check for string implementation first
*/
Expand All @@ -368,9 +359,30 @@ private static boolean isRouteClass(@NotNull PhpClass phpClass) {

@NotNull
public static Map<String, Route> getRoutesInsideUrlGeneratorFile(@NotNull PsiFile psiFile) {

Map<String, Route> routes = new HashMap<>();

// Symfony >= 4
// extract the routes on a return statement
// return [['route'] => [...]]
for (PhpReturn phpReturn : PsiTreeUtil.findChildrenOfType(psiFile, PhpReturn.class)) {
PsiElement argument = phpReturn.getArgument();
if (!(argument instanceof ArrayCreationExpression)) {
continue;
}

// get only the inside arrays
// [[..], [..]] => [..], [..]
for (Map.Entry<String, PsiElement> routeArray : PhpElementsUtil.getArrayKeyValueMapWithValueAsPsiElement((ArrayCreationExpression) argument).entrySet()) {
List<ArrayCreationExpression> routeArrayOptions = new ArrayList<>();
for (PhpPsiElement routeOption : PsiTreeUtil.getChildrenOfTypeAsList(routeArray.getValue(), PhpPsiElement.class)) {
routeArrayOptions.add(PsiTreeUtil.getChildOfType(routeOption, ArrayCreationExpression.class));
}

routes.put(routeArray.getKey(), convertRouteConfigForReturnArray(routeArray.getKey(), routeArrayOptions));
}
}

// Symfony < 4
// heavy stuff here, to get nested routing array :)
// list($variables, $defaults, $requirements, $tokens, $hostTokens)
Collection<PhpClass> phpClasses = PsiTreeUtil.findChildrenOfType(psiFile, PhpClass.class);
Expand Down Expand Up @@ -454,9 +466,60 @@ private static void collectRoutesOnArrayCreation(@NotNull Map<String, Route> rou
}
}

/**
* Used in Symfony > 4 where routes are wrapped into a return array
*/
@NotNull
private static Route convertRouteConfigForReturnArray(@NotNull String routeName, @NotNull List<ArrayCreationExpression> hashElementCollection) {
Set<String> variables = new HashSet<>();
if(hashElementCollection.size() >= 1 && hashElementCollection.get(0) != null) {
ArrayCreationExpression value = hashElementCollection.get(0);
if(value != null) {
variables.addAll(PhpElementsUtil.getArrayValuesAsString(value));
}
}

Map<String, String> defaults = new HashMap<>();
if(hashElementCollection.size() >= 2 && hashElementCollection.get(1) != null) {
ArrayCreationExpression value = hashElementCollection.get(1);
if(value != null) {
defaults = PhpElementsUtil.getArrayKeyValueMap(value);
}
}

Map<String, String>requirements = new HashMap<>();
if(hashElementCollection.size() >= 3 && hashElementCollection.get(2) != null) {
ArrayCreationExpression value = hashElementCollection.get(2);
if(value != null) {
requirements = PhpElementsUtil.getArrayKeyValueMap(value);
}
}

List<Collection<String>> tokens = new ArrayList<>();
if(hashElementCollection.size() >= 4 && hashElementCollection.get(3) != null) {
ArrayCreationExpression tokenArray = hashElementCollection.get(3);
if(tokenArray != null) {
for(ArrayHashElement tokenArrayConfig: tokenArray.getHashElements()) {
if(tokenArrayConfig.getValue() instanceof ArrayCreationExpression) {
Map<String, String> arrayKeyValueMap = PhpElementsUtil.getArrayKeyValueMap((ArrayCreationExpression) tokenArrayConfig.getValue());
tokens.add(arrayKeyValueMap.values());
}
}
}

}

// hostTokens = 4 need them?
return new Route(routeName, variables, defaults, requirements, tokens);
}

/**
* Used in Symfony < 4 where routes are wrapped into a class
*/
@NotNull
private static Route convertRouteConfig(@NotNull String routeName, @NotNull ArrayCreationExpression hashValue) {
List<ArrayHashElement> hashElementCollection = makeCollection(hashValue.getHashElements());
List<ArrayHashElement> hashElementCollection = new ArrayList<>();
hashValue.getHashElements().forEach(hashElementCollection::add);

Set<String> variables = new HashSet<>();
if(hashElementCollection.size() >= 1 && hashElementCollection.get(0).getValue() instanceof ArrayCreationExpression) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,43 @@ static public Map<String, PsiElement> getArrayValuesAsMap(@NotNull ArrayCreation
Map<String, PsiElement> keys = new HashMap<>();
for (PsiElement child : arrayValues) {
String stringValue = PhpElementsUtil.getStringValue(child.getFirstChild());
if(stringValue != null && StringUtils.isNotBlank(stringValue)) {
if(StringUtils.isNotBlank(stringValue)) {
keys.put(stringValue, child);
}
}

return keys;
}

/**
* array('foo' => FOO.class, 'foo1' => 'bar', 1 => 'foo')
*/
@NotNull
static public Map<String, PsiElement> getArrayKeyValueMapWithValueAsPsiElement(@NotNull ArrayCreationExpression arrayCreationExpression) {
HashMap<String, PsiElement> keys = new HashMap<>();

for(ArrayHashElement arrayHashElement: arrayCreationExpression.getHashElements()) {
PhpPsiElement child = arrayHashElement.getKey();
if(child != null && ((child instanceof StringLiteralExpression) || PhpPatterns.psiElement(PhpElementTypes.NUMBER).accepts(child))) {

String key;
if(child instanceof StringLiteralExpression) {
key = ((StringLiteralExpression) child).getContents();
} else {
key = child.getText();
}

if(key == null || StringUtils.isBlank(key)) {
continue;
}

keys.put(key, arrayHashElement.getValue());
}
}

return keys;
}

/**
* array('foo' => 'bar', 'foo1' => 'bar', 1 => 'foo')
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,23 @@ public void testGetRoutesInsideUrlGeneratorFile() {
assertNull(routes.get("_assetic_91dd2a8"));
}

/**
* @see fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper#getRoutesInsideUrlGeneratorFile
*/
public void testGetRoutesInsideUrlGeneratorFileUrlGeneratorRoutes() {
Map<String, Route> routes = RouteHelper.getRoutesInsideUrlGeneratorFile(getProject(), myFixture.copyFileToProject("url_generating_routes.php"));

Route previewError = routes.get("_preview_error");
assertEquals("error_controller::preview", previewError.getController());
assertContainsElements(previewError.getDefaults().keySet(), "_format", "_controller");
assertContainsElements(previewError.getRequirements().keySet(), "code");
assertContainsElements(previewError.getVariables(), "code", "_format");

Route profiler = routes.get("_profiler");
assertEquals("web_profiler.controller.profiler::panelAction", profiler.getController());
assertContainsElements(profiler.getVariables(), "token");
}

/**
* @see fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper#convertMethodToRouteShortcutControllerName
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

// This file has been auto-generated by the Symfony Routing Component.

return [
'_preview_error' => [
['code', '_format'],
['_controller' => 'error_controller::preview', '_format' => 'html'],
['code' => '\\d+'],
[
['variable', '.', '[^/]++', '_format', true],
['variable', '/', '\\d+', 'code', true],
['text', '/_error']
],
[],
[]
],
'_wdt' => [['token'], ['_controller' => 'web_profiler.controller.profiler::toolbarAction'], [], [['variable', '/', '[^/]++', 'token', true], ['text', '/_wdt']], [], []],
'_profiler_home' => [[], ['_controller' => 'web_profiler.controller.profiler::homeAction'], [], [['text', '/_profiler/']], [], []],
'_profiler_search' => [[], ['_controller' => 'web_profiler.controller.profiler::searchAction'], [], [['text', '/_profiler/search']], [], []],
'_profiler_search_bar' => [[], ['_controller' => 'web_profiler.controller.profiler::searchBarAction'], [], [['text', '/_profiler/search_bar']], [], []],
'_profiler_phpinfo' => [[], ['_controller' => 'web_profiler.controller.profiler::phpinfoAction'], [], [['text', '/_profiler/phpinfo']], [], []],
'_profiler_search_results' => [['token'], ['_controller' => 'web_profiler.controller.profiler::searchResultsAction'], [], [['text', '/search/results'], ['variable', '/', '[^/]++', 'token', true], ['text', '/_profiler']], [], []],
'_profiler_open_file' => [[], ['_controller' => 'web_profiler.controller.profiler::openAction'], [], [['text', '/_profiler/open']], [], []],
'_profiler' => [
['token'],
['_controller' => 'web_profiler.controller.profiler::panelAction'],
[],
[
['variable', '/', '[^/]++', 'token', true],
['text', '/_profiler']
],
[],
[]
],
'_profiler_router' => [['token'], ['_controller' => 'web_profiler.controller.router::panelAction'], [], [['text', '/router'], ['variable', '/', '[^/]++', 'token', true], ['text', '/_profiler']], [], []],
'_profiler_exception' => [['token'], ['_controller' => 'web_profiler.controller.exception_panel::body'], [], [['text', '/exception'], ['variable', '/', '[^/]++', 'token', true], ['text', '/_profiler']], [], []],
'_profiler_exception_css' => [['token'], ['_controller' => 'web_profiler.controller.exception_panel::stylesheet'], [], [['text', '/exception.css'], ['variable', '/', '[^/]++', 'token', true], ['text', '/_profiler']], [], []],
];