PHP Dynamic Regular Expression Backbone Router

I already wrote a PHP controller, but I'm rewriting my code, so I can have JSON routes with base routers that map URI patterns to combinations of PHP Class :: methods or directly to HTML documents that are then passed to the client, for example:

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

The array dynamically creates regular expressions to map routes to URLs. I took a look at the trunk code and I extracted the following code (slightly modified):

 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 + '$'); } 

When I pass the route, for example /podcasts/:param1/:param2 , to the code above, I get /^\/podcasts\/([^\/]+)\/([^\/]+)$/ . I tried to write a PHP function to get the exact same regular expression. I tried:

 $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); 

My conclusion:

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

Why is my regex output different? I can process the rest of the matching process and that’s it, but I didn’t understand how to get exactly the same regular expression in PHP as in Javascript. Any suggestions?

+6
source share
1 answer

The JS version does not actually come out of / chars, it creates the same string representation as your PHP version /podcasts/([^/]+)/([^/]+) , but it may have worked , console.log regular expression returned by _routeToRegExp shows a full view with dividing / and inner screens:

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

See http://jsfiddle.net/w6zP2/ for a demonstration.

What the @ajshort note in the comments and what Backbone is is the same solution: skip / escaping and use the alternative syntax; The backbone does this with an explicit call to the Regexp constructor with new RegExp('^' + route + '$') , you can do something like this with $pattern = "~^{$d}$~";

And PHP Fiddle http://phpfiddle.org/main/code/4de-jf3 , which works as expected.

Please note that in Javascript $& , the corresponding substring 1 is inserted in the replacement string and not in PHP 2 . EscapeRegExp should be changed and might look like this:

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

which gives us an updated code:

 $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 See Specifying a String as a Parameter in string.replace
2 See replacement in preg_replace

+2
source

All Articles