match() and ->matchRequest()). * * Ideally, the Router is solely instantiated and accessed via the Bootstrap, the RequestHandler and the UriBuilder. * * See \TYPO3\CMS\Backend\Http\RequestHandler for more details on route matching() and Bootstrap->initializeBackendRouting(). * * The architecture is inspired by the Symfony Routing Component. */ #[Autoconfigure( public: true, configurator: '@' . RouterConfigurator::class, )] class Router { /** * All routes used in the TYPO3 Backend */ protected RouteCollection $routeCollection; public function __construct( protected readonly RequestContextFactory $requestContextFactory, protected readonly BackendEntryPointResolver $backendEntryPointResolver, ) { $this->routeCollection = new RouteCollection(); } /** * Adds a new route with a given identifier */ public function addRoute(string $routeIdentifier, Route $route, array $aliases = []): void { $symfonyRoute = new SymfonyRoute($route->getPath(), [], [], $route->getOptions()); $symfonyRoute->setMethods($route->getMethods()); $this->routeCollection->add($routeIdentifier, $symfonyRoute); foreach ($aliases as $aliasName) { $this->routeCollection->addAlias($aliasName, $routeIdentifier); } } public function addRouteCollection(RouteCollection $routeCollection): void { $this->routeCollection->addCollection($routeCollection); } /** * Fetch all registered routes, only use in UriBuilder. Does not care about aliases, * so be careful with using this method. */ public function getRoutes(): \Iterator { return $this->routeCollection->getIterator(); } public function hasRoute(string $routeName): bool { return $this->routeCollection->get($routeName) !== null; } /** * Returns a route by its identifier, or null if nothing found. * * @return SymfonyRoute|Route|null */ public function getRoute(string $routeName) { return $this->routeCollection->get($routeName); } /** * @internal only use in Core, this should not be exposed */ public function getRouteCollection(): RouteCollection { return $this->routeCollection; } /** * Tries to match a URL path with a set of routes. * * @param string $pathInfo The path info to be parsed * @return Route the first Route object found * @throws ResourceNotFoundException If the resource could not be found */ public function match($pathInfo): Route { foreach ($this->routeCollection->getIterator() as $routeIdentifier => $route) { // This check is done in a simple way as there are no parameters yet (get parameters only) if ($route->getPath() === $pathInfo) { return Route::fromSymfonyRoute($route, $routeIdentifier); } } throw new ResourceNotFoundException('The requested resource "' . $pathInfo . '" was not found.', 1425389240); } /** * Matches a PSR-7 Request and returns a RouteResult with parameters and the resolved route. */ public function matchResult(ServerRequestInterface $request): RouteResult { $path = $this->backendEntryPointResolver->getBackendRoutePath($request); if ($path === null) { throw new ResourceNotFoundException('The requested resource "' . $request->getUri()->getPath() . '" does not contain a known backend route prefix.', 1704787661); } if ($path === '' || $path === '/' || $path === '/index.php') { // Allow the login page to be displayed if routing is not used and on index.php // (consolidate RouteDispatcher::evaluateReferrer() when changing 'login' to something different) $path = '/login'; } $requestContext = $this->requestContextFactory->fromBackendRequest($request); try { $result = (new UrlMatcher($this->routeCollection, $requestContext))->match($path); $matchedSymfonyRoute = $this->routeCollection->get($result['_route']); if ($matchedSymfonyRoute === null) { throw new ResourceNotFoundException('The requested resource "' . $path . '" was not found.', 1607596900); } } catch (\Symfony\Component\Routing\Exception\MethodNotAllowedException $e) { throw new MethodNotAllowedException($e->getAllowedMethods(), 1612649842, $e); } catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { throw new ResourceNotFoundException('The requested resource "' . $path . '" was not found.', 1612649840); } $route = Route::fromSymfonyRoute($matchedSymfonyRoute, $result['_route']); unset($result['_route']); return new RouteResult($route, $result); } /** * Tries to match a URI against the registered routes. * Use ->matchResult() instead, as this method will be deprecated in the future. * * @return Route the first Route object found */ public function matchRequest(ServerRequestInterface $request): Route { return $this->matchResult($request)->getRoute(); } }