在PHP中使用动态正则表达式的类骨干路由器

时间:2013-01-31 17:43:13

标签: php regex backbone.js router

我已经编写了一个PHP控制器,但我正在重写我的代码,所以我可以使用类似Backbone路由器的JSON路由将URI模式映射到PHP Class ::方法组合,或者直接传递给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完全相同的正则表达式。有什么建议吗?

1 个答案:

答案 0 :(得分:2)

JS版本实际上并没有转义/ chars,它产生与PHP版本/podcasts/([^/]+)/([^/]+)相同的字符串表示形式,但是,这可能是你绊倒的一个,console.log返回的正则表达式_routeToRegExp显示带有分隔符的完整表示形式,并且内部的表示已转义:

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

请参阅http://jsfiddle.net/w6zP2/了解演示。

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

执行类似操作

PHP小提琴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

中的替换