webware / smf-legacy-router
RouterInterface for routing legacy SMF urls to middleware/request handlers.
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/webware/smf-legacy-router
Requires
- mezzio/mezzio-router: 3.13.0
Requires (Dev)
- laminas/laminas-diactoros: 3.2.0
- laminas/laminas-stratigility: 3.10.0
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^11.5.42
- roave/security-advisories: dev-master
- webware/coding-standard: ^0.1.0
This package is auto-updated.
Last update: 2026-02-15 05:05:12 UTC
README
A query-parameter-based RouterInterface implementation for routing requests to middleware/handlers based on $request->getQueryParams(). Fully compatible with mezzio/mezzio-router and laminas/laminas-stratigility.
Features
- Query Parameter Matching: Routes match based on path + required query parameter keys (case-sensitive)
- HTTP Method Support: Optional HTTP method filtering (GET, POST, etc.) or match any method
- Duplicate Detection: Prevents conflicting routes with the same path + query params + method
- Most-Specific Matching: Automatically selects the route with the most specific query parameter requirements
- URI Generation: Generate URIs with query parameters via
generateUri() - Laminas Service Manager Integration: Full autowiring support via ConfigProvider
- TDD Implementation: Comprehensive unit and integration test coverage
Installation
composer require webware/smf-legacy-router
Basic Usage
Simple Route Registration
use Webware\Router\QueryParamRoute; use Webware\Router\QueryParamRouter; use Laminas\Diactoros\ServerRequest; $router = new QueryParamRouter(); // Route requiring 'action' query parameter $route = new QueryParamRoute( '/api', // path $myMiddleware, // middleware/handler ['action'], // required query param keys ['POST'], // optional HTTP methods (null = any) 'api-action' // optional route name ); $router->addRoute($route); // Match request $request = new ServerRequest([], [], '/api', 'POST'); $request = $request->withQueryParams(['action' => 'create']); $result = $router->match($request); if ($result->isSuccess()) { $params = $result->getMatchedParams(); // $params = ['action' => 'create'] }
Auto-Generated Route Names
If you don't provide a route name, one is generated automatically:
// Route: /api with query params ['action', 'type'] // Generated name: "/api?action&type" // Route: /api with query params ['action'] and method GET // Generated name: "/api?action^GET" // Route: /api with no query params // Generated name: "/api"
Multiple Routes on Same Path
Different query parameters allow multiple routes on the same path:
// These can coexist: $router->addRoute(new QueryParamRoute('/api', $handler1, ['action'])); $router->addRoute(new QueryParamRoute('/api', $handler2, ['type'])); $router->addRoute(new QueryParamRoute('/api', $handler3, ['action', 'type'])); // Request with ?action=create matches first route // Request with ?type=user matches second route // Request with ?action=create&type=user matches third route (most specific)
HTTP Method Filtering
// Only match POST requests $postRoute = new QueryParamRoute('/api', $handler, ['action'], ['POST']); // Match any HTTP method (default) $anyRoute = new QueryParamRoute('/api', $handler, ['action']); // Match multiple methods $multiRoute = new QueryParamRoute('/api', $handler, ['action'], ['GET', 'POST', 'PUT']);
Duplicate Detection
The router can detect and prevent duplicate routes:
use Webware\Router\QueryParamDuplicateRouteDetector; $detector = new QueryParamDuplicateRouteDetector(); $router = new QueryParamRouter($detector); $router->addRoute(new QueryParamRoute('/api', $handler1, ['action'])); // This will throw DuplicateRouteException: $router->addRoute(new QueryParamRoute('/api', $handler2, ['action']));
Duplicate Detection Rules:
- Same route name → Duplicate
- Same path + query param keys + HTTP method → Duplicate
- Query param keys are order-independent:
['action', 'type']==['type', 'action'] - Query param keys are case-sensitive:
['Action']≠['action']
URI Generation
Generate URIs with query parameters:
$route = new QueryParamRoute( '/users/{id}/posts', $handler, ['action'], null, 'user-posts' ); $router->addRoute($route); // Generate with path substitution and query params $uri = $router->generateUri( 'user-posts', ['id' => '42'], // path substitutions ['query' => ['action' => 'edit', 'draft' => 'true']] // query params ); // Result: /users/42/posts?action=edit&draft=true
Integration with Laminas Stratigility
Use with middleware pipelines:
use Laminas\Stratigility\MiddlewarePipe; use Mezzio\Router\RouteResult; $pipe = new MiddlewarePipe(); // Routing middleware $pipe->pipe(function ($request, $handler) use ($router) { $result = $router->match($request); $request = $request->withAttribute(RouteResult::class, $result); return $handler->handle($request); }); // Dispatch middleware $pipe->pipe(function ($request, $handler) { $result = $request->getAttribute(RouteResult::class); if ($result->isSuccess()) { return $result->getMatchedRoute()->process($request, $handler); } return $handler->handle($request); }); $response = $pipe->handle($request);
Service Manager Configuration
Using ConfigProvider (Recommended)
Add to your config aggregator:
// config/config.php use Laminas\ConfigAggregator\ConfigAggregator; $aggregator = new ConfigAggregator([ \Webware\Router\ConfigProvider::class, // ... other providers ]); return $aggregator->getMergedConfig();
Manual Registration
// config/autoload/dependencies.global.php use Webware\Router\QueryParamRouter; use Webware\Router\QueryParamRouterFactory; return [ 'dependencies' => [ 'factories' => [ QueryParamRouter::class => QueryParamRouterFactory::class, ], ], QueryParamRouter::class => [ 'detect_duplicates' => true, // Enable duplicate detection (default) ], ];
Retrieve from Container
use Webware\Router\QueryParamRouter; $router = $container->get(QueryParamRouter::class);
Configuration Options
Router Configuration
[
QueryParamRouter::class => [
'detect_duplicates' => true, // Enable/disable duplicate detection (default: true)
],
]
Matching Behavior
Case Sensitivity
Query parameter keys are case-sensitive:
$route = new QueryParamRoute('/api', $handler, ['Action']); // Matches: ?Action=value // Does NOT match: ?action=value
Required vs Extra Parameters
Only the required query parameter keys must be present. Extra parameters are allowed but ignored:
$route = new QueryParamRoute('/api', $handler, ['action']); // Matches: ?action=create // Matches: ?action=create&extra=ignored&another=also-ignored // Does NOT match: ?other=value (missing 'action')
Empty Query Parameter Routes
Routes with no required query params match any query parameters:
$route = new QueryParamRoute('/api', $handler, []); // Matches: /api (no query params) // Matches: /api?any=params&are=fine
Most-Specific Route Wins
When multiple routes match, the one with the most required query parameters is selected:
$router->addRoute(new QueryParamRoute('/api', $handler1, ['action'])); // 1 param $router->addRoute(new QueryParamRoute('/api', $handler2, ['action', 'type'])); // 2 params $request->withQueryParams(['action' => 'create', 'type' => 'user']); // Matches second route (more specific)
Matched Parameters
Matched query parameter values are included in RouteResult::getMatchedParams():
$route = new QueryParamRoute('/api', $handler, ['action', 'type']); $result = $router->match($request->withQueryParams(['action' => 'create', 'type' => 'user'])); $params = $result->getMatchedParams(); // ['action' => 'create', 'type' => 'user']
Only required parameter values are included (extra params are ignored).
Failure Scenarios
Missing Required Query Parameter
Returns RouteResult::fromRouteFailure(null) (treated as 404):
$route = new QueryParamRoute('/api', $handler, ['action']); $result = $router->match($request->withQueryParams(['other' => 'value'])); $result->isFailure(); // true
HTTP Method Mismatch
Returns failure when method doesn't match:
$route = new QueryParamRoute('/api', $handler, ['action'], ['POST']); $result = $router->match($request->withMethod('GET')->withQueryParams(['action' => 'value'])); $result->isFailure(); // true
Path Not Found
Returns failure when path doesn't match any route:
$result = $router->match($request->withUri(new Uri('/unknown'))); $result->isFailure(); // true
Comparison with Standard Mezzio Routing
| Feature | Standard Mezzio Router | Query Param Router |
|---|---|---|
| Match by Path | ✅ | ✅ |
| Match by HTTP Method | ✅ | ✅ |
| Match by Query Params | ❌ | ✅ |
Path Parameters ({id}) |
✅ | ✅ (via URI generation) |
| Most-Specific Matching | ❌ | ✅ |
| Duplicate Detection | ✅ (path+method) | ✅ (path+query+method) |
Development
Running Tests
# All tests vendor/bin/phpunit # Unit tests only vendor/bin/phpunit test/unit/ # Integration tests only vendor/bin/phpunit test/integration/ # With coverage vendor/bin/phpunit --coverage-html coverage/
Code Quality
# Static analysis vendor/bin/phpstan analyse # Code style check vendor/bin/php-cs-fixer fix --dry-run --diff
Requirements
- PHP 8.0+
- mezzio/mezzio-router ^3.13
- laminas/laminas-stratigility ^3.10 (for integration)
License
BSD-3-Clause. See LICENSE file.
Contributing
Contributions welcome! Please ensure all tests pass and follow existing code style.
Authors
- Joey (aka Tyrsson) Smith jsmith@webinertia.net