I'm having a lot of trouble making one single regex of my own to match my needs. It's an application that I'm trying not to use a framework, except Doctrine, but in that case doesn't matter. I'm using Front Controller pattern and I mapping my routes to controller's methods using annotations.
Like:
/**
* @GetMethod
* @Route(name=index)
* @return UserProfileDto
*/
public function getUserProfile();
Can anyone help to make one single regex to match everything I need?
The rules are:
Required:
Optional:
Here's what I managed to do:
<?php
header("content-type: text/plain");
// example url access: /profile/edit/23.html, /profile/delete/2.json, /profile.xml, /user/list.xml
$string = $_GET['u'];
$matches = array();
$objRoute = new stdClass();
preg_match_all("~^/(?P<controller>[^/\\.]+)~", $string, $matches);
if ($matches && $matches['controller']) {
$objRoute->controller = $matches['controller'][0];
} else {
$objRoute->controller = "index";
}
preg_match_all("~^/$objRoute->controller/(?P<action>[^/\\.]+)~", $string, $matches);
if ($matches && $matches['action']) {
$objRoute->action = $matches['action'][0];
preg_match_all("~^/$objRoute->controller/{$objRoute->action}(?:/[^\\.]+)?\\.(?P<type>.+)$~", $string, $matches);
} else {
preg_match_all("~^/$objRoute->controller\\.(?P<type>.+)$~", $string, $matches);
$objRoute->action = "index";
$objRoute->parameters = null;
}
if ($matches && $matches['type']) {
$objRoute->type = $matches['type'][0];
preg_match_all("~^/$objRoute->controller/{$objRoute->action}(?:/(?P<parameters>[^\\.]+))?\\.{$objRoute->type}$~", $string, $matches);
if ($matches && $matches['parameters'] && $matches['parameters'][0]) {
$objRoute->parameters = explode("/",$matches['parameters'][0]);
} else {
$objRoute->parameters = null;
}
} else {
die("Bad Request, no reponse type provided");
}
// "advanced" example method route @Route(name=edit/{id})
$route = "edit/{id}";
$route = preg_replace("~\\{([^\\}]+)\\}~", '(?P<' . '${1}' . '>[^/]+)', $route);
$routeTo = $objRoute->action . "/" . implode("/",$objRoute->parameters);
if ($objRoute->parameters && count($objRoute->parameters)) {
preg_match_all("~^$route$~", $routeTo , $matches);
}
foreach($matches as $idx => $match) {
if ($match && $match[0] && !is_numeric($idx)) {
$objRoute->{$idx} = $match[0];
}
}
print_r($objRoute);
?>
I would use a single regex to validate/match the whole route string and extract the controller, action, parameters and type from the returned $matches array. The parameters part, which can have a variable number of parts, is parsed afterward. Here is a tested PHP script wit a commented regex that should be a pretty good starting point:
<?php // test.php Rev:20121105_1900
$route = '/controller/action/param1/param2/param3.html';
$re = '%
# Parse controller, action, params and type from route.
^ # Anchor to start of string.
/ # $controller prefix (required).
(?P<controller> # $controller (required).
[^/\\\\.]+ # Value = one or more non-/\..
) # $controller (required).
(?: # Action is optional.
/ # $action prefix.
(?P<action> # $action.
[^/\\\\.]+ # Value = one or more non-/\..
) # $action.
)? # Action is optional.
(?: # Parameters are optional.
(?P<params> # $params.
(?: # One or more parameters
/ # Params separated by a /.
[^/\\\\.]+ # Value = one or more non-/\..
)+ # One or more parameters
) # $params.
)? # Parameters are optional.
\. # $type prefix (required).
(?P<type> # $type (required).
[^/\\\\.]+ # Value = one or more non-/\..
) # $type (required).
$ # Anchor to end of string.
%x';
if (preg_match($re, $route, $matches)) {
// Valid route string.
printf("OK: \"%s\" is a valid route string.\n", $route);
printf( " controller = \"%s\"\n", $matches['controller']);
printf( " type = \"%s\"\n", $matches['type']);
if ($matches['action']) {
printf( " action = \"%s\"\n", $matches['action']);
} else {
printf( " action = (none)\n");
}
if ($matches['params']) {
printf( " params = \"%s\"\n", $matches['params']);
$params = preg_split('%/%', $matches['params'], -1, PREG_SPLIT_NO_EMPTY);
for ($i = 0; $i < count($params); ++$i) {
printf( " p[%d] = \"%s\"\n", $i+1, $params[$i]);
}
} else {
printf( " params = (none)\n");
}
} else {
// Not a valid route string.
printf("ERROR: \"%s\" is NOT a valid route string.\n", $route);
}
?>
OK: "/controller/action/param1/param2/param3.html" is a valid route string.
controller = "controller"
type = "html"
action = "action"
params = "/param1/param2/param3"
p[1] = "param1"
p[2] = "param2"
p[3] = "param3"