monomelodies/reroute

此包已被废弃,不再维护。作者建议使用monolyth/reroute包。

极其灵活的PHP5路由器

3.4.7 2016-03-14 08:24 UTC

README

灵活的PHP5 HTTP路由器,支持多种类型的URL匹配、URL参数、自定义状态处理和URL生成。Reroute旨在适用于任何类型的项目或框架。

安装

Composer (推荐)

composer require monomelodies/reroute

手动安装

  1. 获取代码;
    1. 从GitHub等地方克隆仓库;
    2. 下载ZIP文件(例如来自GitHub)并解压缩。
  2. 使项目识别Reroute
    1. 在您的PSR-4自动加载器中为命名空间 Reroute\\ 注册 /path/to/reroute/src(推荐);
    2. 或者,手动包含所需的文件。

基本用法

whenthen

由于Reroute路由器响应HTTP请求,我们使用whenthen方法进行响应

<?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.";
    }
});

限制到动词(或扩展调色板)

默认行为是仅匹配GETPOST操作,因为它们在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.
});

可用的动词方法是postputdeleteheadoptions。后续调用扩展当前状态,并在重新声明时覆盖任何现有操作。

引用其他回调

将参数类型提示为匹配定义的操作(大写)的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参数重新传递给可调用;它们将自动注入。因此,对getpost等的调用可能在不同顺序中接受/识别不同的参数。

自定义动词回调不会“冒泡”路由链。因此,专门禁用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块中,并相应地处理任何错误,以便视图/控制器等可以抛出异常。