Contributed by
Nicolas Grekas
in #26059 and #26169.

依照独立跑分,Symfony 4 是 最快的框架,但我们持续努力令其更快。在 Symfony 4.1 中,我们改进了 Routing组件 让它在匹配到到访URL时变得更快。

在web程序中,路由被切分为两个主要的动作: generation(生成),根据给定的路由及其参数生成URL;以及 matching(匹配),决定哪个PHP代码(即controller)会被执行以作为到访URL的响应。

为了在编译阶段加速程序,Symfony 生成了一个名为 "matcher" 的PHP类,包含全部路由定义,为到访的URL而专门优化。例如,这是一个为 Symfony 演示程序 而生成的码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// var/cache/prod/srcProdDebugProjectContainerUrlMatcher.php
class srcProdDebugProjectContainerUrlMatcher
{
    // ...
 
    public function match($rawPathinfo)
    {
        // ...
 
        // blog_post
        if (preg_match('#^/(?P<_locale>en|fr|de|es)/blog/posts/(?P<slug>[^/]++)$#s', $pathinfo, $matches)) {
            if ('GET' !== $canonicalMethod) {
                $allow[] = 'GET';
                goto not_blog_post;
            }
 
            return $this->mergeDefaults(array_replace($matches, array('_route' => 'blog_post')), array (  '_controller' => 'App\\Controller\\BlogController::postShow',  '_locale' => 'en',));
        }
        not_blog_post:
 
        // ...
    }
}

根据以下文章中的点子,在 Symfony 4.1 中我们重构了 matcher 类的生成器: Fast request routing using regular expressions。本来解释了 FastRoute 中使用的技巧,这是由天才的PHP贡献者 Nikita Popov 所写的路由类库。

基本想法是,避免在每一个路由中依次调用 preg_match(),取而代之的是,把全部正则合并为一个单一正则。我们也做了其他大大小小的优化。如果你对此感到好奇,参考 Pull Request #26059#26169 以了解细节。

总之,若以相同的 Symfony 演示程序为例,下面是今日 blog_post 路由如何被匹配到的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$regexList = array(
    0 => '{^(?'
            .'|/(en|fr|de|es)/admin/post/?(*:82)'
            .'|/(en|fr|de|es)/admin/post/new(*:166)'
            .'|/(en|fr|de|es)/admin/post/(\\d+)(*:253)'
            .'|/(en|fr|de|es)/admin/post/(\\d+)/edit(*:345)'
            .'|/(en|fr|de|es)/admin/post/([^/]++)/delete(*:442)'
            .'|/(en|fr|de|es)/blog/?(*:519)'
            .'|/(en|fr|de|es)/blog/rss\\.xml(*:603)'
            .'|/(en|fr|de|es)/blog/page/([1-9]\\d*)(*:694)'
            .'|/(en|fr|de|es)/blog/posts/([^/]++)(*:784)'
            .'|/(en|fr|de|es)/blog/comment/([^/]++)/new(*:880)'
            .'|/(en|fr|de|es)/blog/search(*:962)'
            .'|/(en|fr|de|es)/login(*:1038)'
            .'|/(en|fr|de|es)/logout(*:1116)'
            .'|/(en|fr|de|es)?(*:1188)'
        .')$}sD',
);
 
foreach ($regexList as $offset => $regex) {
    // ...
 
    default:
        $routes = array(
            // ...
            784 => array(array('_route' => 'blog_post', '_controller' => 'App\\Controller\\BlogController::postShow', '_locale' => 'en'), array('_locale', 'slug'), array('GET' => 0), null),
        );
 
    // ...
}

实践中,合并全部正则,把URL匹配时的性能给提高了两个数量级。在我们的基准测试中,Symfony 4.1 URL 匹配能力77倍快 于之前的 Symfony 版本。这也意味着 Symfony 4.1 的路由是最快的PHP路由,打败了 FastRoute 和其他所有 routing libraries(路由类库)。

最强的一点: 你毋须在程序中做任何改变 即可使用这个最快的路由系统。只需在 end of May 2018 发布时升级到 Symfony 4.1 即可。同时你还可以在自己的程序中测试它,并把发现的问题上报。

Update: Nicolas 发布了两篇技术文章来详细解释新 router 是如何运作的: