PHPackages                             webware/smf-legacy-router - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. webware/smf-legacy-router

ActiveLibrary[Templating &amp; Views](/categories/templating)

webware/smf-legacy-router
=========================

RouterInterface for routing legacy SMF urls to middleware/request handlers.

0.1.x-dev(4mo ago)03BSD-3-ClausePHPCI failing

Since Feb 15Pushed 4mo agoCompare

[ Source](https://github.com/tyrsson/smf-legacy-router)[ Packagist](https://packagist.org/packages/webware/smf-legacy-router)[ RSS](/packages/webware-smf-legacy-router/feed)WikiDiscussions 0.1.x Synced today

READMEChangelogDependencies (8)Versions (2)Used By (0)

Query Parameter Router
======================

[](#query-parameter-router)

A query-parameter-based `RouterInterface` implementation for routing requests to middleware/handlers based on `$request->getQueryParams()`. Fully compatible with [mezzio/mezzio-router](https://github.com/mezzio/mezzio-router) and [laminas/laminas-stratigility](https://github.com/laminas/laminas-stratigility).

Features
--------

[](#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
------------

[](#installation)

```
composer require webware/smf-legacy-router
```

Basic Usage
-----------

[](#basic-usage)

### Simple Route Registration

[](#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

[](#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

[](#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

[](#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
-------------------

[](#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
--------------

[](#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
-------------------------------------

[](#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
-----------------------------

[](#service-manager-configuration)

### Using ConfigProvider (Recommended)

[](#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

[](#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

[](#retrieve-from-container)

```
use Webware\Router\QueryParamRouter;

$router = $container->get(QueryParamRouter::class);
```

Configuration Options
---------------------

[](#configuration-options)

### Router Configuration

[](#router-configuration)

```
[
    QueryParamRouter::class => [
        'detect_duplicates' => true,  // Enable/disable duplicate detection (default: true)
    ],
]
```

Matching Behavior
-----------------

[](#matching-behavior)

### Case Sensitivity

[](#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

[](#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

[](#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

[](#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-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
-----------------

[](#failure-scenarios)

### Missing Required Query Parameter

[](#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

[](#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

[](#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
---------------------------------------

[](#comparison-with-standard-mezzio-routing)

FeatureStandard Mezzio RouterQuery Param RouterMatch 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
-----------

[](#development)

### Running Tests

[](#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

[](#code-quality)

```
# Static analysis
vendor/bin/phpstan analyse

# Code style check
vendor/bin/php-cs-fixer fix --dry-run --diff
```

Requirements
------------

[](#requirements)

- PHP 8.0+
- [mezzio/mezzio-router](https://github.com/mezzio/mezzio-router) ^3.13
- [laminas/laminas-stratigility](https://github.com/laminas/laminas-stratigility) ^3.10 (for integration)

License
-------

[](#license)

BSD-3-Clause. See [LICENSE](LICENSE) file.

Contributing
------------

[](#contributing)

Contributions welcome! Please ensure all tests pass and follow existing code style.

Authors
-------

[](#authors)

- Joey (aka Tyrsson) Smith

Support
-------

[](#support)

- [GitHub Issues](https://github.com/tyrsson/smf-legacy-router/issues)
- [GitHub Discussions](https://github.com/tyrsson/smf-legacy-router/discussions)

###  Health Score

26

—

LowBetter than 41% of packages

Maintenance74

Regular maintenance activity

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity20

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

140d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4d6eed33e61a99f1147789696252f4523130b5035a19eb07e034a7407fd44548?d=identicon)[tyrsson](/maintainers/tyrsson)

---

Top Contributors

[![tyrsson](https://avatars.githubusercontent.com/u/1237487?v=4)](https://github.com/tyrsson "tyrsson (10 commits)")

---

Tags

phptemplaterepository

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/webware-smf-legacy-router/health.svg)

```
[![Health](https://phpackages.com/badges/webware-smf-legacy-router/health.svg)](https://phpackages.com/packages/webware-smf-legacy-router)
```

###  Alternatives

[alhimik1986/php-excel-templator

PHP Spreadsheet extension for generating excel files from template

351360.9k1](/packages/alhimik1986-php-excel-templator)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
