maer /router
一个小型、简单、可扩展的单文件PHP路由器,具有分组、过滤器和命名路由功能
Requires
- php: >=5.5
Requires (Dev)
- phpunit/php-timer: 1.*
- phpunit/phpunit: 4.7.*
- squizlabs/php_codesniffer: ^3.2
README
一个小型、简单、可扩展的单文件PHP路由器,具有分组、过滤器和命名路由功能
我并不声称这个路由器比其他路由器更快或更好。打败像nikic/FastRoute这样的东西有点困难。构建这个的主要原因有两个:1. 我想要一个简单但灵活的路由器,即插即用,几乎无需配置。2. 建造东西很有趣,你也会从中学到很多东西!
使用方法
安装
克隆此存储库或使用composer通过以下命令下载库
composer require maer/router dev-master
将dev-master
更改为最后一个已标记的版本。
简单示例
// Load composers autoloader include '/path/to/vendor/autoload.php'; $r = new Maer\Router\Router; // Define routes $r->get('/', function() { return "Hello there"; }); // It also works with: $r->post('/', function() {}); $r->put('/', function() {}); $r->delete('/', function() {}); $r->patch('/', function() {}); $r->options('/', function() {}); $r->head('/', function() {}); $r->connect('/', function() {}); $r->trace('/', function() {}); $r->any('/', function() {}); // Catches all methods // ...or if you want to use some non-standard HTTP verb $r->add('SOMEVERB', '/', function() {}); // ...or if you want to define multiple verbs at once $r->add(['GET', 'POST', ...], function() {}); // Dispatch the router $response = $r->dispatch(); echo $response;
路由参数
你可以使用一些占位符来使用路由参数。所有参数都将按照在路由中定义的顺序传递给控制器
// Match any alpha [a-z] character $r->get('/something/(:alpha)', function($param) { // Do stuff }); // Match any numeric [0-9.,] character. It can also start with a - $r->get('/something/(:num)', function($param) { // Do stuff }); // Match any alphanumeric [a-z0-9] character $r->get('/something/(:alphanum)', function($param) { // Do stuff }); // Match any character (except /) [^/] character $r->get('/something/(:any)', function($param) { // Do stuff }); // Catch-all. Match all routes, including / (.*) $r->get('/something/(:any)', function($param) { // Do stuff }); // Append ? to making a parameter optional. $r->get('/something/(:alpha)?', function($param = null){ // Matches /something and /something/anything }); // Combine mutliple placeholders $r->get('/something/(:alpha)/(:any)/(:alphanum)?', function($param, $param2, $param3 = null) { // Do stuff });
路由回调
路由回调可以以不同的方式定义
// Anonymous function $r->get('/', function() { // Something }); // Class method $r->get('/', ['Namespace\ClassName', 'methodName']); // or $r->get('/', 'Namespace\ClassName@methodName'); // Static class method $r->get('/', 'Namespace\ClassName::methodName');
所有回调都将接收任何路由参数。
如果你发送一个类方法(非静态),当路由器分发并且找到匹配项时,路由器将实例化该类然后调用该方法。
过滤器
有before
和after
过滤器
// Defining filters $r->filter('myfilter', function() { // Do some magic stuff. }); $r->filter('anotherfilter', function() { // Do some magic stuff. }); // Add filter to your routes $r->get('/something/', function() { }, ['before' => 'myfilter', 'after' => 'anotherfilter']); // Add multiple filters by combining them with | $r->get('/something/', function() { }, ['before' => 'myfilter|anotherfilter']);
过滤器回调可以与路由回调相同的格式,这意味着你可以将类方法用作过滤器。
在过滤器之前,将接收所有路由参数,就像路由回调一样。在过滤器之后,也将接收所有参数,但第一个参数将是路由回调的响应。
注意:过滤器的调用顺序与它们定义的顺序相同。如果任何过滤器返回的不是null
,则分发将停止,并返回该响应。
命名路由
给任何路由添加一个名称
// Name a route $r->get('/something', function() { }, ['name' => 'some-page']); // Resolve a named route echo $r->getRoute('some-page'); // Returns: /something // With route parameters $r->get('/something/(:any)/(:any)', function($param1, $param2) { }); // Resolve and pass values for the placeholders echo $r->getRoute('some-page', ['first', 'second']); // Returns: /something/first/second
如果你没有传递足够的参数来覆盖所有必需的参数,将抛出异常。
添加基本URL
默认情况下,路由器返回不带基本URL(协议 + 主机名)的路径
但是,如果你想使getRoute()
的响应包括基本URL,你需要设置基本URL
$r->baseUrl('http://foo.bar');
现在,当你想要获取一个命名路由时,你可以传递一个布尔值作为第三个参数。 true
= 包含基本URL,false
= 不包含。
$r->getRoute('some-page', [], true); // Returns: http://foo.bar/something
如果你不想传递第三个参数,而是默认返回基本URL,你可以使用以下方式设置它
$r->alwaysPrependBaseUrl(true);
如果你调用getRoute()
而不传递第三个参数,它将始终添加基本URL,除非你将false
作为第三个参数传递给getRoute()
。
分组路由
为了避免为许多路由重复添加相同的过滤器,最好将它们分组在一起。
$r->group(['before' => 'a_before_filter'], function($r) { $r->get('/', function() { //.... }); // Just keep defining routes });
$r->group()
-方法仅接受匿名函数作为回调。路由器实例始终作为参数传递给回调。
在定义分组时,你可以添加与路由相同的before
和after
过滤器。你还可以像下面描述的那样为分组使用前缀。
分组前缀
要为分组添加相同的前缀,请使用prefix
参数。
$r->group(['prefix' => '/admin'], function() { // This matches: /admin $r->get('/', function() { }); // This matches: /admin/something $r->get('/something', function() { }); });
在创建分组时,您可以混合使用before
、after
和prefix
。
CRUD 路由
为了简化 CRUD 路由的创建,存在一个crud()
函数。
$r->crud('/posts', 'PostsController', [ 'name' => 'posts', ]);
上面等同于您定义以下路由
$r->get('/posts', 'PostsController@many', [ 'name' => 'posts.many' ]); $r->get('/posts/(:any)', 'PostsController@one', [ 'name' => 'posts.one' ]); $r->post('/posts', 'PostsController@create', [ 'name' => 'posts.create' ]); $r->put('/posts/(:any)', 'PostsController@update', [ 'name' => 'posts.update' ]); $r->delete('/posts/(:any)', 'PostsController@delete', [ 'name' => 'posts.delete' ]);
当然,您也可以在分组内使用crud()
函数。
重定向
您还可以将路由器注册为重定向某些 URL。
注意:所有重定向路由都将触发在已注册的路由之前。
注册重定向
// A simple redirect $r->redirect('/from/path', '/to/path'); // It works with absolute targets as well $r->redirect('/foo', 'https://example.com'); // You can also register a named route as target $r->redirect('/foo', null, [ 'route' => ['someRouteName'], // Add route arguments, if needed 'params' => ['argument1', 'argument2', ...] ]); // If you want to redirect using another http code than 307 (default) $r->redirect('/foo', '/bar', [ 'code' => 301 ]);
您还可以在重定向上注册before
过滤器。
将当前请求重定向到命名路由
使用上述方法注册重定向时,它将在调度时处理,就像任何其他路由一样。
有时您可能希望立即重定向用户,也许在路由器已经调度之后(甚至之前)。您可以使用toRoute()
来做到这一点。
// Make a redirect to a named route $r->toRoute('someRouteName', [ 'argument1', 'argument2', ... ]); // With another http code than 307 (default) $r->toRoute('someRouteName', [], 301);
上面的代码将立即重定向请求。这就像执行header('location: ...')
一样。
分发路由器
要调度路由器,通常只需要调用$r->dispatch()
方法即可。然而,如果您想要使用特定的 URL 和方法调度路由器(这在编写测试时很有用),可以将它们传递给调度器。
$response = $r->dispatch('GET', '/some/url');
如果您想自己触发所有回调(过滤器和路由回调),例如,如果您正在使用 IoC 容器,请调用$r->getMatch()
方法,您将获得匹配的路由对象。
$r->get('/', function() { }, ['before' => 'beforefilter', 'after' => 'afterfilter', 'name' => 'somename']); $route = $r->getMatch('GET', '/'); // Returns: // object => // pattern => "/group1" // name => "somename", // callback => object(Closure)#8 (0) {} // before => ['beforefilter'], // after => ['afterfilter'], // args => [] // method => "GET"
如果before和after过滤器是闭包,您可以通过以下方式触发它们
$response = $r->executeCallback('beforefilter');
未找到
如果没有匹配,将抛出Maer\Router\NotFoundException
异常。您可以使用$router->notFound()
方法注册一个将被执行的回调。
$r->notFound(function() { return "Ops! The page was not found!"; }); // Callbacks can be in all the same formats as for routes
不允许的方法
如果存在 URL 匹配,但使用了错误的 HTTP 方法,将抛出Maer\Router\MethodNotAllowedException
异常。您可以使用$router->methodNotAllowed()
方法注册一个将被执行的回调。
$r->methodNotAllowed(function() { return "Ops! Method not allowed!"; }); // Callbacks can be in all the same formats as for routes
添加自定义回调解析器
如果您的回调格式为['Classname', 'method']
,您可能希望自定义其解析方式。如果您,例如,正在使用某种 IoC 并进行依赖注入,这将很有用。
要创建自定义解析器,请使用$r->resolver()
方法。示例
$r->resolver(function($callback) use($container) { // The argument will always be an array with ['Class', 'method'] return [ $container->get($callback[0]), $container[1] ]; });
发布说明
1.5.0
- 添加了重定向方法redirect()
- 添加了重定向方法toRoute()
1.4.0
- 添加了
crud()
方法。
如果您有任何问题、建议或问题,请告诉我!
编码愉快!