6

我已经编写了一个 PHP 控制器,但是我正在重写我的代码,这样我就可以拥有类似于 Backbone Router 的 JSON 路由,将 URI 模式映射到 PHP Class::method 组合,或者直接映射到 HTML 文档,然后将其传递给客户端,像这样:

{
  "/home" : "index.html",
  "/podcasts": "podcasts.html",
  "/podcasts/:param1/:param2": "SomeClass::someMethod"
}

Backbone 动态创建正则表达式来匹配路由与 URL。我查看了主干代码,并提取了以下代码(稍作修改):

function _routeToRegExp (route) {
  var optionalParam = /\((.*?)\)/g;
  var namedParam    = /(\(\?)?:\w+/g;
  var splatParam    = /\*\w+/g;
  var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;
  route = route.replace(escapeRegExp, '\\$&')
               .replace(optionalParam, '(?:$1)?')
               .replace(namedParam, function(match, optional){
                 return optional ? match : '([^\/]+)';
               })
              .replace(splatParam, '(.*?)');
  return new RegExp('^' + route + '$');
}

当我将 , 之类的路线传递给/podcasts/:param1/:param2上面的代码时,我得到/^\/podcasts\/([^\/]+)\/([^\/]+)$/. 我试图编写一个 PHP 函数来获得完全相同的正则表达式。我试过了:

$route = '/podcasts/:param1/:param2';
$a = preg_replace('/[\-{}\[\]+?.,\\\^$|#\s]/', '\\$&', $route); // escapeRegExp
$b = preg_replace('/\((.*?)\)/', '(?:$1)?', $a);                // optionalParam
$c = preg_replace('/(\(\?)?:\w+/', '([^\/]+)', $b);             // namedParam
$d = preg_replace('/\*\w+/', '(.*?)', $c);                      // splatParam
$pattern = "/^{$d}$/";
echo "/^\/podcasts\/([^\/]+)\/([^\/]+)$/\n";
echo "{$pattern}\n";
$matches = array();
preg_match_all($pattern, '/podcasts/param1/param2', $matches);
print_r($matches);

我的输出是:

/^\/podcasts\/([^\/]+)\/([^\/]+)$/    // The expected pattern
/^/podcasts/([^\/]+)/([^\/]+)$/       // echo "{$pattern}\n";
Array                                 // print_r($matches);
(
)

为什么我的正则表达式输出不同?我可以处理其余的映射过程,但我还没有弄清楚如何在 PHP 中获得与 Javascript 中完全相同的正则表达式。有什么建议么?

4

1 回答 1

2

JS 版本实际上并没有转义 / 字符,它产生与您的 PHP 版本相同的字符串表示形式, /podcasts/([^/]+)/([^/]+)但是,这可能是让您感到困惑的原因,console.log返回的正则表达式的 a_routeToRegExp显示带有分隔 / 的完整表示形式和内部的转义:

//Firefox output
RegExp /^\/podcasts\/([^\/]+)\/([^\/]+)$/ 

有关演示,请参见http://jsfiddle.net/w6zP2/ 。

@ajshort 在评论中指出的和 Backbone 所做的是相同的解决方案:跳过 / 转义并使用替代语法;Backbone 通过显式调用 Regexp 构造函数来完成此new RegExp('^' + route + '$')操作,您可以执行类似的操作$pattern = "~^{$d}$~";

还有一个似乎可以按预期工作的 PHP Fiddle http://phpfiddle.org/main/code/4de-jf3 。

请注意,在$&替换字符串中的 Javascript 中插入匹配的子字符串1,而不是在 PHP 2中。escapeRegExp 应该被修改并且可能看起来像:

$a = preg_replace('/[\-{}\[\]+?.,\\\^$|#~\s]/', '\\\\$0', $route);

这给了我们一个更新的代码:

$a = preg_replace('/[\-{}\[\]+?.,\\\^$|#~\s]/', '\\\\$0', $route); // escapeRegExp
$b = preg_replace('/\((.*?)\)/', '(?:$1)?', $a);                // optionalParam
$c = preg_replace('/(\(\?)?:\w+/', '([^\/]+)', $b);             // namedParam
$d = preg_replace('/\*\w+/', '(.*?)', $c);                      // splatParam
$pattern = "~^{$d}$~";

echo "/^\/podcasts\/([^\/]+)\/([^\/]+)$/\n";
echo "{$pattern}\n";
$matches = array();
preg_match_all($pattern, '/podcasts/param1/param2', $matches);
print_r($matches);

1请参阅在string.replace中将字符串指定为参数2请参阅preg_replace中的替换

于 2013-02-01T05:00:28.360 回答