Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Middleware Dispatcher Laravel Package

yiisoft/middleware-dispatcher

A PSR-15 middleware dispatcher for Yii applications. Build and execute middleware pipelines with lazy resolution through the DI container, fallback request handlers, and support for individual middleware or grouped stacks. Integrates cleanly with Yii's request handling and the PSR-7/PSR-15 ecosystem...

Deep Wiki
Context7

Getting Started

composer require yiisoft/middleware-dispatcher

The package only gives you the dispatcher — you supply the ecosystem around it: a PSR-7/PSR-17 implementation, a PSR-11 container, and a terminal RequestHandlerInterface (your "kernel").

use Psr\Http\Server\RequestHandlerInterface;
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;

// $finalHandler runs when every middleware calls $handler->handle($request)
$dispatcher = new MiddlewareDispatcher($finalHandler, $container);

$dispatcher = $dispatcher->withMiddlewares([
    ErrorHandlerMiddleware::class,   // outermost — catches everything below
    CorsMiddleware::class,
    RouterMiddleware::class,
]);

$response = $dispatcher->handle($request);

Read in this order:

  1. MiddlewareDispatcher::handle() — ~20 lines, it's the whole pipeline. Wraps definitions in reverse so the first entry is outermost.
  2. MiddlewareFactory::createMiddleware() — tells you exactly which definition shapes are legal (instance, class-string, callable/closure, [class, method]) and how the container is consulted.
  3. MiddlewareDefinition / the definitions collection — the escape hatch when you need parameters or lazy construction.
  4. InvalidMiddlewareDefinitionException — the failure mode you'll actually hit.

First realistic use case: a BFF/API gateway or a Yii3 config/web/di.php-style bootstrap where you want a stack you can compose per route group and unit-test without a framework. Because it's plain PSR-15, the same stack runs under Yii3, Mezzio, Slim, or a Laravel Route:: shim.

Implementation Patterns

Build once, dispatch many. The dispatcher is immutable and definitions are lazy, but the object graph still gets walked on each handle(). Register the assembled dispatcher as a container singleton; never call withMiddlewares() inside a controller.

Class-string definitions = constructor injection. This is the main reason to use the package over a hand-rolled foreach. Definitions are resolved through the PSR-11 container, so AuthMiddleware(LoggerInterface $log, AuthConfig $cfg) just works under yiisoft/di or Laravel's container. Closures and MiddlewareInterface instances skip the factory entirely.

Per-route / per-group pipelines. Keep a array<string, list<definition>> map in config and build a dispatcher per group at bootstrap. Cheap, and each group gets a different terminal handler:

$apiV1 = (new MiddlewareDispatcher($apiKernel, $container))
    ->withMiddlewares($config['pipelines']['api_v1']);

Nesting via a composite handler. To get "group middleware then route middleware", write a tiny RequestHandlerInterface whose handle() calls an inner dispatcher. That gives you true nested pipelines with distinct error boundaries — hard to do with a flat stack.

Conditional/lazy middleware. Because instantiation happens only when the pipeline reaches that point, put RoutingMiddleware near the top and expensive middleware (auth, DB session) behind it. Anything after a short-circuiting middleware never gets constructed.

Reuse foreign middleware. PSR-15 means laminas, Slim, and Mezzio middleware drop straight in. For Laravel middleware you'll need an adapter that shims $next($request) onto $handler->handle($request) — write it once.

Testing. No framework needed: pass a stub terminal handler that returns a canned response, record the order middleware were entered/left, assert on it. This is the fastest way to catch ordering mistakes.

Gotchas and Tips

  • withMiddlewares() returns a clone. $dispatcher->withMiddlewares([...]); is a silent no-op. Always reassign. This is the single most common bug with this package.
  • Order is outermost-first. Entry #0 runs first on the way in and last on the way out. Error handling, CORS, and request-ID belong at the top; routing and 404 belong near the bottom.
  • Ordering vs. the terminal handler. The fallback handler is always innermost. If nothing short-circuits, it runs — make it an explicit 404 router, not a leftover.
  • No exception catching. The dispatcher does not wrap middleware in try/catch. Without your own error middleware at position 0, a thrown exception escapes handle() and any "return JSON on error" logic never fires.
  • Bad definitions fail at dispatch, not at boot. A class-string that implements neither MiddlewareInterface nor callable throws InvalidMiddlewareDefinitionException on the first request. Add a smoke test that dispatches each configured pipeline once in CI.
  • Container gaps. If a middleware isn't registered, the factory falls back to autowiring. With a non-autowiring container this fails at request time; with an autowiring one it may silently construct an unconfigured object. Register middleware explicitly when they have required scalars or config.
  • Don't keep per-request state on middleware properties. Container-managed middleware are effectively singletons across requests — same footgun as Laravel.
  • Debugging pipeline order: drop a LoggerInterface::debug() call before and after $handler->handle() in a scratch middleware; the nesting in the log is the real execution order, and it's the fastest way to spot a misordered stack.
  • API drift between majors. The add() / dispatch() era became withMiddlewares() / handle(). Check which major you're pinning before copying snippets from blog posts — most search results are for v1.
  • Middleware parameters. The plain array form doesn't carry config. If you need to pass arguments to a middleware's factory, use a MiddlewareDefinition (definition + parameters) rather than inventing wrapper classes.
  • PSR-17 needed transitively. The dispatcher itself barely creates responses, but almost every middleware you'll add does — pull in a response factory and register it in the container or you'll get confusing null returns deep in the stack.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console