Reputation: 897
I'm trying to create a mock regex pattern that filters laravel like routes but I'm having trouble getting my regular expression figured out. I want to replace {variable} from the route and match it to a uri.
example:
Route:
/user/{id}
/user/{id}/edit
URI:
/user/1
/user/1/edit
Current Code (following code is in a foreach loop that iterates through routes)
//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.";
}
The issue is that when the second uri above(/user/1/edit) is entered into the browser, it will match it to the first route instead of the second route. I assume because I'm replacing the variable({id}) with (.*?) so it will match anything after the variable.
Upvotes: 2
Views: 312
Reputation: 61885
The problem can be demonstrated as thus (which is an actual minimal test-case, work on creating these!)
preg_match("{^/(.*?)/(.*?)/$}", "/foo/bar/", $matches);
var_dump($matches); // captures -> "foo", "bar"
preg_match("{^/(.*?)/$}", "/foo/bar/", $matches);
var_dump($matches); // captures -> "foo/bar"
This is because the non-greedy "?" qualifier says "matches as little as can be matched while still making the best attempt to match". In the second case the regular expression engine first tried to match just "foo" and then since the pattern failed the regular expression backtracked and the capture was forced to capture "foo/bar".
The solution is to prohibit the group from capturing anything it is not allowed to capture.
preg_match("{^/([^/]+)/$}", "/foo/bar/", $matches);
var_dump($matches); // match failed
Upvotes: 2