PHPackages                             sirix/mezzio-routing-attributes - 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. [API Development](/categories/api)
4. /
5. sirix/mezzio-routing-attributes

ActiveLibrary[API Development](/categories/api)

sirix/mezzio-routing-attributes
===============================

Attribute-based routing support for Mezzio applications

1.0.1(1mo ago)0407↑68.2%2MITPHPPHP ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0CI passing

Since Mar 12Pushed 1mo agoCompare

[ Source](https://github.com/sirix777/mezzio-routing-attributes)[ Packagist](https://packagist.org/packages/sirix/mezzio-routing-attributes)[ Fund](https://buymeacoffee.com/sirix)[ GitHub Sponsors](https://github.com/sirix777)[ RSS](/packages/sirix-mezzio-routing-attributes/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (10)Dependencies (43)Versions (15)Used By (2)

Mezzio Routing Attributes
=========================

[](#mezzio-routing-attributes)

[![Latest Stable Version](https://camo.githubusercontent.com/01e3c4aa6aaffd7e922531d8fd78c22218d8b237e7232ccdedf709f66fc8158c/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726f7574696e672d617474726962757465732f76)](https://packagist.org/packages/sirix/mezzio-routing-attributes)[![Total Downloads](https://camo.githubusercontent.com/72f23226d75acac533b2158d036478c2f4ee8d852b1dc9c33719a361a67dc907/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726f7574696e672d617474726962757465732f646f776e6c6f616473)](https://packagist.org/packages/sirix/mezzio-routing-attributes)[![Latest Unstable Version](https://camo.githubusercontent.com/5cd27483cd90702a85304849476e3346626a3dc0133ed76a84b2a2d1288285aa/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726f7574696e672d617474726962757465732f762f756e737461626c65)](https://packagist.org/packages/sirix/mezzio-routing-attributes)[![License](https://camo.githubusercontent.com/fdbbe84c4700ebeb1b8b35a8de08701a408d7265949f84d46f60611c9ae71ccc/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726f7574696e672d617474726962757465732f6c6963656e7365)](https://packagist.org/packages/sirix/mezzio-routing-attributes)[![PHP Version Require](https://camo.githubusercontent.com/c39a03108a8240a0a06ca618d3a80de4a7a408f3acbfbd350a6cd3c878c1d567/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d726f7574696e672d617474726962757465732f726571756972652f706870)](https://packagist.org/packages/sirix/mezzio-routing-attributes)

Attribute-based route registration for Mezzio applications.

Stable `1.0` releases follow semantic versioning for the public API documented below.

Installation
------------

[](#installation)

```
composer require sirix/mezzio-routing-attributes
```

Optional CLI command registration requires console packages:

```
composer require laminas/laminas-cli symfony/console
```

Install `mezzio/mezzio-tooling` only when you want integration with Mezzio's upstream `mezzio:routes:list` command.

Status
------

[](#status)

This package provides:

- PHP 8 route attributes (`Route`, `Get`, `Post`, `Put`, `Patch`, `Delete`, `Any`)
- Class-level and method-level attribute extraction
- Route provider registration via `RouteCollectorInterface`
- Optional route middleware stacks in attributes (`middleware: [...]`)
- Optional class discovery from configured directories
- Compiled route cache artifact (`require`-based)
- CLI commands:
    - `routing-attributes:routes:list`
    - `routing-attributes:cache:clear`

Stability and Public API
------------------------

[](#stability-and-public-api)

The stable public API for `1.x` is:

- Route attributes:
    - `Sirix\Mezzio\Routing\Attributes\Attribute\Route`
    - `Sirix\Mezzio\Routing\Attributes\Attribute\Get`
    - `Sirix\Mezzio\Routing\Attributes\Attribute\Post`
    - `Sirix\Mezzio\Routing\Attributes\Attribute\Put`
    - `Sirix\Mezzio\Routing\Attributes\Attribute\Patch`
    - `Sirix\Mezzio\Routing\Attributes\Attribute\Delete`
    - `Sirix\Mezzio\Routing\Attributes\Attribute\Any`
- Configuration keys under `routing_attributes`:
    - `classes`
    - `duplicate_strategy`
    - `handlers.mode`
    - `override_mezzio_routes_list_command`
    - `route_list.classic_routes_middleware_display`
    - `discovery.enabled`
    - `discovery.paths`
    - `discovery.strategy`
    - `discovery.psr4.mappings`
    - `discovery.psr4.fallback_to_token`
    - `cache.enabled`
    - `cache.file`
- Extension contract for custom route metadata attributes:
    - `Sirix\Mezzio\Routing\Contracts\RouteAttributeModifierInterface`
- Package integration entry point:
    - `Sirix\Mezzio\Routing\Attributes\ConfigProvider`
- CLI command names and documented options:
    - `routing-attributes:routes:list`
    - `routing-attributes:cache:clear`

All other source classes are implementation details unless this README documents them as an integration point. They may be final, internal, or changed in a minor release when needed to keep the documented API working.

`Route` is public because it is the generic route attribute and the base class for the package HTTP-method attributes. For custom route metadata, prefer `RouteAttributeModifierInterface`; extending `Route` in application code is not a supported extension point.

Recommended production mode:

- use an explicit `classes` list;
- enable compiled cache;
- clear/warm cache during deploy;
- restart long-running workers after route/cache changes.

Configuration
-------------

[](#configuration)

Production default (performance-first):

```
return [
    'routing_attributes' => [
        'classes' => [
            App\Handler\PingHandler::class,
        ],
        'duplicate_strategy' => 'throw', // throw|ignore
        'handlers' => [
            'mode' => 'psr15', // psr15|callable
        ],
        'override_mezzio_routes_list_command' => false,
        'route_list' => [
            'classic_routes_middleware_display' => 'upstream', // upstream|resolved
        ],
        'discovery' => [
            'enabled' => false,
            'paths' => [],
            'strategy' => 'token', // token|psr4
            'psr4' => [
                'mappings' => [],
                'fallback_to_token' => true,
            ],
        ],
        'cache' => [
            'enabled' => true,
            'file' => 'data/cache/mezzio-routing-attributes.php',
        ],
    ],
];
```

Supported `routing_attributes.cache` keys:

- `enabled` (`bool`)
- `file` (`non-empty string`, required when `enabled=true`)

The package registers its own factories through `ConfigProvider`; application handlers and middleware still need to be available in your container.

Optional CLI Support
--------------------

[](#optional-cli-support)

CLI command registration is enabled only when the optional console dependencies are installed.

```
composer require laminas/laminas-cli symfony/console
```

When `mezzio/mezzio-tooling` is available, the package can decorate the upstream routes list command. Without it, the package registers its own `mezzio:routes:list` alias when console support is available.

Discovery Behavior
------------------

[](#discovery-behavior)

- If `discovery.enabled=false`, only explicit `classes` are used.
- If `discovery.enabled=true`, classes are discovered from `discovery.paths`.
- If compiled cache is enabled and cache file already exists, discovery is skipped on boot.
- Prefer discovery for development or cache warmup, not as the main production boot path.
- `strategy=token` parses PHP files without requiring PSR-4 path mappings.
- `strategy=psr4` resolves class names from configured `discovery.psr4.mappings`; when `fallback_to_token=true`, files that cannot be mapped are parsed with the token strategy.

Compiled Cache Behavior
-----------------------

[](#compiled-cache-behavior)

- If `cache.enabled=true` and cache file exists, routes are registered from compiled cache.
- If cache file is missing or invalid, routes are extracted/discovered and cache file is rebuilt.
- Cache writes are best-effort: write failures do not break application boot, but they leave the next boot on the non-compiled path.
- Cache format is optimized for startup speed and keeps middleware pipeline resolution lazy per service.
- Ensure the cache directory is writable by the process that warms/rebuilds routes.

Cache Clear Command
-------------------

[](#cache-clear-command)

Clear compiled cache file:

```
php vendor/bin/laminas routing-attributes:cache:clear
```

Override file path:

```
php vendor/bin/laminas routing-attributes:cache:clear --file=data/cache/custom-routes.php
```

In RoadRunner/Swoole-style runtimes, reload workers after clearing or rebuilding the cache.

Upgrading from `0.1.x`
----------------------

[](#upgrading-from-01x)

`1.0.0` stabilizes the current production-oriented configuration model. Review these changes if your application started on an older `0.1.x` release:

- Custom attribute modifiers now use `Sirix\Mezzio\Routing\Contracts\RouteAttributeModifierInterface` from `sirix/mezzio-routing-contracts`. Replace the old `Sirix\Mezzio\Routing\Attributes\Contract\RouteAttributeModifierInterface` namespace.
- Compiled route cache is configured with `routing_attributes.cache.enabled` and `routing_attributes.cache.file`. Legacy cache keys such as `mode`, `backend`, `strict`, and `write_fail_strategy` are no longer supported.
- Discovery class-map cache configuration was removed. Use compiled route cache plus explicit `classes` for production, and enable discovery mainly for development or cache warmup.
- Optional CLI integrations are optional dependencies. Install `laminas/laminas-cli` and `symfony/console` when you want package commands registered automatically, and install `mezzio/mezzio-tooling` only for upstream route-list integration.
- Cache writes are best-effort. A failed cache write does not stop application boot, but the next boot will run the non-compiled path until the cache file can be written.

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

[](#basic-usage)

Method-level attribute:

```
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Sirix\Mezzio\Routing\Attributes\Attribute\Get;

final class PingHandler implements RequestHandlerInterface
{
    #[Get('/ping', name: 'ping')]
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        throw new \RuntimeException('Implement your response.');
    }
}
```

Class-level attribute:

```
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Sirix\Mezzio\Routing\Attributes\Attribute\Get;

#[Get('/ping', name: 'ping')]
final class PingHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        throw new \RuntimeException('Implement your response.');
    }
}
```

Custom Attribute Modifiers
--------------------------

[](#custom-attribute-modifiers)

You can create route-related attributes in your own package by implementing `Sirix\Mezzio\Routing\Contracts\RouteAttributeModifierInterface`.

Example custom attribute:

```
namespace Acme\Routing\Attribute;

use Attribute;
use Sirix\Mezzio\Routing\Contracts\RouteAttributeModifierInterface;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final readonly class RequireTenant implements RouteAttributeModifierInterface
{
    public function __construct(private string $tenantHeader = 'x-tenant-id') {}

    public function getMiddleware(): array
    {
        return [Acme\Middleware\RequireTenantMiddleware::class];
    }

    public function getDefaults(): array
    {
        return ['tenant_header' => $this->tenantHeader];
    }
}
```

Usage with route attributes:

```
use Acme\Routing\Attribute\RequireTenant;
use Sirix\Mezzio\Routing\Attributes\Attribute\Get;

#[RequireTenant('x-tenant-id')]
final class OrdersHandler
{
    #[Get('/orders', name: 'orders.list')]
    #[RequireTenant('x-org-id')]
    public function index(mixed ...$args): mixed
    {
        // ...
    }
}
```

### Route Defaults and Placeholders

[](#route-defaults-and-placeholders)

The `getDefaults()` method allows you to provide default values for route placeholders. This is useful when you have optional parameters in your route paths.

Example with optional parameter:

```
use Attribute;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Sirix\Mezzio\Routing\Attributes\Attribute\Get;
use Sirix\Mezzio\Routing\Contracts\RouteAttributeModifierInterface;

#[Attribute]
final readonly class DefaultFormat implements RouteAttributeModifierInterface
{
    public function __construct(private string $format = 'html') {}

    public function getMiddleware(): array
    {
        return [];
    }

    public function getDefaults(): array
    {
        return ['format' => $this->format];
    }
}

final class ExportHandler
{
    #[Get('/export/:format?')]
    #[DefaultFormat('json')]
    public function __invoke(ServerRequestInterface $request): ResponseInterface
    {
        // $request->getAttribute('format') will be 'json' if not provided in URL
    }
}
```

Notes:

- class-level and method-level modifiers are merged for method routes;
- method-level defaults override class-level defaults on the same key;
- middleware from modifiers is appended after middleware declared in `Route`/`Get` attributes.
- defaults are passed to the Mezzio `Route::setOptions()` and can be used by the underlying router (like FastRoute) to fill missing optional placeholders.

Benchmarks
----------

[](#benchmarks)

Run:

```
composer benchmark
composer benchmark-threshold
```

Run test coverage with PCOV:

```
composer coverage
```

The coverage command requires the `pcov` PHP extension and runs PHPUnit with `pcov.enabled=1` and `pcov.directory=src`.

Baseline run (`PHP 8.2.30`, refreshed on `2026-05-25`):

- Provider benchmark command: `php8.2 benchmarks/route-provider-benchmark.php`
- Threshold benchmark command: `php8.2 benchmarks/route-cache-threshold-benchmark.php`
- Fixture corpus: `test/Extractor/Fixture`
- Manual scenarios register `2` routes; discovery scenarios register `10` routes from the fixture corpus.
- `warm_cache_hit_manual`: `0.0018 ms` median, `2.0156 KB` median peak
- `no_cache_manual`: `0.0074 ms` median, `3.4453 KB` median peak
- `cold_cache_rebuild_manual`: `0.0351 ms` median, `5.9063 KB` median peak
- `warm_cache_hit_discovery_token`: `0.0106 ms` median, `7.8906 KB` median peak
- `warm_cache_hit_discovery_psr4`: `0.0106 ms` median, `7.8906 KB` median peak
- Threshold benchmark (`compiled`) showed cache-win from `10` routes onward.
- At `12800` routes: `21.9587 ms` (no-cache) vs `8.6249 ms` (compiled), speedup `60.72%`; peak memory `13213.21 KB` vs `9260.80 KB`.

These are microbenchmarks for route registration/cache paths, not end-to-end HTTP latency.

Troubleshooting
---------------

[](#troubleshooting)

- Service not found: register handler/action class in container.
- Route changes are not visible: clear compiled cache with `routing-attributes:cache:clear`.
- In long-running workers (RoadRunner/Swoole), reload/restart workers after cache rebuild/clear.
- Invalid cache payload errors: delete cache file and warm it again.

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance94

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 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

Every ~6 days

Total

14

Last Release

31d ago

Major Versions

0.1.10 → 1.0.02026-05-26

### Community

Maintainers

![](https://www.gravatar.com/avatar/6ecccf9003c061847e877eeea3bdf1b382f6f9dbb11d33112d6b2740bf0533f9?d=identicon)[sirix777](/maintainers/sirix777)

---

Top Contributors

[![sirix777](https://avatars.githubusercontent.com/u/68593154?v=4)](https://github.com/sirix777 "sirix777 (2 commits)")

---

Tags

middlewarelaminasrouterroutingpsr-15mezzioattributesphp8routing-attributes

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/sirix-mezzio-routing-attributes/health.svg)

```
[![Health](https://phpackages.com/badges/sirix-mezzio-routing-attributes/health.svg)](https://phpackages.com/packages/sirix-mezzio-routing-attributes)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k19.1M1.7k](/packages/cakephp-cakephp)[mezzio/mezzio

PSR-15 Middleware Microframework

3903.8M120](/packages/mezzio-mezzio)[thecodingmachine/graphqlite

Write your GraphQL queries in simple to write controllers (using webonyx/graphql-php).

5733.2M40](/packages/thecodingmachine-graphqlite)[mezzio/mezzio-authentication-oauth2

OAuth2 (server) authentication middleware for Mezzio and PSR-7 applications.

28545.4k3](/packages/mezzio-mezzio-authentication-oauth2)[mezzio/mezzio-authentication

Authentication middleware for Mezzio and PSR-7 applications

131.7M39](/packages/mezzio-mezzio-authentication)[mezzio/mezzio-swoole

Swoole support for Mezzio

92247.8k3](/packages/mezzio-mezzio-swoole)

PHPackages © 2026

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