<?php /** * Functions for URL routing: given an URL, call the corresponding handler * function (the 'route' to the corresponding output). * * @author Taddeus Kroes * @version 1.0 * @date 14-07-2012 */ namespace WebBasics; require_once 'base.php'; /** * A Router is used to call a handler function with corresponding to an URL. * * Simple example: a website with the pages 'home' and 'contact'. * <code> * function home() { * return 'This is the home page.'; * } * * function contact() { * return 'This is the contact page.'; * } * * $router = new Router(array( * '/home' => 'home', * '/contact' => 'contact' * )); * $response = $router->call_handler('/home'); // 'This is the home page.' * $response = $router->call_handler('/contact'); // 'This is the contact page.' * </code> * * You can use regular expression patterns to specify an URL. Any matches are * passed to the handler function as parameters: * <code> * function page($pagename) { * return "This is the $pagename page."; * } * * $router = new Router(array( * '/(home|contact)' => 'page' * )); * $response = $router->call_handler('/home'); // 'This is the home page.' * $response = $router->call_handler('/contact'); // 'This is the contact page.' * </code> * * @package WebBasics */ class Router extends Base { /** * The regex delimiter that is added to the begin and end of patterns. * * @var string */ const DELIMITER = '%'; /** * An associative array of regex patterns pointing to handler functions. * * @var array */ private $routes = array(); /** * Create a new Router instance. * * @param array $routes An initial list of routes to set. */ function __construct(array $routes=array()) { foreach( $routes as $pattern => $handler ) $this->add_route($pattern, $handler); } /** * Add a route as a (pattern, handler) pair. * * The pattern is regular expression pattern without delimiters. The * function adds '%^' at the begin and '$%' at the end of the pattern as * delimiters. * * The handler function must receive no arguments if the regex pattern * does not contain groups (which are contained in parentheses). If there * are groups, the matches for these are passed to the handler fucntion in * an array. * * @param string $pattern A regex pattern to mach URL's against. * @param mixed $handler The handler function to call when $pattern is matched. * @throws \InvalidArgumentException If $handler is not callable. */ function add_route($pattern, $handler) { if( !is_callable($handler) ) throw new \InvalidArgumentException(sprintf('Handler for patterns "%s" is not callable.', $pattern)); $this->routes[self::DELIMITER.'^'.$pattern.'$'.self::DELIMITER] = $handler; } /** * Call the handler function corresponding to the specified url. * * If any groups are in the matched regex pattern, a list of matches is * passed to the handler function. If the handler function returns FALSE, * the url has not been 'handled' and the next pattern will be checked for * a match. Otherwise, the return value of the handler function is * returned as the result. * * @param string $url An url to match the saved patterns against. * @return mixed FALSE if no pattern was matched, the return value of the * corresponding handler function otherwise. */ function call_handler($url) { foreach( $this->routes as $pattern => $handler ) { if( preg_match($pattern, $url, $matches) ) { array_shift($matches); $result = call_user_func_array($handler, $matches); if( $result !== false ) return $result; } } return false; } } ?>