PHPackages                             karelwintersky/arris.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. [Framework](/categories/framework)
4. /
5. karelwintersky/arris.router

ActiveLibrary[Framework](/categories/framework)

karelwintersky/arris.router
===========================

Arris Application µFramework - AppRouter class

2.3.1(3mo ago)0746[2 issues](https://github.com/ArrisFramework/Arris.AppRouter/issues)MITPHPPHP 8.\*

Since Aug 2Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/ArrisFramework/Arris.AppRouter)[ Packagist](https://packagist.org/packages/karelwintersky/arris.router)[ RSS](/packages/karelwintersky-arrisrouter/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (10)Dependencies (2)Versions (33)Used By (0)

About
=====

[](#about)

Попытка написать свой роутер на базе

Реализует возможность статического класса AppRouter.

- `init()`
- `get()`
- `post()`
- etc

Пример использования:
=====================

[](#пример-использования)

```
use Arris\AppRouter;
use Arris\Exceptions\{
    AppRouterHandlerError,
    AppRouterMethodNotAllowedException,
    AppRouterNotFoundException
};

try {
    AppRouter::init(
        logger: null,
        allowEmptyHandlers: true,
    );

    AppRouter::get('/', [ DynamicClass::class, 'present_dynamic_method'], 'root');

    AppRouter::get('/function/', 'example_function', 'root.function_call');

    AppRouter::group(
        prefix: '/admin',
        before: 'MiddleAdmin@before',
        after: [ MiddleAdmin::class, 'after' ],
        callback: function () {
            AppRouter::get('/', function () { d('this is simple closure'); }, 'admin.root');

            AppRouter::get('/foo[/]', 'StaticClass@present_static_method', 'admin.foo');

            AppRouter::get('/list/', [StaticClass::class, 'present_static_method'], 'admin.list');

            AppRouter::group(
                prefix: '/users',
                before: [MiddleAdminUsers::class, 'before'],
                after: [MiddleAdminUsers::class, 'after'],
                callback: static function() {
                    AppRouter::get('/', [ DynamicClass::class, 'users'], 'admin.users.root');
                    AppRouter::get('/all/', 'DynamicClass@all', 'admin.users.all');
                    AppRouter::get('/invoke/', 'DynamicClass@' , 'admin.users.invoke');
                    AppRouter::get('/list/', [StaticClass::class, 'method_not_exist'], 'admin.users.list');
                    AppRouter::get('/empty/[{id:\d+}[/]]', /*[ DynamicClass::class, 'create']*/ [] , 'admin.users.empty');
                }
            );
        }
    );

    AppRouter::dispatch();

} catch (AppRouterHandlerError|AppRouterNotFoundException|AppRouterMethodNotAllowedException $e) {
    var_dump($e->getMessage());
} catch (RuntimeException|Exception $e) {
    var_dump($e);
    echo "" . PHP_EOL;
}
```

Детали
======

[](#детали)

init - Инициализация роутера
----------------------------

[](#init---инициализация-роутера)

```
AppRouter::init(
    logger: AppLogger::scope('routing'),
    /* other options */
);
```

Опции:

- `namespace` - неймспейс по-умолчанию, может быть задан вызовом `AppRouter::setDefaultNamespace()`
- `prefix` - префикс URL (аналогично поведению для групп)
- `allowEmptyHandlers` (false) - разрешить ли пустые хэндлеры?
- `allowEmptyGroups` (false) - разрешить ли пустые группы?

setOption - переопределение опций
---------------------------------

[](#setoption---переопределение-опций)

Некоторые опции могут быть переопределены только вызовом:

```
AppRouter::setOption(name, value);
```

Допустимые имена опций:

- `AppRouter::OPTION_ALLOW_EMPTY_HANDLERS` - разрешить пустые (заданные как `[]`) хэндлеры? Если false - кидается исключение `AppRouterHandlerError: Handler not found or empty`.
- `AppRouter::OPTION_ALLOW_EMPTY_GROUPS` - разрешить ли пустые группы? Пустой считается группа без роутов. Если разрешено - для такой группы будут парситься миддлвары и опции.
- `AppRouter::OPTION_DEFAULT_ROUTE` - дефолтное значение для реверс-роутинга
- `AppRouter::OPTION_USE_ALIASES` - разрешить ли алиасы?

Декларация роутов
-----------------

[](#декларация-роутов)

Методы: `get`, `post`, `put`, `patch`, `delete`, `head`, `options`

```
AppRouter::method(
    route: '/my/awesome/uri/',
    handler: хэндлер,
    name: 'имя'
);
```

- `route` - строка (с регулярками/алиасами регулярок)
- `handler` - хэндлер
- `name` - имя роута для обратного роутинга (reverse routing)

### Как можно задать handler?

[](#как-можно-задать-handler)

- `function() { }`, то есть Closure;
- `[Class::class, 'method']` - массив из двух элементов, подразумевается, что метод динамический, то есть класс будет инстанциирован перед вызовом метода.
- `Class@method` - строка, содержащая `@`. Будет применена рефлексия для вычисления типа метода. Если метод динамический - класс будет инстанциирован.
- `Class@` - будет вызван метод `__invoke()` у класса.
- `function` - функция
- `null` - строго пустой роут, вызов **всегда** выбросит исключение `AppRouterNotFoundException -> URL not found`
- `[]`. По умолчанию будет выброшено исключение `AppRouterHandlerError`, но... есть нюанс:

### Пустой хэндлер?

[](#пустой-хэндлер)

Если задать опцию `allowEmptyHandlers: true` или вызвать `AppRouter::setOption('allowEmptyHandlers', true)`, то можно будет использовать пустые хэндлеры, например:

```
AppRouter::get('/admin/users/', [], 'admin.users.root');
```

В этом случае пройдет стандартная цепочка роутинга - будут инстанциированы и вызваны миддлвары, сначала before, потом в обратном порядке after, например:

```
string(30) "Class MiddleAdmin instantiated"
string(19) "MiddleAdmin::before"
string(35) "Class MiddleAdminUsers instantiated"
string(24) "MiddleAdminUsers::before"

string(23) "MiddleAdminUsers::after"
string(18) "MiddleAdmin::after"

```

Группировка роутов
------------------

[](#группировка-роутов)

```
\Arris\AppRouter::group(
    prefix: '/admin',
    before: 'MiddleAdmin@before',
    after: [ MiddleAdmin::class, 'after' ],
    callback: function () {
        /* роуты группы */
    }
);
```

Реверс-роутинг
--------------

[](#реверс-роутинг)

`AppRouter::getRouter(name)` возвращает URL, соответствующий имени роута.

При этом, имя `*` вернет все маршруты. Если имя не найдено - будет возвращен роут по-умолчанию.

При этом:

- именованные группы-плейсхолдеры будут заменены на переданные переменные
- необязательные оконечные слэши будут заменены на обязательные
- будут удалены необязательные группы

Таким образом, если роут определен:

```
AppRouter::get('/entry/delete/{id}/', 'handler', 'callback_entry_delete');
```

То вызов

```
Arris\AppRouter::getRouter('callback_entry_delete', [ 'id' => 15 ])
```

Сгенерирует строчку: `/entry/delete/15/`

### Роут по-умолчанию

[](#роут-по-умолчанию)

Если роут **не найден** или передан пустой роут - будет возвращен URL `/`. Это поведение может быть переопределено вызовом:

```
AppRouter::setOption('getRouterDefaultValue', '/foo/bar');
```

### Вызов реверс-роутинга в шаблонах

[](#вызов-реверс-роутинга-в-шаблонах)

Что полезно, реверс-роутинг может вызываться в Smarty-шаблонах:

```
Delete Entry

```

Что требует определения в Smarty или Arris.Presenter:

```
->registerClass("Arris\AppRouter", "Arris\AppRouter")
```

Исключения (Exceptions)
-----------------------

[](#исключения-exceptions)

Класс может выкинуть три исключения:

- `AppRouterHandlerError` - ошибка в хэндлере (пустой, неправильный, итп)
- `AppRouterNotFoundException` - роут не определен (URL ... not found)
- `AppRouterMethodNotAllowedException` - используемый метод недопустим для этого роута

При этом передается расширенная информация по роуту, получить которую можно через метод `$e->getError()`, потому что переопределить финальный метод `getMessage()` НЕВОЗМОЖНО.

Алиасы (BETA)
-------------

[](#алиасы-beta)

Включается с помощью

```
AppRouter::setOption('useAliases', true);
```

После этого можно задать алиасы:

```
AppRouter::addAlias([
    [ 'userid'    =>  '\d+' ],
    [ 'username'  =>  '[a-zA-Z]+' ]
]);
```

И определить роуты:

```
AppRouter::get(
    route: '/user/{userid}[/]',
    handler: function ($userid = 0) { var_dump('Closure => userid: ' . $userid ); },
    name: 'root.userid'
);

AppRouter::get(
    route: '/user/{username}[/]',
    handler: function ($username = 'anon') { var_dump('Closure => username: ' . $username ); },
    name: 'root.username'
);
```

Теперь при вызове `/user//` в зависимости от совпадения с регуляркой будет вызван один из хэндлеров:

- `\d+`, то есть число - хэндлер userid
- `[a-zA-Z]+`, то есть латинская строка - хэндлер username

### Реверс-роутинг и алиасы

[](#реверс-роутинг-и-алиасы)

Прекрасно работает:

```
echo AppRouter::getRouter('root.userid',    [ 'userid'   => 42 ]);          // => /user/42/
echo AppRouter::getRouter('root.username',  [ 'username' => 'wombat' ]);    // => /user/wombat/
```

### Опциональные группы и алиасы

[](#опциональные-группы-и-алиасы)

В данном случае объявить опциональной можно только одну группу, хотя так делать не стоит:

```
AppRouter::get('/user/{userid}[/]', function ($userid = 0) { var_dump('Closure => userid: ' . $userid  ); });
AppRouter::get('/user/[{username}[/]]', function ($username = 'anon') { var_dump('Closure => username: ' . $username  ); });
```

При переходе на `/user/` произойдет вызов хэндлера **username**.

Объявление двух групп опциональными вызовет исключение:

```
BadRouteException: Cannot register two routes matching "/user/" for method "GET"

```

### Как на самом деле сделать "опциональную" группу:

[](#как-на-самом-деле-сделать-опциональную-группу)

```
AppRouter::get('/user/', function () { var_dump('Closure => user root '  ); });
AppRouter::get('/user/{userid}[/]', function ($userid = 0) { var_dump('Closure => userid: ' . $userid  ); });
AppRouter::get('/user/{username}[/]', function ($username = 'anon') { var_dump('Closure => username: ' . $username  ); });
```

- `/user` = 'Closure =&gt; user root'
- `/user/123/` = 'Closure =&gt; userid: 123'
- `/username/wombat/` = 'Closure =&gt; username: wombat'

### Использование алиасов с выключенной опцией useAliases

[](#использование-алиасов-с-выключенной-опцией-usealiases)

Вызовет исключение:

```
BadRouteException: Cannot register two routes matching "/user/([^/]+)" for method "GET""

```

Происходит это, очевидно, потому что без алиасов подгруппы `{userid}` и `{username}` раскрываются в `([^/]+)`, а роуты с одинаковыми URL определить нельзя.

**NB:**

В версии 2.0.\* реализованы **только** глобальные алиасы. Возможности задать алиасы для роутов группы (и только для них) нет.

Отладочные функции
==================

[](#отладочные-функции)

```
echo AppRouter\Helper::dumpRoutingRulesWeb( AppRouter::getRoutingRules(), false );die;
```

Даст примерно такую картинку:

[![image](https://private-user-images.githubusercontent.com/2164874/486939525-2d6f4083-9203-4c4e-8408-610cf3277713.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODIzNTQwMTEsIm5iZiI6MTc4MjM1MzcxMSwicGF0aCI6Ii8yMTY0ODc0LzQ4NjkzOTUyNS0yZDZmNDA4My05MjAzLTRjNGUtODQwOC02MTBjZjMyNzc3MTMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI2MDYyNSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNjA2MjVUMDIxNTExWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NDBiY2MwMjIyNDUxOWUyMTBlM2JhOGRkMzY0NjA3MDkyNTFiMzMwMWYxOGM3MGYzZjhkNjMyMGEzODVlZDk1MyZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmcmVzcG9uc2UtY29udGVudC10eXBlPWltYWdlJTJGcG5nIn0.pkfbZgyfRkDntQqVEIyz586ZM0NZKZDpiaFuUyQp9a4)](https://private-user-images.githubusercontent.com/2164874/486939525-2d6f4083-9203-4c4e-8408-610cf3277713.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODIzNTQwMTEsIm5iZiI6MTc4MjM1MzcxMSwicGF0aCI6Ii8yMTY0ODc0LzQ4NjkzOTUyNS0yZDZmNDA4My05MjAzLTRjNGUtODQwOC02MTBjZjMyNzc3MTMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI2MDYyNSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNjA2MjVUMDIxNTExWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NDBiY2MwMjIyNDUxOWUyMTBlM2JhOGRkMzY0NjA3MDkyNTFiMzMwMWYxOGM3MGYzZjhkNjMyMGEzODVlZDk1MyZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmcmVzcG9uc2UtY29udGVudC10eXBlPWltYWdlJTJGcG5nIn0.pkfbZgyfRkDntQqVEIyz586ZM0NZKZDpiaFuUyQp9a4)---

ToDo
====

[](#todo)

- Опция `middlewareNamespace` для `init()` - неймспейс посредников по умолчанию.

###  Health Score

42

—

FairBetter than 89% of packages

Maintenance78

Regular maintenance activity

Popularity17

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity55

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 ~30 days

Recently: every ~51 days

Total

32

Last Release

117d ago

Major Versions

0.9.1 → 1.0.02023-08-07

v1.x-dev → 2.0.02025-03-04

PHP version history (3 changes)0.9PHP &gt;=7.4

1.2.0PHP &gt;=7.4 || 8.\*

2.0.0PHP 8.\*

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2164874?v=4)[Karel Wintersky](/maintainers/KarelWintersky)[@KarelWintersky](https://github.com/KarelWintersky)

---

Top Contributors

[![KarelWintersky](https://avatars.githubusercontent.com/u/2164874?v=4)](https://github.com/KarelWintersky "KarelWintersky (46 commits)")

---

Tags

middlewareroutingfast-routemiddlewaresfast-router

### Embed Badge

![Health badge](/badges/karelwintersky-arrisrouter/health.svg)

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

###  Alternatives

[symfony/symfony

The Symfony PHP framework

31.4k86.9M2.2k](/packages/symfony-symfony)[tempest/framework

The PHP framework that gets out of your way.

2.2k31.1k12](/packages/tempest-framework)[luthier/luthier

Improved routing, middleware support, authentication tools and more for CodeIgniter 3 framework

151118.9k](/packages/luthier-luthier)[yiisoft/yii-middleware

Yii Middleware

21159.1k1](/packages/yiisoft-yii-middleware)

PHPackages © 2026

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