PHPackages                             mitsuki/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. [HTTP &amp; Networking](/categories/http)
4. /
5. mitsuki/router

ActiveLibrary[HTTP &amp; Networking](/categories/http)

mitsuki/router
==============

A lightweight and high-performance PHP router for the Mitsuki framework, powered by Symfony Routing components.

v1.0.1(4mo ago)14MITPHPCI passing

Since Feb 20Pushed 4mo agoCompare

[ Source](https://github.com/zgeniuscoders/mitsuki-router)[ Packagist](https://packagist.org/packages/mitsuki/router)[ RSS](/packages/mitsuki-router/feed)WikiDiscussions main Synced today

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

Mitsuki Router
==============

[](#mitsuki-router)

Mitsuki is a **modern HTTP routing engine** for PHP, powered by Symfony Routing/HttpKernel and **PHP 8 attributes**. It provides:

- Automatic controller discovery
- Attribute-based routing (`#[Controller]`, `#[Route]`)
- PSR-11 container integration
- HttpKernel-compatible controller resolution
- Compiled routes caching

---

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

[](#installation)

```
composer require mitsuki/router
```

> The router relies on Symfony Routing, HttpFoundation, HttpKernel, Filesystem, and a PSR-11 container.

---

Core Concepts
-------------

[](#core-concepts)

### Attributes

[](#attributes)

- `#[Controller('prefix')]` on classes
- `#[Route('name', 'path', methods)]` on methods

```
use Mitsuki\Attributes\Controller;
use Mitsuki\Attributes\Route;
use Mitsuki\Controller\BaseController;
use Mitsuki\Http\Responses\JsonResponse;
use Mitsuki\Http\Request;

#[Controller('posts')]
class PostController extends BaseController
{
    #[Route('posts.index', '', ['GET'])]
    public function index(): JsonResponse
    {
        return $this->json(['data' => ['post 1', 'post 2', 'post 3']]);
    }

    #[Route('posts.store', '', ['POST'])]
    public function store(Request $request): JsonResponse
    {
        return $this->json([
            'title' => $request->request->get('title'),
        ]);
    }

    #[Route('posts.show', '{id}', ['GET'])]
    public function show(int $id): JsonResponse
    {
        return $this->json(['data' => 'post 1']);
    }

    #[Route('posts.update', '{id}', ['PUT'])]
    public function update(Request $request, int $id): JsonResponse
    {
        return $this->json(['id' => $id]);
    }

    #[Route('posts.destroy', '{id}', ['DELETE'])]
    public function destroy(int $id): JsonResponse
    {
        return $this->json([], status: 204);
    }
}
```

---

Architecture
------------

[](#architecture)

### `Router` Class

[](#router-class)

Namespace: `Mitsuki\Hermite\Router`

**Responsibilities:**

- Load routes from **compiled cache** or **controller scanning**
- Build Symfony `RouteCollection`
- Delegate matching to `UrlMatcher`
- Resolve **HttpKernel-compatible controller callable**

```
public function __construct(
    private RouteCollection    $routeCollection,
    private RequestContext     $requestContext,
    private ContainerInterface $container,
    private ControllerResolver $controllerResolver,
    string                     $cacheDir
)
```

### Workflow

[](#workflow)

1. **`load()`**

    - If `cache_routes.php` exists → load routes from cache
    - Otherwise → use `ControllerResolver` to discover controllers
    - Analyze attributes, build routes, write cache
2. **`getCallable(Request $request)`**

    - Updates `RequestContext::setMethod()`
    - Uses `UrlMatcher` to match URL + method
    - Binds route parameters to request attributes
    - Fetches controller instance via container
    - Returns `[instance, 'method']` for HttpKernel

### Path Concatenation

[](#path-concatenation)

In `getRoutesFromControllers()`:

- Class prefix: `#[Controller('posts')]`
- Method path: `#[Route('posts.show', '{id}', ['GET'])]`
- Normalized result: `/posts/{id}`

**Normalization:**

- Removes multiple `/`
- Strips trailing `/`
- Falls back to `/` if empty

---

Container Integration
---------------------

[](#container-integration)

Example with **PHP-DI**:

```
use Mitsuki\Hermite\Router;
use Mitsuki\Controller\Resolvers\ControllerResolver;
use Psr\Container\ContainerInterface;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\EventDispatcher\EventDispatcher;

return [
    'project.root' => dirname(__DIR__) . '/src',
    'cache.dir'    => dirname(__DIR__) . '/var/caches',

    ControllerResolver::class => fn($c) =>
        new ControllerResolver($c->get('project.root')),

    Router::class => function (ContainerInterface $c) {
        $router = new Router(
            $c->get(RouteCollection::class),
            $c->get(RequestContext::class),
            $c,
            $c->get(ControllerResolver::class),
            $c->get('cache.dir')
        );

        $controllers = $c->has('controllers') ? $c->get('controllers') : [];
        $router->load($controllers);

        return $router;
    },

    ControllerResolverInterface::class => fn($c) => new class($c->get(Router::class)) implements ControllerResolverInterface {
        public function __construct(private Router $router) {}
        public function getController(Request $request): callable|false
        {
            return $this->router->getCallable($request);
        }
    },

    HttpKernelInterface::class => function (ContainerInterface $c) {
        $argumentResolver = new ArgumentResolver(
            null,
            [
                new RequestAttributeValueResolver(),
                new RequestValueResolver(),
                new DefaultValueResolver(),
            ]
        );

        return new HttpKernel(
            new EventDispatcher(),
            $c->get(ControllerResolverInterface::class),
            new RequestStack(),
            $argumentResolver
        );
    },

    RequestContext::class => \DI\create(RequestContext::class),
    RouteCollection::class => \DI\create(RouteCollection::class),
];
```

---

Request Lifecycle
-----------------

[](#request-lifecycle)

```
HTTP Request
   ↓
HttpKernel::handle()
   ↓
Router::getCallable()
   ↓
[ControllerInstance, method]
   ↓
ArgumentResolver (Request, attributes, defaults)
   ↓
Controller executed
   ↓
JsonResponse / Response

```

---

Usage Example
-------------

[](#usage-example)

```
use Mitsuki\Http\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

$kernel = $container->get(HttpKernelInterface::class);

$request  = Request::create('/posts', 'GET');
$response = $kernel->handle($request);

echo $response->getStatusCode();  // 200
echo $response->getContent();     // JSON data
```

---

Testing
-------

[](#testing)

Tested with **PestPHP** (95%+ coverage).

**Integration tests:**

```
test('GET /posts returns index with paginated data - E2E', function () {
    $request  = Request::create('/posts', 'GET');
    $response = $this->app->get(HttpKernelInterface::class)->handle($request);

    $data = json_decode($response->getContent(), true);

    expect($response->getStatusCode())->toBe(200)
        ->and($data['data'])->toHaveCount(3);
});

test('returns 404 Not Found for unknown routes', function () {
    $request = Request::create('/unknown', 'GET');

    expect(fn() => $this->app->get(HttpKernelInterface::class)->handle($request))
        ->toThrow(NotFoundHttpException::class);
});
```

**Run tests with coverage:**

```
XDEBUG_MODE=coverage ./vendor/bin/pest --coverage --coverage-html coverage-report
```

---

Best Practices
--------------

[](#best-practices)

- Always define **unique route names** in `#[Route]`
- Use **consistent prefixes** via `#[Controller('prefix')]`
- Write tests for each CRUD endpoint
- Enable route caching in production for optimal performance

---

Roadmap
-------

[](#roadmap)

- Route-specific middleware support
- Configurable route groups (auth, API, etc.)
- URL generation from route names
- Request validation &amp; DTO integration

---

---

📄 License
---------

[](#-license)

This project is licensed under the MIT License.

---

**Maintained by Zgenius Matondo**GitHub:

###  Health Score

32

—

LowBetter than 69% of packages

Maintenance76

Regular maintenance activity

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity36

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

Every ~0 days

Total

2

Last Release

132d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/59ea5d2ce29d5426a3d7feabbcc7b07772b03dd80e4cd13afd6f9ac5e0469998?d=identicon)[zgenius](/maintainers/zgenius)

---

Top Contributors

[![zgeniuscoders](https://avatars.githubusercontent.com/u/101071661?v=4)](https://github.com/zgeniuscoders "zgeniuscoders (1 commits)")

---

Tags

routerroutingmvcattributesfast-routemicro-frameworkmitsukiphp8http-kernelsymfony-component

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/mitsuki-router/health.svg)

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

###  Alternatives

[symfony/framework-bundle

Provides a tight integration between Symfony components and the Symfony full-stack framework

3.6k251.7M11.6k](/packages/symfony-framework-bundle)[laravel/framework

The Laravel Framework.

34.8k543.8M20.1k](/packages/laravel-framework)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6942.5M421](/packages/drupal-core-recommended)[nelmio/api-doc-bundle

Generates documentation for your REST API from attributes

2.4k67.4M263](/packages/nelmio-api-doc-bundle)[api-platform/core

Build a fully-featured hypermedia or GraphQL API in minutes!

2.6k51.2M338](/packages/api-platform-core)[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.9M388](/packages/easycorp-easyadmin-bundle)

PHPackages © 2026

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