haszi / router
路由器/调度器的学习练习
Requires
- php: >=8.0
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.4
- vimeo/psalm: ^5.16
README
编写用于了解路由器/调度器和编写这些内容的难点的路由器/调度器。
要求
PHP 8.0+
安装
composer require haszi/router
功能
支持
- GET、HEAD、POST、PUT、DELETE、PATCH、OPTIONS HTTP请求方法
- 每种方法的简写方法
- 静态URI
- 路由参数
- 闭包和'controller@method'类型处理器
- 路由分组
- 为找不到的路由自定义处理器
- 路由中间件之前
入门
基本用法
use Haszi\Router\Router; use Haszi\Router\Dispatcher; $router = new Router(); $router->addRoute('*', '/greet', function () { echo 'Hello World!'; }); $router->get('/users', 'Users@list'); $dispatcher = new Dispatcher($router); $dispatcher->dispatch('GET', '/greet);
HTTP方法
该路由器支持以下HTTP方法
- GET
- HEAD
- POST
- PUT
- DELETE
- PATCH
- OPTIONS
使用*代表HTTP方法将为上述每种HTTP方法添加一个路由。
对于上述每种方法,路由器中也有相应的简写方法。*的简写是方法'any'。
$router->get('/route', $handler); $router->head('/route', $handler); $router->post('/route', $handler); $router->put('/route', $handler); $router->delete('/route', $handler); $router->patch('/route', $handler); $router->options('/route', $handler); $router->any('/route', $handler);
也可以使用HTTP方法字符串数组定义一个路径的多个HTTP方法。
$router->addRoute(['GET','HEAD'], '/route', $handler);
处理器
路由器接受闭包或形式为'controller@method'的字符串作为处理器。后者将作为闭包存储,并尝试在类上调用该静态方法(如果方法在类上可用,是静态的、非抽象的且公开)或尝试实例化该类并调用该方法。在多个路由匹配的情况下,调度器将只调用已注册的第一个路由的处理程序。
// Closures $router->get('/ping', fn () => 'pong'); $router->get('/sum/{firstNum}/{secondNum}', function ($first, $second) { return $first + $second; }); // Using the controller@method notation $router->get('/users', 'Users@list');
路由
静态路由
静态路由是没有动态、可变组件的路由。这些路由将与URI完全匹配。
$router->get('/login', 'Login@login');
动态路由
动态路由是某些部分可以变动的路由。路由器通过使用占位符({}表示法)或PCRE正则表达式支持动态路由。
请注意,占位符将接受任何输入,即相当于一个(.*?)正则表达式。在调度路由时,所有占位符值都将传递给匹配的路由的处理程序。
当使用正则表达式时,所有由捕获组返回的变量都将传递给匹配的路由的处理程序。
// 'id' which can be one ore more of any of characters // will be passed to Users::update() / Users->update() $router->put('/users/{id}', 'Users@update'); // 'id' which will be one or more digits // will be passed to Users::update() / Users->update() $router->get('/artist/(\d+)', 'Users@update');
可选参数
路由的一部分可以通过使用?标记使正则表达式的一部分成为可选的。(/\d+)?
// will match /albums/2023 or /albums/acdc or /albums $router->get('/albums/{year}?', $handler); // will match /albums/2023 or /albums $router->get('/albums(/d+)?', $handler);
路由分组
可以将路由定义为组,该组将为该组中定义的每个路由应用相同的前缀。
$router->group('/users/{id}', function ($router) use ($id) { $router->get('/posts', 'Users@getPosts'); $router->get('/comments', 'Users@getComments'); }); // is equivalent to $router->get('/users/{id}/posts', 'Users@getPosts'); $router->get('/users/{id}/comments', 'Users@getComments');
未知路由处理器
可以在路由器中定义一个自定义处理器,用于当没有找到路由时。如果有此类处理器已注册,则调度器将在找不到匹配路由时调用此处理器。一次只能定义一个处理器。设置新处理器时,将替换之前的处理器。
$this->router->setRouteNotFoundHandler(fn () => 'Route not found'); // returns 'Route not found' $this->router->getRouteNotFoundHandler();
路由中间件之前
路由中间件是在实际路由处理程序被调用之前执行的处理器。请注意,将为每个路由执行所有已注册的中间件,并且它们将按注册的顺序执行。
$router->before('GET', '/hello-world', fn () => 'Hello '); $router->before('GET', '/hello-world', fn () => 'World!'); $beforeRoute = $router->getBeforeMiddleware('GET', 'hello-world'); $result = ''; foreach ($beforeRoute as $middleware) { $result .= ($middleware['route']->getHandler())(); } // $result contains 'Hello World!'
调度器
与路由器一起的派发器是一种基本实现,它执行了路由器的所有功能。即,它执行已注册的中间件,如果注册了“路由未找到”处理程序,则调用它(否则抛出异常),并使用可选的路由参数调用已注册的路由处理程序,并将结果返回给调用者。
$dispatcher = new Dispatcher($router); $dispatcher->dispatch('GET', '/about-us);
致谢 / 策划
这个路由器/派发器背后的概念和实现受到了 bramus/router 和 FastRoute 的启发和影响。此外,还受到了 Symfony 和 Laravel 的启发。