diff options
author | Côme Chilliet <come.chilliet@nextcloud.com> | 2025-05-14 11:47:46 +0200 |
---|---|---|
committer | Côme Chilliet <come.chilliet@nextcloud.com> | 2025-06-05 17:58:54 +0200 |
commit | 446b22ac3c2036d65a4ee76ddeb8d9b748619db3 (patch) | |
tree | 6db78fa8aaf4afdc512a3258ef7bf3ef857566a6 | |
parent | 216da3a81c900ba21a656d2e21de552377157d4e (diff) | |
download | nextcloud-server-446b22ac3c2036d65a4ee76ddeb8d9b748619db3.tar.gz nextcloud-server-446b22ac3c2036d65a4ee76ddeb8d9b748619db3.zip |
feat(router): Add fallback for legacy action and file routes
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
-rw-r--r-- | lib/private/Route/CachingRouter.php | 50 | ||||
-rw-r--r-- | lib/private/Route/Router.php | 46 |
2 files changed, 81 insertions, 15 deletions
diff --git a/lib/private/Route/CachingRouter.php b/lib/private/Route/CachingRouter.php index 66bdacc89a8..edfbdc99c3a 100644 --- a/lib/private/Route/CachingRouter.php +++ b/lib/private/Route/CachingRouter.php @@ -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); + } + } + } } diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php index b0c355dcbee..5d0f46d6268 100644 --- a/lib/private/Route/Router.php +++ b/lib/private/Route/Router.php @@ -312,21 +312,11 @@ 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'); } @@ -334,6 +324,32 @@ class Router implements IRouter { } /** + * @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 * * @return \Symfony\Component\Routing\Generator\UrlGenerator @@ -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); } |