路由器/ URL Matcher类的问题

时间:2019-02-05 19:41:31

标签: php regex routing

由于正则表达式并不是我的专长,因此我需要帮助解决这个小问题(在PHP中)。 我想将给定的网址与定义的路由数组进行匹配,例如:

$definedRoute = '/admin/user/[:id]/edit';
$url = '/admin/user/37/edit';

在我的类中,我想这样(getRoutes()返回定义的路由数组):

foreach ($this->getRoutes() as $route) {
        $pattern = '~' . preg_replace('~\[\:[a-z]+\]~', '[a-z0-9]+', 
str_replace('/', '\/', $route['definition'])) . '~';
        if (preg_match($pattern, $url)) {
            $parameters = $this->getRouteParameters($route['definition']);
            (new $route['class']())->{$route['method']}($parameters);
            // die? break?
        }
    }

我是这样处理的:用小写字母和数字的正则表达式替换每次出现的[:id]之类的命名参数,例如[a-z0-9]+。 这实际上是可行的,但在某些情况下,它将匹配多个路径,因此匹配错误的路径。另外,在大多数情况下,它始终会与~\/~匹配。但是每个网址只能匹配一次。

编辑#1:问题是:路线多次匹配。我该如何预防?

有人可以启发我吗?

1 个答案:

答案 0 :(得分:0)

我不知道这是否能涵盖所有可能的情况,但是您可以使用preg_match_allpreg_match而不是遍历模式。它还可以提高性能。

这是使匹配顺序(从左到右)很重要,对于数组和循环,您不能做到这一点(实际上可以,但是比较难看)。然后,我们可以按照复杂度对路由进行排序,如下所示:

//this is intentionally in the opposite order of what I want it.
$routes = ['definition' => ['\/', '\/admin\/user\/[a-z0-9]+\/']];

//the more / separators the closer to the beginning we want it. or the more complex regexs go first.
uasort($routes['definition'], function($a,$b){
    //count the number of / in the route
    //note the <=> spaceship (as it's called) is only available in PHP7+
    return substr_count($b, '/') <=> substr_count($a, '/');
});

$url = '/admin/user/37/edit';

//in regex the pipe | is OR
preg_match('~^('.implode('|', $routes['definition']).')~i', $url, $matches);

print_r($matches);

Sandbox

输出:

Array
(
    [0] => Array
        (
            [0] => /admin/user/37/edit
        )

    [1] => Array
        (
            [0] => /admin/user/37/edit
        )

)

在这种情况下哪个是正确的。即使您确实有多个匹配项,也可以计算它们strlen的长度,然后从它们中选择最长或“最佳”的匹配项。使用strlen非常简单,并且可能按长度排序,因此我将由您自己决定。

但是,我不能说这是保证它100%都能正常工作的保证,这只是我的第一件事。

另一个想法

另一个想法是您没有将匹配项锚定到字符串的开头和结尾。理论上,路由可以/应该匹配整个字符串,因此在上面的示例中,如果您在此处添加^$

   preg_match('~^('.implode('|', $routes['definition']).')$~i', $url, $matches);

这将确保完全匹配,并且在这种情况下,~\/~也不会匹配,即使数组没有排序,如下面的沙箱所示。

Sandbox

那说明您只需要/需要部分比赛并不是不可想象的。这取决于您以及如何构建路由和URL。当然,您可以只使用^开头,以及匹配类型开头,但是在这种情况下,您需要对它们进行排序。

Preg Match vs Preg Match All

Preg匹配也将起作用,但只会返回第一个匹配项。因此,如果它匹配多个时间,则无法比较它们以找到最佳时间。如果您使用^$,这可能很好。

希望有帮助。