aura.router



简介

Aura Router是一个Web路由器的PHP 5.3+的实现。给定一个URI路径和全局变量$_SERVER,它能够解析出控制器、动作和参数值。

你的应用库或框架期望得到匹配的路由信息,并根据路由信息把请求分配到某个控制器。只要你的系统能够提供URI路径和全局变量$_SERVER,你就可以使用Aura Router。

Aura Router的灵感来自于Solar重写规则http://routes.groovie.org


基本用法


映射路由

要为应用创建一个路由,只需实例化aura\router包中的Map类并调用add()方法。

<?php
// create the map object
$map = require '/path/to/aura.router/scripts/instance.php';

// add a short-form named route without params
$map->add('home', '/');

// add a short-form unnamed route with params
$map->add(null, '/{:controller}/{:action}/{:id}');

// add a long-form named route
$map->add('read', array(
    'path' => '/blog/read/{:id}{:format}',
    'params' => array(
        'id'     => '(\d+)',
        'format' => '(\..+)?',
    ),
    'values' => array(
        'controller => 'blog',
        'action'    => 'read'
        'format'    => 'html',
    ),
));

推荐把Map对象放置在全局可用的地方;例如:注册表、服务定位器或者依赖注入容器。讨论这些已超出本文档的范围。


区配路由

要让URI路径区配你的路由映射,只需调用match()方法,并传入路径字符串和$_SERVER值。

<?php
// get the incoming request URI path
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

// get the route based on the path and server
$route = $map->match($path, $_SERVER);

match()方法内部不直接解析URI路径或使用$_SERVER变量。因为不同的系统环境对于这些信息具有不同的表现形式,比如:一个URI对象或环境对象。只要你能够得到路径字符串和服务器环境数组,你就可以在你的代码库或框架中使用Aura Router。

返回的$route对象将包含一个$values数组,数组元素是路由路径识别的参数值。例如,成功匹配路由路径/{:controller}/{:action}/{:id}将会填充$route->values数组,其中,数组的键名与路径中待匹配的参数相对应,分别是:controlleractionid


分配路由

现在你已经得到路由信息了,你可以开始分配它了。下面的示例将演示:一个代码库或框架系统处理路由信息,然后调用页面控制器的过程。

<?php
if (! $route) {
    // no route object was returned
    echo "No application route was found for that URI path.";
    exit();
}

// does the route indicate a controller?
if (isset($route->values['controller'])) {
    // take the controller class directly from the route
    $controller = $route->values['controller'];
} else {
    // use a default controller
    $controller = 'Default';
}

// does the route indicate an action?
if (isset($route->values['action'])) {
    // take the action method directly from the route
    $action = $route->values['action'];
} else {
    // use a default action
    $action = 'index';
}

// instantiate the controller class
$page = new $controller();

// invoke the action method with the route values
echo $page->$action($route->values);

注意,Aura Router不会为你分配路由;上面的代码只是一个演示如何使用路由信息的简单示例。


产生路由路径

如果能从路由中得到URI路径,就能很轻松地创建链接。借助Aura Router,这很容易实现,只需要调用Map对象的generate()方法,并提供路由名称即可。

<?php
// $path => "/blog/read/42.atom"
$path = $map->generate('read', array(
    'id' => 42,
    'format' => '.atom',
));

$href = htmlspecialchars($path, 'UTF-8');
echo '<a href="$href">Atom feed for this blog entry</a>';

Aura Router不会处理动态匹配的路由;每个路由都必须要有一个名字以便产生URI路径。

上面的示例表明,向generate()方法传递一个数据数组作为第二个参数将会把数据插入到路由路径中。第二个参数是可选的。如果有不匹配数据数组键名的路径参数,那些参数{:param}将会被保留。如果有不匹配路由参数的数据数组键名,那些值将不会添加到路由路径中。



高级用法


长格式路由规范

当你添加路由时,你需要定义一个数组,数组键名必须是以下名称中的一个或多个:

  • path — 此路由代表的路径。路径中可能会包含{:param}形式的参数标记。如果你想为参数定义正则表达式的子模式,你可以在params部分为其定义键名,又或是在行内以这种方式定义:{:id:(\d+)}
  • params — 路径参数的正则表达式子模式;行内参数将会覆盖这些设置。例如:
'params' => array(
    'id' => '(\d+)',
)
  • values — 路由的默认值。区配的参数将会覆盖这些设置。
'values' => array(
    'controller' => 'blog',
    'action' => 'read',
    'id' => 1,
)
  • method
    $server['REQUEST_METHOD']必须匹配这些值。
  • secure — 当此值为true时,必须开启$server['HTTPS'],或者请求必须在443端口(译者注:而不是80端口);当此值为false时,上面的要求都不成立。
  • routable — 当此值为false时,路由将不会用来区配路径,仅仅作为产生路径的规则。
  • is_match — 自定义回调或闭包,形式如:function(array $server, \ArrayObject $matches),当匹配时返回真,不匹配时返回假。这就允许开发者建立任何形式的匹配逻辑,并能修改从路径中匹配的参数值$matches
  • generate — 自定义回调或闭包,形式如:function(\aura\router\Route $route, array $data),当产生路径时,返回一个修改过的数组$data

下面是一个完整的长格式路由规范,路由名为read,包含上面提到的所有键名:

<?php
$map->add('read', array(
    'path' => '/blog/read/{:id}{:format}',
    'params' => array(
        'id' => '(\d+)',
        'format' => '(\..+)?',
    ),
    'values' => array(
        'controller' => 'blog',
        'action' => 'read',
        'id' => 1,
        'format' => '.html',
    ),
    'secure' => false,
    'method' => array('GET'),
    'routable' => true,
    'is_match' => function(array $server, \ArrayObject $matches) {

        // disallow matching if referred from example.com
        if ($server['HTTP_REFERER'] == 'http://example.com') {
            return false;
        }

        // add the referer from $server to the match values
        $matches['referer'] = $server['HTTP_REFERER'];
        return true;

    },
    'generate' => function(\aura\router\Route $route, array $data) {
        $data['foo'] = 'bar';
        return $data;
    }
));

