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.

 
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 Router extends Symfony's UrlMatcher. Routes are collected from
YAML files (module.routing.yml) and route subscribers, compiled to a
RouteCollection that is cached in cache.data.
Route sources
module.routing.yml
RouteSubscriber (dynamic routes)
Route providers (e.g. entity routes)
Route attributes set
_controller, _title, _access
_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.
 
Controller returns…
 
 
Response object
Symfony handles directly
Any 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 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.

Next: Phase ③ VIEW Event & Main Content Renderers →