Skip to content

fix: resolve named middleware lazily so registration order does not drop it - #334

Merged
mychidarko merged 1 commit into
v5.xfrom
fix/lazy-named-middleware
Oct 16, 2026
Merged

mychidarko merged 1 commit into
v5.xfrom
fix/lazy-named-middleware

Conversation

@mychidarko

Copy link
Copy Markdown
Member

Problem

Named middleware is looked up when the route is declared (Router::mapHandler() does static::$namedMiddleware[$name] ?? null). If registerMiddleware() / auth()->middleware() runs after the routes that reference it, the lookup returns null, the route is stored with no middleware at all, and there is no warning. The handler then runs unprotected.

$app->get('/clients/{id}', ['middleware' => 'auth.required', fn ($id) => ...]);
$app->registerMiddleware('auth.required', fn () => ...);   // too late → silently ignored

// GET /clients/42  →  handler runs, auth never called

The array form ('middleware' => ['auth.required']) is worse: it stores a null handler and invoke() dies with TypeError: stripos(): Argument #1 ($haystack) must be of type string, null given.

Reproduced on v5.0 and v3.12. Surfaced while triaging #327 — not the same bug as #327 (that one is already fixed on v5.x with tests), but it is the kind of ordering mistake that produces "auth works on some routes and not others" reports.

Fix

mapHandler() now goes through a new resolveMiddleware():

  • callables pass through unchanged
  • class names are wrapped exactly as before
  • named middleware is wrapped in a closure that looks the name up at request time, so registration order no longer matters
  • if the name is still unknown when the request hits the route, it throws RuntimeException("Middleware named 'x' not found…") instead of letting the handler run. Fail-closed on purpose: a missing auth middleware should be a 500, not an open route. Happy to downgrade to trigger_error if you'd rather match invoke()'s existing behaviour.

Behaviour for middleware registered before routes is unchanged (same callable, same params forwarded).

Tests

Two new tests in tests/middleware.test.php:

  • named middleware registered after the routes still runs — string form, array form and group form, all registered after the routes. Fails on v5.x (middleware never runs / TypeError), passes here.
  • a route using an unregistered named middleware fails closed — asserts the exception and that the handler did not run.

alchemy test: 63 passed (89 assertions). alchemy lint: clean.

@mychidarko
mychidarko merged commit 8b613c1 into v5.x Oct 16, 2026
20 checks passed
@mychidarko
mychidarko deleted the fix/lazy-named-middleware branch October 17, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant