Routing组件

3.4 版本
维护中的版本

Routing组件把一个HTTP请求映射到一组配置变量中

安装 

你可以通过下述两种方式安装:

然后,包容vendor/autoload.php文件,以开启Composer提供的自动加载机制。否则,你的程序将无法找到这个Symfony组件的类。

用法 

为了配置一个基本路由系统,你需要三部分内容:

下面是个速成例子。注意它假设你已经配好自动加载器(autoloader)来加载路由组件了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
 
$route = new Route('/foo', array('controller' => 'MyController'));
$routes = new RouteCollection();
$routes->add('route_name', $route);
 
$context = new RequestContext('/');
 
$matcher = new UrlMatcher($routes, $context);
 
$parameters = $matcher->match('/foo');
// array('controller' => 'MyController', '_route' => 'route_name')

RequestContext 中的参数(译注:原文parameters)可以利用存在$_SERVER中的值来加载(译注:该类在构造时需要传入的值可以从$_SERVER中获得。同时该类有个getParameters()方法),但如果使用HttpFoundation组件会更容易,请参考下文

只要你想,你可以添加任意多的路由到 RouteCollection 中。

RouteCollection::add()方法接收两个参数。第一个是路由名称,第二个是 Route 对象,其构造器预期一个URL路径以及一些(译注:可变参数)“由一些自定义变量所组成的”数组。这个“自定义变量”之数组可以是你的程序认为重要的任何内容,当某路由匹配时该数组会被返回。

UrlMatcher::match()返回你设置在路由中的变量和通配符(见下文)。你的程序现在可以使用这些信息来继续执行请求。除了那些你已经(在路由配置中)配好的变量之外,还有一个_route键被添加(到数组中),它持有当前匹配的路由之名称(name)。

如果没有找到匹配的路由,一个 ResourceNotFoundException 异常被抛出。

定义路由 

一个完整的路由定义,包括以下七部分:

  1. 路由的URL路径。它要与传到RequestContenxt中的URL相匹配,同时要包含指定名称的通配符(如,{placeholders}),以匹配URL中的动态部分。

  2. 一个“默认值”的数组。它包含一个“当请求与路由匹配时”所返回的“可以是任意值”的数组。

  3. 一个“所需条件”之数组。以正则表达式定义了通配符对应值的约束条件。

  4. 一个“选项”的数组。包含了路由的内部配置,是最轻量的常规需求。

  5. 一个host。它要匹配请求中的host部分。请参考中文路由组件之 如何基于Host来匹配路由

  6. 一个schemes的数组。它们负责强制执行一个HTTP scheme(httphttps)。

  7. 一个methods的数组。它们负责强制执行一个特定的HTTP方法(HEADGETPOST...)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$route = new Route(
    '/archive/{month}', // path
    array('controller' => 'showArchive'), // default values
    array('month' => '[0-9]{4}-[0-9]{2}', 'subdomain' => 'www|m'), // requirements
    array(), // options
    '{subdomain}.example.com', // host
    array(), // schemes
    array() // methods
);
 
// ...
 
$parameters = $matcher->match('/archive/2012-01');
// array(
//     'controller' => 'showArchive',
//     'month' => '2012-01',
//     'subdomain' => 'www',
//     '_route' => ...
//  )
 
$parameters = $matcher->match('/archive/foo');
// throws ResourceNotFoundException

上例中,路由被/archive/2012-01匹配到了,因为{month}能够匹配给定的“通配符正则”。但是,/archive/foo并不匹配,因为“foo”无法与month匹配。

使用通配符的话,在调用match方法时这些(参数/parameters)都会以“结果数组”被返回。通配符所“匹配到的”路径部分(如2012-01)作为值来使用。

如果你需要匹配“起于一个特定路径,以任意后缀结束”的全部URLs,可以像下面这样配置路由定义:

1
2
3
4
5
$route = new Route(
    '/start/{suffix}',
    array('suffix' => ''),
    array('suffix' => '.*')
);

使用前缀 

你可以添加路由或 RouteCollection 的其他实例到另一个 collection中。通过这种方式你可以打造路由树(a tree of routes)。而且,你还能对一个使用了“RouteCollection类中的方法”的subtree(子树)的所有路由的参数(parameters)、正则条件(requirements)、选项(options)、schemes和host定义一个前缀和默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$rootCollection = new RouteCollection();
 
$subCollection = new RouteCollection();
$subCollection->add(...);
$subCollection->add(...);
$subCollection->addPrefix('/prefix');
$subCollection->addDefaults(array(...));
$subCollection->addRequirements(array(...));
$subCollection->addOptions(array(...));
$subCollection->setHost('admin.example.com');
$subCollection->setMethods(array('POST'));
$subCollection->setSchemes(array('https'));
 
$rootCollection->addCollection($subCollection);

设置Request参数 

RequestContext 提供了关于当前请求的信息。你可以通过这个类的构造器来定义一个HTTP请求的所有参数:

1
2
3
4
5
6
7
8
9
10
public function __construct(
    $baseUrl = '',
    $method = 'GET',
    $host = 'localhost',
    $scheme = 'http',
    $httpPort = 80,
    $httpsPort = 443,
    $path = '/',
    $queryString = ''
)

通常情况下,你要传入的值来自于$_SERVER变量,以便得到 RequestContext 实例。但是如果你使用HttpFoundation组件的话,你可以使用 Request 类来快速提供 RequestContext 所需之信息:

1
2
3
4
use Symfony\Component\HttpFoundation\Request;
 
$context = new RequestContext();
$context->fromRequest(Request::createFromGlobals());

生成一个URL 

虽然 UrlMatcher 尝试找到一个路由来匹配给定的请求,但你也能从一个特定的路由来构造一个URL:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Routing\Generator\UrlGenerator;
 
$routes = new RouteCollection();
$routes->add('show_post', new Route('/show/{slug}'));
 
$context = new RequestContext('/');
 
$generator = new UrlGenerator($routes, $context);
 
$url = $generator->generate('show_post', array(
    'slug' => 'my-blog-post',
));
// /show/my-blog-post

如果你有定义一个scheme的话,若当前 RequestContext 没有匹配到条件(requirement),则一个绝对URL会被生成。

从文件中加载路由 

你已经看到,在PHP中正确地添加路由到collection(路由集合)中是多么容易了。但你还能够从多种(格式的)文件加载路由。

路由组件内置了一些“类加载器”,它们能令你有能力从“某种格式的外部文件”来加载(load)一组路由定义。loader预期一个 FileLocater 实例作为自己的构造参数。你可以使用 FileLocater 来定义一个“路径数组”以令其找寻对应的文件。如果文件被找到,loader返回一个 RouteCollection

如果你正使用YamlFileLoder,那么路由定义看上去是下面这样:

1
2
3
4
5
6
7
8
# routes.yml
route1:
    path:     /foo
    defaults: { _controller: 'MyController::fooAction' }
 
route2:
    path:     /foo/bar
    defaults: { _controller: 'MyController::foobarAction' }

为了加载上面这个文件,你可以使用以下代码。它假定你的routes.yml文件就在代码中的目录下:

1
2
3
4
5
6
7
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
 
// look inside *this* directory 锁定“这个”目录来查找
$locator = new FileLocator(array(__DIR__));
$loader = new YamlFileLoader($locator);
$collection = $loader->load('routes.yml');

除了 YamlFileLoader,还有两个loaders以相同方式工作:

如果你使用 PhpFileLoader 的话,(在加载时)你需要提供那个PHP文件的名字,该文件返回的是一个 RouteCollection 对象:

1
2
3
4
5
6
7
8
9
10
11
12
// RouteProvider.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
 
$collection = new RouteCollection();
$collection->add(
    'route_name',
    new Route('/foo', array('controller' => 'ExampleController'))
);
// ...
 
return $collection;

Closure路由 

还有一个closure加载器 ClosureLoader,它调用一个匿名函数,将其返回值作为一个 RouteCollection

1
2
3
4
5
6
7
8
use Symfony\Component\Routing\Loader\ClosureLoader;
 
$closure = function () {
    return new RouteCollection();
};
 
$loader = new ClosureLoader();
$collection = $loader->load($closure);

Annotation路由 

最后,还有 AnnotationDirectoryLoaderAnnotationFileLoader 用于从类的注释中(class annotations)来加载路由定义。具体细节暂且不提。

all-in-one Router/一体化路由 

Route 类,是一个一体化的包,用以快速使用路由组件。其构造器预期一个loader实例、一个“相对于主路由定义”的路径(a path to main route definition path)以及一些其他设定:

1
2
3
4
5
6
7
public function __construct(
    LoaderInterface $loader,
    $resource,
    array $options = array(),
    RequestContext $context = null,
    array $defaults = array()
);

通过cache_dir选项,你可以开启路由缓存(route cahing,如果你提供一个path的话),或者关闭缓存(如果它被设为null的话)。在你需要使用的时候,缓存于后台自动实现。 Router 的一个基本使用例程可参考下面:

1
2
3
4
5
6
7
8
9
10
$locator = new FileLocator(array(__DIR__));
$requestContext = new RequestContext('/');
 
$router = new Router(
    new YamlFileLoader($locator),
    'routes.yml',
    array('cache_dir' => __DIR__.'/cache'),
    $requestContext
);
$router->match('/foo/bar');

如果你使用缓存,路由组件将编译新类,存放于cache_dir目录下。这意味着你的脚本在那个目录下,必须有写入的权限。

// TODO...by Xtt1341...and RESUMED by 20160930...It's a real souvenir!

Keep Going! 

  • Symfony起步六篇之路由

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)