Skip to content

Commit

Permalink
BUGFIX: Static compile attribute routes
Browse files Browse the repository at this point in the history
The necessary reflection data used to build the routes from
attributes is not available at (Production) runtime.
This is an issue in itself but not trivial to fix, therefore
we fix this here by using the `CompileStatic` attribute to
bring the necessary data over from compiletime.

Fixes: #3400
  • Loading branch information
kitsunet committed Oct 12, 2024
1 parent 7136a1f commit ce3d9f1
Showing 1 changed file with 35 additions and 10 deletions.
45 changes: 35 additions & 10 deletions Neos.Flow/Classes/Mvc/Routing/AttributeRoutesProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ public function __construct(
public function getRoutes(): Routes
{
$routes = [];
$annotatedClasses = $this->reflectionService->getClassesContainingMethodsAnnotatedWith(Flow\Route::class);

foreach ($annotatedClasses as $className) {
foreach (static::compileRoutesConfiguration($this->objectManager) as $className => $routesForClass) {
$includeClassName = false;
foreach ($this->classNames as $classNamePattern) {
if (fnmatch($classNamePattern, $className, FNM_NOESCAPE)) {
Expand All @@ -92,12 +90,37 @@ public function getRoutes(): Routes
continue;
}

$routes = [...$routes, ...$routesForClass];
}

$routes = array_map(static fn(array $routeConfiguration): Route => Route::fromConfiguration($routeConfiguration), $routes);
return Routes::create(...$routes);
}

/**
* @param ObjectManagerInterface $objectManager
* @return array<string, array<int, mixed>
* @throws InvalidActionNameException
* @throws InvalidControllerException
* @throws \Neos\Flow\Utility\Exception
* @throws \Neos\Utility\Exception\FilesException
* @throws \ReflectionException
*/
#[Flow\CompileStatic]

Check failure on line 109 in Neos.Flow/Classes/Mvc/Routing/AttributeRoutesProvider.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test static analysis (deps: highest)

PHPDoc tag @return has invalid value (array<string, array<int, mixed>): Unexpected token "@throws", expected '>' at offset 110
public static function compileRoutesConfiguration(ObjectManagerInterface $objectManager): array
{
$reflectionService = $objectManager->get(ReflectionService::class);

$routesByClassName = [];
$annotatedClasses = $reflectionService->getClassesContainingMethodsAnnotatedWith(Flow\Route::class);

foreach ($annotatedClasses as $className) {
if (!in_array(ActionController::class, class_parents($className), true)) {
throw new InvalidControllerException('TODO: Currently #[Flow\Route] is only supported for ActionController. See https://github.com/neos/flow-development-collection/issues/3335.');
}

$controllerObjectName = $this->objectManager->getCaseSensitiveObjectName($className);
$controllerPackageKey = $this->objectManager->getPackageKeyByObjectName($controllerObjectName);
$controllerObjectName = $objectManager->getCaseSensitiveObjectName($className);
$controllerPackageKey = $objectManager->getPackageKeyByObjectName($controllerObjectName);
$controllerPackageNamespace = str_replace('.', '\\', $controllerPackageKey);
if (!str_ends_with($className, 'Controller')) {
throw new InvalidControllerException('Only for controller classes');
Expand All @@ -109,17 +132,18 @@ public function getRoutes(): Routes
$controllerName = substr($localClassName, 11);
$subPackage = null;
} elseif (str_contains($localClassName, '\\Controller\\')) {
list($subPackage, $controllerName) = explode('\\Controller\\', $localClassName);
[$subPackage, $controllerName] = explode('\\Controller\\', $localClassName);
} else {
throw new InvalidControllerException('Unknown controller pattern');
}

$annotatedMethods = $this->reflectionService->getMethodsAnnotatedWith($className, Flow\Route::class);
$routesByClassName[$className] = [];
$annotatedMethods = $reflectionService->getMethodsAnnotatedWith($className, Flow\Route::class);
foreach ($annotatedMethods as $methodName) {
if (!str_ends_with($methodName, 'Action')) {
throw new InvalidActionNameException('Only for action methods');
}
$annotations = $this->reflectionService->getMethodAnnotations($className, $methodName, Flow\Route::class);
$annotations = $reflectionService->getMethodAnnotations($className, $methodName, Flow\Route::class);
foreach ($annotations as $annotation) {
if ($annotation instanceof Flow\Route) {
$controller = substr($controllerName, 0, -10);
Expand All @@ -139,11 +163,12 @@ public function getRoutes(): Routes
$annotation->defaults ?? []
)
];
$routes[] = Route::fromConfiguration($configuration);
$routesByClassName[$className][] = $configuration;
}
}
}
}
return Routes::create(...$routes);

return $routesByClassName;
}
}

0 comments on commit ce3d9f1

Please sign in to comment.