D11 Pipeline: Routing -> Controller Resolution
Phase ②
Routing & Controller Resolution
How Drupal matches an incoming request to a route, resolves and invokes the controller, and branches on the controller's return value.
← Phase ① HttpKernel::handle() / KernelEvents::REQUEST
Kernel Event
KernelEvents::REQUEST subscribers (in priority order)
Multiple subscribers fire before the controller. Key Drupal subscribers:
p512
EarlyRenderingControllerWrapperSubscriber
Wraps controller to detect & warn on premature rendering.
p300
RoutePreloader
Warms route collection cache for subsequent requests.
p50
DynamicPageCacheSubscriber
Checks Dynamic Page Cache. Returns cached response if hit → bypasses controller.
p33
AuthenticationSubscriber
Authenticates request using configured auth providers (cookie, basic_auth, etc.).
p32
AccessAwareRouter
Matches route and enforces access checking. Throws 403/404 as appropriate.
p28
LanguageRequestSubscriber / ContextualLinksSubscriber / etc.
Language negotiation, theme negotiation, maintenance-mode checks.
Routing
Router — Route Matching
Drupal's
YAML files (
Router extends Symfony's UrlMatcher. Routes are collected fromYAML files (
module.routing.yml) and route subscribers, compiled to aRouteCollection that is cached in cache.data.Route sources
module.routing.yml
RouteSubscriber (dynamic routes)
Route providers (e.g. entity routes)
RouteSubscriber (dynamic routes)
Route providers (e.g. entity routes)
Route attributes set
_controller, _title, _access
_route_name, _format
_theme, _node_operation_route
_route_name, _format
_theme, _node_operation_route
Controller Resolution
ControllerResolver + ArgumentResolver
ControllerResolver resolves the _controller route attribute to a callable(class::method, service:method, or closure).
ArgumentResolver (Symfony)uses argument value resolvers to inject typed arguments from the request.
a
Resolve controller callable
Supports
ClassName::method, service_id:method, static calls, closures.b
KernelEvents::CONTROLLER (subscribers may swap controller)
e.g.
EarlyRenderingControllerWrapperSubscriber wraps it here.c
Resolve arguments
Injects
Request, route parameters, entity upcasting (via ParamConverter / argument value resolvers).Execution
Controller is invoked
The controller performs its application logic — loads entities, queries data,
builds render arrays — and returns one of the types below.
builds render arrays — and returns one of the types below.
Controller returns…
Response object
Symfony handles directly
Any
Common cases: file downloads, redirects (
Symfony\Component\HttpFoundation\Response (or subclass) bypasses all Drupal render pipeline stages. Symfony dispatches KernelEvents::RESPONSE and sends immediately.Common cases: file downloads, redirects (
RedirectResponse), JSON APIs (JsonResponse), raw HTML (Response).Render array
KernelEvents::VIEW dispatched
HttpKernel fires the
→ continues to Phase ③
VIEW event when the controller result is not a Response. MainContentViewSubscriber (subscribed to this event) takes over and guarantees a Response will be produced.→ continues to Phase ③
Drupal 11 notes
D11 fully removes the legacy _legacy_access_check mechanism. Route access is enforced entirely via the AccessManager and typed access check services. Entity parameter upcasting now uses Symfony's typed ArgumentValueResolverInterface rather than the older ParamConverterManager approach. PHP 8.1 attribute-based routing is not yet standard but is under discussion for future releases.