由于正则表达式并不是我的专长,因此我需要帮助解决这个小问题(在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:问题是:路线多次匹配。我该如何预防?
有人可以启发我吗?
答案 0 :(得分:0)
我不知道这是否能涵盖所有可能的情况,但是您可以使用preg_match_all
或preg_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);
输出:
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);
这将确保完全匹配,并且在这种情况下,~\/~
也不会匹配,即使数组没有排序,如下面的沙箱所示。
那说明您只需要/需要部分比赛并不是不可想象的。这取决于您以及如何构建路由和URL。当然,您可以只使用^
开头,以及匹配类型开头,但是在这种情况下,您需要对它们进行排序。
Preg Match vs Preg Match All
Preg匹配也将起作用,但只会返回第一个匹配项。因此,如果它匹配多个时间,则无法比较它们以找到最佳时间。如果您使用^
和$
,这可能很好。
希望有帮助。