PHP - Regex for routing



我正试图创建一个模拟正则表达式模式来过滤类似laravel的路由,但我很难弄清楚我的正则表达式。我想替换路由中的{variable},并将其与uri匹配。

示例:

路线:

 /user/{id}
 /user/{id}/edit

URI:

/user/1
/user/1/edit

当前代码(以下代码位于foreach循环中,该循环遍历路由)

//escape slashes
$pattern = '/^' . str_replace("/", '/', $route) . '$/i';
//strip out variables from route to compare with uri
$pattern = preg_replace('/{(.*?)}/', '(.*?)', $pattern);
if (preg_match($pattern, $uri)){
     echo "Found a match.";
}

问题是,当上面的第二个uri(/user/1/edit)输入到浏览器中时,它会将其与第一个路由匹配,而不是与第二个路由匹配。我假设,因为我将变量({id})替换为(.*?),所以它将匹配变量之后的任何内容。

问题可以这样证明(这是一个实际的最小测试用例,请创建这些!)

preg_match("{^/(.*?)/(.*?)/$}", "/foo/bar/", $matches);
var_dump($matches);    // captures -> "foo", "bar"
preg_match("{^/(.*?)/$}", "/foo/bar/", $matches);
var_dump($matches);    // captures -> "foo/bar"

这是因为非贪婪的"?"限定符表示"只有的匹配可以匹配,而仍在进行最佳匹配"。在第二种情况下,正则表达式引擎首先试图只匹配"foo",然后由于模式失败,正则表达式回溯,捕获被迫捕获"foo/bar"。

解决方案是禁止组捕获任何不允许捕获的内容。

preg_match("{^/([^/]+)/$}", "/foo/bar/", $matches);
var_dump($matches);    // match failed

最新更新