My previous version of php url router was not good enough, so i`ve done some core modification to the class. Its more flexible now. The whole idea is very similar to Rails` and Merb`s url router (merb is a rails-fork). In fact, previous version doesn`t even support GET variables in the request uri. New version just merges variables from request string into the same parameters array where other variables are stored. Also there is a command “default_routes” that maps default routes like this “/:controller/:action/:id”. So, basically you have two options – use defaults & custom routes or use only custom, which means that all other requests will be ignored. Function default_routes must be declared last.
Here is the sources:
define('ROUTER_DEFAULT_CONTROLLER', 'home'); define('ROUTER_DEFAULT_ACTION', 'index'); class Router { public $request_uri; public $routes; public $controller, $controller_name; public $action, $id; public $params; public $route_found = false; public function __construct() { $request = $_SERVER['REQUEST_URI']; $pos = strpos($request, '?'); if ($pos) $request = substr($request, 0, $pos); $this->request_uri = $request; $this->routes = array(); } public function map($rule, $target=array(), $conditions=array()) { $this->routes[$rule] = new Route($rule, $this->request_uri, $target, $conditions); } public function default_routes() { $this->map('/:controller'); $this->map('/:controller/:action'); $this->map('/:controller/:action/:id'); } private function set_route($route) { $this->route_found = true; $params = $route->params; $this->controller = $params['controller']; unset($params['controller']); $this->action = $params['action']; unset($params['action']); $this->id = $params['id']; $this->params = array_merge($params, $_GET); if (empty($this->controller)) $this->controller = ROUTER_DEFAULT_CONTROLLER; if (empty($this->action)) $this->action = ROUTER_DEFAULT_ACTION; if (empty($this->id)) $this->id = null; $w = explode('_', $this->controller); foreach($w as $k => $v) $w[$k] = ucfirst($v); $this->controller_name = implode('', $w); } public function execute() { foreach($this->routes as $route) { if ($route->is_matched) { $this->set_route($route); break; } } } } class Route { public $is_matched = false; public $params; public $url; private $conditions; function __construct($url, $request_uri, $target, $conditions) { $this->url = $url; $this->params = array(); $this->conditions = $conditions; $p_names = array(); $p_values = array(); preg_match_all('@:([\w]+)@', $url, $p_names, PREG_PATTERN_ORDER); $p_names = $p_names[0]; $url_regex = preg_replace_callback('@:[\w]+@', array($this, 'regex_url'), $url); $url_regex .= '/?'; if (preg_match('@^' . $url_regex . '$@', $request_uri, $p_values)) { array_shift($p_values); foreach($p_names as $index => $value) $this->params[substr($value,1)] = urldecode($p_values[$index]); foreach($target as $key => $value) $this->params[$key] = $value; $this->is_matched = true; } unset($p_names); unset($p_values); } function regex_url($matches) { $key = str_replace(':', '', $matches[0]); if (array_key_exists($key, $this->conditions)) { return '('.$this->conditions[$key].')'; } else { return '([a-zA-Z0-9_\+\-%]+)'; } } }
Setup example:
$r = new Router(); // create router instance $r->map('/', array('controller' => 'home')); // main page will call controller "Home" with method "index()" $r->map('/login', array('controller' => 'auth', 'action' => 'login')); $r->map('/logout', array('controller' => 'auth', 'action' => 'logout')); $r->map('/signup', array('controller' => 'auth', 'action' => 'signup')); $r->map('/profile/:action', array('controller' => 'profile')); // will call controller "Profile" with dynamic method ":action()" $r->map('/users/:id', array('controller' => 'users'), array('id' => '[\d]{1,8}')); // define filters for the url parameters $r->default_routes(); $r->execute();
Usage example:
$router = new Router(); // ... some configs ... $controller = $router->controller; // will return name as it appears in url, ex: 'user_images' $controller = $router->controller_name; // will return processed name of controller // for example, if class name in url is 'user_images', then 'controller_name' var will be UserImages $router->action; $router->id; // if parameter :id presents $router->params; // array(...) $router->route_matched; // true - if route found, false - if not
Small and useful class.
