monomelodies / reroute
Requires
- jakeasmith/http_build_url: ^1.0.0
- league/pipeline: ^0.1.0
- zendframework/zend-diactoros: ^1.2
Requires (Dev)
- phpunit/phpunit: 4.0.*
- dev-master
- 3.4.7
- 3.4.6
- 3.4.5
- 3.4.4
- 3.4.3
- 3.4.2
- 3.4.1
- 3.4.0
- 3.3.4
- 3.3.3
- 3.3.2
- 3.3.1
- 3.3.0
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.4
- 3.1.3
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.0
- 1.1.8
- 1.1.7
- 1.1.6
- 1.1.5
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.0
- 0.1.9
- 0.1.8
- 0.1.7
- 0.1.6
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1.0
- 0.0.4
- 0.0.3
- 0.0.2
- 0.0.1
- dev-develop
This package is auto-updated.
Last update: 2022-02-01 12:43:45 UTC
README
灵活的PHP5 HTTP路由器,支持多种类型的URL匹配、URL参数、自定义状态处理和URL生成。Reroute旨在适用于任何类型的项目或框架。
安装
Composer (推荐)
composer require monomelodies/reroute
手动安装
- 获取代码;
- 从GitHub等地方克隆仓库;
- 下载ZIP文件(例如来自GitHub)并解压缩。
- 使项目识别Reroute
- 在您的PSR-4自动加载器中为命名空间
Reroute\\
注册/path/to/reroute/src
(推荐); - 或者,手动包含所需的文件。
- 在您的PSR-4自动加载器中为命名空间
基本用法
when
?then
!
由于Reroute路由器响应HTTP请求,我们使用when
和then
方法进行响应
<?php $router = new Router; $router->when('/some/url/')->then(function () { // Return something. });
when
开始匹配 whenever it can,因此如果您的项目位于(例如)http://my-url.com/bla/my-framework/libs
,则上面的示例路由可以在没有更好定义的情况下匹配/bla/my-framework/libs/some/url/
。
请注意,Reroute匹配URL的部分,因此您的定义的路由以
/
开头没有特殊含义。
when
返回一个新路由器,其指定的URL为“基本”URL(构造函数的第一个参数)。对于嵌套路由器(见下文),这包括所有父路由器的基本URL。示意图如下
<?php $router = new Router; $foo = $router->when('/foo/'); $bar = $foo->when('/bar/'); $baz = $bar->when('/baz/')->then('I match /foo/bar/baz/!');
then
返回的内容可以是任何东西。如果传递一个可调用的参数,那么它最终应该返回非可调用的东西。因此,以下四种形式是等效的
<?php $router->when('/some/url/')->then(function () { return 'Hello world!'; });; $router->when('/some/url/')->then('Hello world!'); class Foo { public static function getInstance() { return new Foo; } public function __invoke() { return 'Hello world!'; } } $router->when('/some/url/')->then(new Foo); $router->when('/some/url/')->then(['Foo', 'getInstance']);
命名状态
当调用两个参数时,第一个参数假定是该状态的(最好是唯一的)名称。命名状态可以在任何点通过在路由器上调用get('name_of_state')
来检索
<?php
$router->when('/the/url/')->then('myname', 'handler');
$state = $router->get('myname'); // Ok!
$state instanceof Reroute\State; // true
解析请求
在定义路由后,您将在前端控制器中的某个地方实际解析请求
<?php use Zend\Diactoros\ServerRequestFactory; if ($state = $router(ServerRequestFactory::fromGlobals())) { echo $state; } else { // 404! }
(请注意,您不需要显式传递ServerRequest
对象,路由器默认使用当前请求。)
调用路由器启动一个管道。通过调用路由器的pipe
方法,您可以将中间件添加到堆栈中。
如果找到了与当前URL匹配的有效状态,则管道返回其返回值。否则,它将解析为null
。
要模拟不同于实际请求类型的请求类型,只需更改
$_SERVER['REQUEST_METHOD']
。
传递参数
您的URL实际上是正则表达式,因此您可以将变量匹配到回调中
<?php $router->when("/(?'name'\w+)/")->then(function ($name) { return "Hi there, $name!"; });
变量可以是命名的(在这种情况下,您传递给回调的顺序无关紧要 - Reroute通过反射来决定最佳匹配)或匿名的(在这种情况下,将按顺序传递)。
简写占位符
对于更简单的URL,您还可以使用一些简写占位符。以下三个语句是相同的
<?php $router->when("/(?'param'.*?)/"); $router->when('/:param/'); $router->when('/{param}/');
在使用占位符时,请注意,您对参数类型的控制较少。使用正则表达式更强大,因为您可以强制例如"/(?'id'\d+)/"
匹配整数。PHP 7支持在可调用中扩展类型提示,因此这将在未来的版本中得到改进。
检查当前请求
通过将参数类型提示为Psr\Http\Message\RequestInterface
的实例,您可以注入原始请求对象并检查使用的方法(或任何其他内容)
<?php use Psr\Http\Message\RequestInterface; $router->when('/some/url/')->then(function (RequestInterface $request) { switch ($request->getMethod()) { case 'POST': // Perform some action case 'GET': return 'ok'; default: return $request->getMethod()." method not allowed."; } });
限制到动词(或扩展调色板)
默认行为是仅匹配GET
和POST
操作,因为它们在Web应用程序中最常见。通常,对静态页面的POST
应类似于GET
。然而,可以特别指示某些URL响应特定方法
<?php use Zend\Diactoros\Response\EmptyResponse; $router->when('/some/url/')->then('my-awesome-state', function () { // Get not allowed! return new EmptyResponse(403); })->post(function () { // ...do something, POST is allowed... // Since we disabled get, this should redirect somewhere valid afterwards. });
可用的动词方法是post
、put
、delete
、head
和options
。后续调用扩展当前状态,并在重新声明时覆盖任何现有操作。
引用其他回调
将参数类型提示为匹配定义的操作(大写)的callable
可以用来“链式”到另一个操作。因此,以下模式对于需要在例如POST
上进行特殊处理的URL是常见的
<?php $router->when('/some/url/')->then('my-state', function() { return 'This is a normal page'; })->post(function (callable $GET) { // Perform some action... return $GET; });
请注意,不需要将任何URL参数重新传递给可调用;它们将自动注入。因此,对get
、post
等的调用可能在不同顺序中接受/识别不同的参数。
自定义动词回调不会“冒泡”路由链。因此,专门禁用
POST
在/foo/
上不会影响/foo/bar/
的默认行为。
如果注入的操作不适用于此状态,则返回405错误。
分组
when
的第二个可选参数是一个可调用对象,它期望一个参数:一个新的(子)路由器。使用when
在子路由器上定义的所有路由都将继承父路由器的URL
<?php $router->when('/foo/', function ($router) { $router->then('I match /foo/!'); $router->when('/bar/')->then('I match /foo/bar/!'); });
由于when
也返回新的子路由器,因此如果您愿意,也可以使用以下模式之一
<?php $router->when('/foo/')->when('/bar/')->then('I match /foo/bar/!'); // ...or... $foo = $router->when('/foo/'); $foo->when('/bar/')->then('I match /foo/bar/!');
为了方便链式调用,then
返回(子)路由器本身
<?php $router->when('/foo/') ->then('I match /foo/!') ->when('/bar/') ->then('But I match /foo/bar/!');
管道中间件
由于路由是管道化的,因此您可以在任何位置添加一个或多个对pipe
方法的调用以添加中间件
<?php $router->when('/restricted/') ->pipe(function ($payload) { if (!user_is_authenticated()) { // In the real world, probably raise an exception you can // catch elsewhere and show a login page or something... return null; } return $payload; }) ->when('/super-secret-page/') ->then('For authenticated eyes only!');
您可以多次调用pipe
。如果管道在任何地方短路,则不会执行子路由器。
当使用命名参数时,管道化的可调用对象可以可选地指定它还想使用哪些参数
<?php $router->when("/(?'foo':\d+)/") ->pipe(function ($payload, $foo) { if ($foo != 42) { // return error response or something... } return $payload; });
这与状态解析可调用类似,不同之处在于始终有一个第一个参数$payload
,并且无法注入$request
。
这种用法的一个常见示例是在一组路由中定义一个管道,用于第一个$language
参数,并将一些环境变量设置为它的值,供所有底层路由使用。
生成URL
要生成一个已命名状态的URL,请使用generate
方法。
<?php $router->when('/:some/:params/')->then('myname', 'handler'); echo $router->generate('myname', ['some' => 'foo', 'params' => 'bar']); // outputs: /foo/bar/
generate
方法的可选第三个参数是一个布尔值,表示当用户已经在当前主机上时,是否应该优先选择不带协议/主机名的路由。默认值为true。如果第三个参数为false
,上述示例可能输出https:///foo/bar/
。
由于匿名状态显然只能通过其实际URL检索(在这种情况下,您可以直接使用硬编码...),因此生成操作仅适用于命名状态。如果您的URL可能随时间变化,请使用命名状态!
处理404和其他错误
<?php $router->when(null)->then('404', function() { return "The URL did an oopsie!"; });
通过传递null
作为URL,内部会生成一些随机的值,通常不会与路由表中的任何实际内容匹配。因此,这是一个安全的占位符。但您可以使用任何内容,只要它不在您的应用程序中使用即可。
接下来,尝试解析当前请求的URI。如果失败,则使用404状态。
<?php if ($state = $router()) { echo $state; } else { // Note that we must "invoke" the state. echo $router->get('404')(); }
最佳实践是将状态解析包裹在try/catch
块中,并相应地处理任何错误,以便视图/控制器等可以抛出异常。