karelwintersky/arris.router
最新稳定版本:2.3.0
Composer 安装命令:
composer require karelwintersky/arris.router
包简介
Arris Application µFramework - AppRouter class
README 文档
README
Попытка написать свой роутер на базе https://github.com/nikic/FastRoute
Реализует возможность статического класса 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 "<br>" . PHP_EOL; }
Детали
init - Инициализация роутера
AppRouter::init( logger: AppLogger::scope('routing'), /* other options */ );
Опции:
namespace- неймспейс по-умолчанию, может быть задан вызовомAppRouter::setDefaultNamespace()prefix- префикс URL (аналогично поведению для групп)allowEmptyHandlers(false) - разрешить ли пустые хэндлеры?allowEmptyGroups(false) - разрешить ли пустые группы?
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?
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-шаблонах:
<button data-url="{Arris\AppRouter::getRouter('callback_entry_delete', [ 'id' => $item.user_id ])}">Delete Entry</button>
Что требует определения в Smarty или Arris.Presenter:
->registerClass("Arris\AppRouter", "Arris\AppRouter")
Исключения (Exceptions)
Класс может выкинуть три исключения:
AppRouterHandlerError- ошибка в хэндлере (пустой, неправильный, итп)AppRouterNotFoundException- роут не определен (URL ... not found)AppRouterMethodNotAllowedException- используемый метод недопустим для этого роута
При этом передается расширенная информация по роуту, получить которую можно через метод $e->getError(), потому что
переопределить финальный метод getMessage() НЕВОЗМОЖНО.
Алиасы (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/<value>/ в зависимости от совпадения с регуляркой будет вызван один из хэндлеров:
\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 => user root'/user/123/= 'Closure => userid: 123'/username/wombat/= 'Closure => username: wombat'
Использование алиасов с выключенной опцией 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;
Даст примерно такую картинку:
ToDo
- Опция
middlewareNamespaceдляinit()- неймспейс посредников по умолчанию.
统计信息
- 总下载量: 652
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 1
其他信息
- 授权协议: MIT
- 更新时间: 2023-08-02