feat(router): Add fallback for legacy action and file routes

Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
This commit is contained in:
Côme Chilliet 2025-05-14 11:47:46 +02:00
parent 216da3a81c
commit 446b22ac3c
No known key found for this signature in database
GPG key ID: A3E2F658B28C760A
2 changed files with 81 additions and 15 deletions

View file

@ -23,6 +23,8 @@ use Symfony\Component\Routing\RouteCollection;
class CachingRouter extends Router {
protected ICache $cache;
protected array $legacyCreatedRoutes = [];
public function __construct(
ICacheFactory $cacheFactory,
LoggerInterface $logger,
@ -104,4 +106,52 @@ class CachingRouter extends Router {
$this->eventLogger->end('cacheroute:match');
return $parameters;
}
/**
* @param array{action:mixed, ...} $parameters
*/
protected function callLegacyActionRoute(array $parameters): void {
/*
* Closures cannot be serialized to cache, so for legacy routes calling an action we have to include the routes.php file again
*/
$app = $parameters['app'];
$this->useCollection($app);
parent::requireRouteFile($parameters['route-file'], $app);
$collection = $this->getCollection($app);
$parameters['action'] = $collection->get($parameters['_route'])?->getDefault('action');
parent::callLegacyActionRoute($parameters);
}
/**
* Create a \OC\Route\Route.
* Deprecated
*
* @param string $name Name of the route to create.
* @param string $pattern The pattern to match
* @param array $defaults An array of default parameter values
* @param array $requirements An array of requirements for parameters (regexes)
*/
public function create($name, $pattern, array $defaults = [], array $requirements = []): Route {
$this->legacyCreatedRoutes[] = $name;
return parent::create($name, $pattern, $defaults, $requirements);
}
/**
* Require a routes.php file
*/
protected function requireRouteFile(string $file, string $appName): void {
$this->legacyCreatedRoutes = [];
parent::requireRouteFile($file, $appName);
foreach ($this->legacyCreatedRoutes as $routeName) {
$route = $this->collection?->get($routeName);
if ($route === null) {
/* Should never happen */
throw new \Exception("Could not find route $routeName");
}
if ($route->hasDefault('action')) {
$route->setDefault('route-file', $file);
$route->setDefault('app', $appName);
}
}
}
}

View file

@ -312,27 +312,43 @@ class Router implements IRouter {
$application = $this->getApplicationClass($caller[0]);
\OC\AppFramework\App::main($caller[1], $caller[2], $application->getContainer(), $parameters);
} elseif (isset($parameters['action'])) {
$action = $parameters['action'];
if (!is_callable($action)) {
throw new \Exception('not a callable action');
}
unset($parameters['action']);
unset($parameters['caller']);
$this->eventLogger->start('route:run:call', 'Run callable route');
call_user_func($action, $parameters);
$this->eventLogger->end('route:run:call');
$this->logger->warning('Deprecated action route used', ['parameters' => $parameters]);
$this->callLegacyActionRoute($parameters);
} elseif (isset($parameters['file'])) {
$param = $parameters;
unset($param['_route']);
$_GET = array_merge($_GET, $param);
unset($param);
include $parameters['file'];
$this->logger->debug('Deprecated file route used', ['parameters' => $parameters]);
$this->includeLegacyFileRoute($parameters);
} else {
throw new \Exception('no action available');
}
$this->eventLogger->end('route:run');
}
/**
* @param array{file:mixed, ...} $parameters
*/
protected function includeLegacyFileRoute(array $parameters): void {
$param = $parameters;
unset($param['_route']);
$_GET = array_merge($_GET, $param);
unset($param);
require_once $parameters['file'];
}
/**
* @param array{action:mixed, ...} $parameters
*/
protected function callLegacyActionRoute(array $parameters): void {
$action = $parameters['action'];
if (!is_callable($action)) {
throw new \Exception('not a callable action');
}
unset($parameters['action']);
unset($parameters['caller']);
$this->eventLogger->start('route:run:call', 'Run callable route');
call_user_func($action, $parameters);
$this->eventLogger->end('route:run:call');
}
/**
* Get the url generator
*
@ -496,7 +512,7 @@ class Router implements IRouter {
* @param string $file the route file location to include
* @param string $appName
*/
private function requireRouteFile($file, $appName) {
protected function requireRouteFile(string $file, string $appName): void {
$this->setupRoutes(include $file, $appName);
}