注意,使用闭包而不是回调意味着:你不能使用serialize()var_export函数序列化或或导出路由映射,也意味着你不能缓存路由映射。


短格式路由规范

你完全可以使用长格式路由规范。如果你向路由传递一个字符串而非数组 。。。

<?php
$map->add('archive', '/archive/{:year}/{:month}/{:day}');

。。。那么Aura Router将会使用默认的子模式去匹配路径参数(匹配除斜线“/”外的所有字符),并且把路由名称作为默认的动作'action'。因此,上述短格式路由相当于下面的长格式路由:

<?php
$map->add('archive', array(
    'path' => '/archive/{:year}/{:month}/{:day}',
    'params' => array(
        'year'  => '([^/]+)',
        'month' => '([^/]+)',
        'day'   => '([^/]+)',
    ),
    'values' => array(
        'action' => 'archive',
    ),
));


附加路由组

你可以在你应用中的单个“挂载点”下一次性添加一系列路由规则。例如:如果你想把所有和博客相关的路由都挂载到'/blog'下,你可以这么做:

<?php
$map->attach('/blog', array(

    // the routes to attach
    'routes' => array(

        // a short-form route named 'browse'
        'browse' => '/',

        // a long-form route named 'read'
        'read' => array(
            'path' => '/{:id}{:format}',
            'params' => array(
                'id'     => '(\d+)',
                'format' => '(\.json|\.atom)?'
            ),
            'values' => array(
                'format' => '.html',
            ),
        ),

        // a short-form route named 'edit'
        'edit' => '/{:id:(\d+)}/edit',
    ),
));

每个路由路径都会加上/blog前缀,因此有效的路径将变成:

  • browse: /blog/
  • read: /blog/{:id}{:format}
  • edit: /blog/{:id}/edit

你还可以在附加路由数组中设置其他的键名;这些设置将会被每个附加路由作为默认设置使用,所以你无需重复设置共用的信息:

<?php
$map->attach('/blog', array(

    // common params for the routes
    'params' => array(
        'id'     => '(\d+)',
        'format' => '(\.json|\.atom)?',
    ),

    // common values for the routes
    'values' => array(
        'controller' => 'blog',
        'format'     => '.html',
    ),

    // the routes to attach
    'routes' => array(
        'browse' => '/',
        'read'   => '/{:id}{:format}',
        'edit'   => '/{:id}/edit',
    ),
));


构造期附加路由组

你可以在某个附加路由数组中配置你的路由,然后把将其传递给Map构造器。这允许你分开进行路由配置和路由构造。

注意:你可以为每个附加路由组指定一个前缀name_prefix作为通用路由信息的一部分;该组的路由名称将会以此值作为前缀。这能够帮助解决不同路由组中相同路由名称冲突的问题。

<?php
$attach = array(
    // attach to /blog
    '/blog' => array(

        // prefix for route names
        'name_prefix' => 'projectname.blog.',

        // common params for the routes
        'params' => array(
            'id' => '(\d+)',
            'format' => '(\.json|\.atom)?',
        ),

        // common values for the routes
        'values' => array(
            'controller' => 'blog',
            'format' => '.html',
        ),

        // the routes to attach
        'routes' => array(
            'browse' => '/',
            'read' => 'path' => '/{:id}{:format}',
            'edit' => '/{:id}/edit',
        ),
    ),

    // attach to '/forum'
    '/forum' => array(
        // prefix for route names
        'name_prefix' => 'projectname.forum.',
        // ...
    ),

    // attach to '/wiki'
    '/wiki' => array(
        // prefix for route names
        'name_prefix' => 'projectname.wiki.',
        // ...
    ),
);

// create the route factory
$route_factory = new \aura\router\RouteFactory;

// create a Map with attached route groups
$map = new \aura\router\Map($route_factory, $attach);

在开发模块化应用程序时,这个技巧就显得尤其有用了。每个程序都可以返回自己的路由组配置,然后系统配置机制将会把每个路由组合并到同一个数组中以供Map使用。例如:

<?php
// get a routes array from each application packages
$attach = array(
    '/blog'  => require 'projectname/blog/routes.php',
    '/forum' => require 'projectname/forum/routes.php',
    '/wiki'  => require 'projectname/wiki/routes.php',
);

// create the route factory
$route_factory = new \aura\router\RouteFactory;

// create a Map with attached route groups
$map = new \aura\router\Map($route_factory, $attach);


缓存路由

在产品发布的时候,你可能会希望缓存路由映射,避免Map对象在每次页面加载时都重新构造路由对象。getRoutes()setRoutes()方法就是为此设计的。

下面是一个使用Map对象实现路由文缓存和恢复(基于文件)的简单示例。

<?php
// create a Map object
$map = require '/path/to/aura.router/instance.php';

// the cache file location
$cache = '/path/to/routes.cache';

// does the cache exist?
if (file_exists($cache)) {

    // restore from the cache
    $routes = unserialize(file_get_contents($cache));
    $map->setRoutes($routes);

} else {

    // build the map routes using add() and attach() ...
    // ... ... ...
    // ... then save to the cache for the next page load
    $routes = $map->getRoutes();
    file_put_contents($cache, serialize($routes));

}

注意,如果在路由定义中存在闭包,你就不能缓存Map路由。这是因为闭包不能正确地被缓存。如果你希望使用缓存策略,建议你使用传统的回调形式而不是闭包。

 

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>