* @author Danny
*/
final class Router {
-
+
/**
* Array to store named routes in, used for reverse routing.
* @var array
*/
private $named_routes = array();
-
+
/**
* Boolean whether a route has been matched.
* @var boolean
*/
private $route_found = false;
-
+
/**
* The matched route. Contains an array with controller, action and optional parameter values.
* @var array
*/
private $route = array();
-
+
/**
* The base REQUEST_URI. Gets prepended to all route url's.
*
* @var string
*/
private $base_url = '';
-
+
/**
* Creates an instance of the Router class
* @param string $base_url Base url to prepend to all route url's (optional)
public function __construct($base_url = '') {
$this->base_url = $base_url;
}
-
+
/**
* Set the base url - gets prepended to all route url's.
* @param string $base_url
*/
public function setBaseUrl($base_url) {
- $this->base_url = $base_url;
+ $this->base_url = $base_url;
}
-
+
/**
* Has a route been matched?
* @return boolean True if a route has been found, false if not.
public function hasRoute() {
return $this->route_found;
}
-
+
/**
* Get array with data of the matched route.
* @return array Array containing the controller, action and parameters of matched route.
return $this->route;
}
-
/**
* Match a route to the current REQUEST_URI. Returns true on succes (route matches), false on failure.
*
* @return boolean True if route matches URL, false if not.
*/
public function match($route_url, $target = '', array $args = array()) {
-
+
// check if this is a named route, if so, store it.
if (isset($args['as'])) {
$this->named_routes[$args['as']] = $route_url;
}
-
+
// check if a route has already been found
// if so, function doesn't have to run
- if($this->route_found) return;
+ if ($this->route_found)
+ return;
// check for matching method
if (isset($args['via'])) {
$request_url = strtolower(rtrim($request_url, '/'));
// setup route regex for route url
- $route_regex = preg_replace("/:(\w+)/", "(\w+)", $this->base_url.$route_url);
+ $route_regex = preg_replace_callback("/:(\w+)/", function($matches) use ($args) {
+
+ // does match have filter regex set?
+ if (isset($args['filters']) && isset($matches[1]) && isset($args['filters'][$matches[1]])) {
+ return $args['filters'][$matches[1]];
+ }
+
+ return "(\w+)";
+ }, $this->base_url . $route_url);
// check if request url matches route regex. if not, return false.
if (!preg_match("@^{$route_regex}*$@", $request_url, $matches))
return false;
-
-
+
+
// setup parameters
$params = array();
// target explicitly given
$target = explode('#', $target);
- $controller = $target[0];
- $action = (isset($target[1])) ? $target[1] : 'index';
+ if (!isset($params['controller']))
+ $params['controller'] = $target[0];
+ if (!isset($params['action']))
+ $params['action'] = (isset($target[1])) ? $target[1] : 'index';
} else {
// target not explicitly given
// extract from url
- $target = explode('/', ltrim(str_replace($this->base_url,'',$request_url),'/'));
-
- $controller = $target[0];
- $action = (isset($target[1])) ? $target[1] : 'index';
+ $target = explode('/', ltrim(str_replace($this->base_url, '', $request_url), '/'));
+
+ if (!isset($params['controller']))
+ $params['controller'] = $target[0];
+ if (!isset($params['action']))
+ $params['action'] = (isset($target[1])) ? $target[1] : 'index';
}
-
+
+
+ // If route had a :controller segment, use that segment as the target controller
+ $controller = $params['controller'];
+ unset($params['controller']);
+
+ // If route had a :action segment, use that segment as the target action
+ $action = $params['action'];
+ unset($params['action']);
+
$this->route_found = true;
$this->route = array('controller' => $controller, 'action' => $action, 'params' => $params);
return true;
}
-
+
/**
* Matches REST URL's for a given controller
*
public function resources($controller, array $args = array()) {
$routes = array(
'create' => array("/$controller", "$controller#create", array('via' => 'POST')),
- 'index' => array("/".$controller, "$controller#index", array('via' => 'GET', 'as' => $controller)),
- 'new' => array("/$controller/new", "$controller#new", array('via' => 'GET', 'as' => $controller.'#new')),
+ 'index' => array("/" . $controller, "$controller#index", array('via' => 'GET', 'as' => $controller)),
+ 'new' => array("/$controller/new", "$controller#new", array('via' => 'GET', 'as' => $controller . '#new')),
'update' => array("/$controller/:id", "$controller#update", array('via' => 'PUT')),
'destroy' => array("/$controller/:id", "$controller#destroy", array('via' => 'DELETE')),
- 'show' => array("/$controller/:id", "$controller#show", array('via' => 'GET', 'as' => $controller.'#show')),
+ 'show' => array("/$controller/:id", "$controller#show", array('via' => 'GET', 'as' => $controller . '#show')),
'edit' => array("/$controller/:id/edit", "$controller#edit", array('via' => 'GET'))
);
-
+
if (isset($args['only'])) {
// only route to specified methods
$only = explode(',', $args['only']);
if (isset($routes[$o]))
$this->match($routes[$o][0], $routes[$o][1], $routes[$o][2]);
}
-
+
// abandon
return;
} elseif (isset($args['except'])) {
unset($routes[$e]);
}
}
-
+
// loop all routes
foreach ($routes as $r) {
$this->match($r[0], $r[1], $r[2]);
}
}
-
+
/**
* Reverse route a named route
*
* @param array $params Optional array of parameters to use in URL
* @return string The url to the route
*/
- public function reverse($route_name,array $params = array()) {
+ public function reverse($route_name, array $params = array()) {
// Check if route exists
- if(!isset($this->named_routes[$route_name])) return false;
-
+ if (!isset($this->named_routes[$route_name]))
+ return false;
+
$route_url = $this->named_routes[$route_name];
-
+
// replace route url with given parameters
- if($params && preg_match_all("/:(\w+)/", $route_url, $param_keys)) {
+ if ($params && preg_match_all("/:(\w+)/", $route_url, $param_keys)) {
// grab array with matches
$param_keys = $param_keys[1];
// loop trough parameter names, store matching value in $params array
foreach ($param_keys as $i => $key) {
if (isset($params[$key]))
- $route_url = preg_replace("/:(\w+)/",$params[$key],$route_url,1);
+ $route_url = preg_replace("/:(\w+)/", $params[$key], $route_url, 1);
}
}
-
+
return $route_url;
-
}
-
}
\ No newline at end of file
$r = new Router('/rest-router');
// maps / to controller 'users' and method 'index'.
-$r->match('/','users#index');
+$r->match('/', 'users#index');
// maps /user/5 to controller 'users', method 'show' with parameter 'id' => 5
-$r->match('/users/:id','users#show');
+// this route won't match /users/i5 because of the filter regex.
+$r->match('/users/:id', 'users#show', array('filters' => array('id' => '(\d+)')));
// maps POST request to /users/ to controller 'users' and method 'create'
-$r->match('/users','users#create',array('via' => 'post'));
+$r->match('/users', 'users#create', array('via' => 'post'));
// maps /photos/show to controller 'photos' and method 'show'
$r->match('/photos/show');
// maps GET /users/5/edit to controller 'users', method 'edit' with parameters 'id' => 5 and saves route as a named route.
-$r->match('/users/:id/edit','users#edit',array('via' => 'get', 'as' => 'user_edit_page'));
+$r->match('/users/:id/edit', 'users#edit', array('via' => 'get', 'as' => 'user_edit_page'));
-// echoes /users/5/edit
-echo $r->reverse('user_edit_page',array('id' => '5'));
// maps multiple routes
// GET /users will map to users#index
// GET /users/5 will map to users#show
-$r->resources('users',array('only' => 'index,show'));
+$r->resources('users', array('only' => 'index,show'));
-if($r->hasRoute()) {
+if ($r->hasRoute()) {
extract($r->getRoute());
?>
<h1>Route found!</h1>
<?php
} else {
?><h1>No route found.</h1><?php
-}
\ No newline at end of file
+}
+
+?><h3>Reversed routing</h3><?php
+// echoes /users/5/edit
+echo "Route for user_edit_page with ID 5: ". $r->reverse('user_edit_page', array('id' => '5'));
\ No newline at end of file