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

Laminas Router Laravel Package

laminas/laminas-router

Laminas Router is a flexible PHP routing component for matching HTTP and console requests to handlers and generating URLs from route definitions. It supports tree routing, route assembly, and integration with laminas-mvc and Mezzio.

View on GitHub
Deep Wiki
Context7

Getting Started

Laminas Router is not a Laravel router and it does not pretend to be one. It is a standalone matching/assembling engine: you give it a Laminas\Http\Request and a tree of route objects, it gives you a RouteMatch (or null) plus the ability to reverse-generate URLs. There is no dispatcher, no controller resolution, no middleware, no DI magic.

Minimum setup:

composer require laminas/laminas-router
# only if you actually match real HTTP requests:
composer require laminas/laminas-http

laminas-http moved to require-dev in 3.15.0, so the README's "PSR-7 applications" claim is misleading — the shipped Http\* routes type-hint Laminas\Http\Request, not Psr\Http\Message\ServerRequestInterface. You will install and bridge that yourself.

First place to look, in order:

  1. docs.laminas.dev/laminas-router — the quickstart and the per-route "options" tables are the actual API reference.
  2. src/Http/ — Literal, Segment, Regex, Scheme, Method, Hostname, Wildcard, Placeholder. These are your equivalents of Route::get(), Route::domain(), where(), etc.
  3. src/Http/TreeRouteStack.php and src/Http/HttpRouteMatch.php — the two classes 90% of your code touches.

First use case — match a request and inspect the match:

use Laminas\Http\PhpEnvironment\Request;
use Laminas\Router\Http\Literal;
use Laminas\Router\Http\Segment;
use Laminas\Router\Http\TreeRouteStack;

$router = new TreeRouteStack();
$router->addRoute('home', new Literal([
    'route'    => '/',
    'defaults' => ['controller' => 'home'],
]));
$router->addRoute('user/view', new Segment([
    'route'       => '/user/:id',
    'constraints' => ['id' => '[0-9]+'],
    'defaults'    => ['controller' => 'user', 'action' => 'view'],
]));

$match = $router->match(new Request());

if ($match === null) {
    // 404 — you decide, the router does not throw
}

$match->getMatchedRouteName(); // "user/view"
$match->getParams();            // ['controller' => 'user', 'action' => 'view', 'id' => '42']

That $match->getParams() array is your hand-off point. In Laravel terms it is roughly "route action + route parameters", except you still have to turn 'controller' => 'user' into a callable.

Implementation Patterns

Config-array trees instead of fluent registration. SimpleRouteStack::addRoutes() accepts the same shape Laminas MVC uses, which makes routes portable to config files and cacheable:

$router->addRoutes([
    'api' => [
        'type'    => Segment::class,
        'options' => ['route' => '/api/:version', 'constraints' => ['version' => 'v\d+']],
        'child_routes' => [
            'users' => [
                'type'    => Literal::class,
                'options' => ['route' => '/users', 'defaults' => ['controller' => 'api.users']],
            ],
        ],
    ],
]);

Child routes are your route groups/prefixes. Note that a parent with children only matches a bare /api/v1 if you set 'may_terminate' => true in its options.

Constraints are mandatory hygiene. A bare Segment /:id will happily swallow /login, and because paths are matched as a tree, ordering matters (see Gotchas). For anything user-facing, always pair the segment with constraints, and use Literal for static paths.

Reverse routing via assemble() is the piece Laravel devs underuse. It gives you the same guarantee as route():

echo $router->assemble(
    ['id' => 42],
    ['name' => 'user/view']  // or ['name' => 'api/users', 'child_name' => ...]
);

Extra parameters are appended as a query string; missing required ones throw. The return is a Laminas\Uri object — cast to string. Cache the assembled URL list, or better, keep assemble() calls near the route definitions so the constraint regex and the generator stay in sync.

HTTP-aware routes are composable, not per-endpoint. Method (verb), Scheme (https), and Hostname (:sub.example.com) are leaf routes intended as children — Hostname + Segment mirrors Route::domain()->group(), and Method mirrors Route::post(). Build them as children of the path segment rather than duplicating paths per verb.

Dispatch is yours to write. The idiomatic integration is a thin middleware/controller of your own: resolve the router, call match(), store the RouteMatch as a request attribute, translate controller/action params into a callable via your container, then invoke it. In a Laravel app you'd bind the TreeRouteStack as a singleton in a service provider and hydrate it from a config/routes.php-style array — but if you're doing that, ask why the app isn't just using Laravel's router.

Custom route types. If you need e.g. a locale-prefixed route, implement Laminas\Router\Http\HttpRouteInterface (3.17+; Http\RouteInterface is deprecated) and register it with a RoutePluginManager so the config-array form can instantiate it by class name.

Gotchas and Tips

  • Your app must already be running PHP 8.2+. 8.1 was dropped in 3.16.0 and 8.0 in 3.12.0. Check composer.json before assuming it drops into an older legacy app.
  • "PSR-7" is aspirational. match() needs Laminas\Http\Request. If your stack is PSR-7 (Slim, Mezzio, plain middleware), you need a conversion layer or to construct a Laminas\Http\PhpEnvironment\Request from globals. This is the single most common integration snag.
  • Route order is reversed. Routes are evaluated last-registered-first (LIFO), with an optional priority argument to addRoute(). The $route->priority property is a declared-but-deprecated artefact (see 3.7.0/3.9.0) — prefer the argument, and don't read the property. Practical rule: register catch-alls like Wildcard/Placeholder before specific routes, or explicitly out-prioritise literals over segments. A greedy Segment registered last will shadow everything.
  • A miss is null, not an exception. No 404 is raised, no MethodNotAllowed is distinguished. If you want a 405, you must re-match with the method filter removed and compare, or add Method children and handle the null yourself.
  • assemble() and match() are not symmetric friends. Encoding of parameters, the trailing-slash behaviour of Segment, and whether the parent route's regex is reapplied can all surprise you. Write a test that round-trips every route name: match(assemble(params)) should return the same params.
  • Hostname must not contain scheme or port. The route definition's accepted tokens were tightened in 3.11.1; putting https:// in route is a silent no-match.
  • Everything is going @final. 3.18.0 added soft @final markers and 3.19.0 documents the v4.0 deprecations (Http\RouteInterface → HttpRouteInterface, RouteMatch → HttpRouteMatch; the old class name alias is already in place). Do not extend TreeRouteStack, Segment, or RouteMatch — compose them, and type-hint the new interface names so the v4 upgrade is a find-and-replace.
  • Debug tip: when a route unexpectedly doesn't match, split the problem — $route->match($request, $pathOffset) per route node, printing the returned RouteMatch and remaining $pathOffset. Nine times out of ten the offset isn't where you assumed, because a sibling route already consumed part of the path.
  • Adoption risk: 35 stars, no dependents listed, and a maintenance cadence dominated by Renovate bumps. Treat it as a stable-but-quiet dependency: pin the major, keep it behind a single wrapper class of your own, and be ready to swap in something like FastRoute or Symfony Routing if you need a dispatcher or active community support.
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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator