Added Canvas 1.1.0, originally not under SCM so no historical development records...
[canvas.git] / library / Router.php
blobd157f8216abc64800eb5a490ab0c377e8b5da59a
1 <?php
2 // @title Routes handler
3 // @author Matt Todd <matt@matttoddphoto.com>
4 // @created 2005-12-22
5 // @desc A class to handle routing requests (breaking down request URLs to
6 // their property values, returned straight-up
7 // @requires stdexception.php (StdException class)
9 include_once('library/stdexception.php');
11 // classes
12 class Router {
13 private $validators = array(
14 "name"=>'/[\w\d_\?!]+/',
15 "word"=>'/[\w\?!]+/',
16 "number"=>'/\d+/',
17 "date"=>'/(19|20)\d\d-[01]?\d-[0-3]?\d/', // e.g.: 2005-12-24
18 "year"=>'/(19|20)\d\d/', // e.g.: 2005 (but not 2105, sorry)
19 "month"=>'/[01]?\d/', // e.g.: 12 (but not 22)
20 "day"=>'/[0-3]?\d/', // e.g.: 24 (but not 44)
21 "time"=>'/\d\d:\d\d(:\d\d)?/',
22 "filename"=>'/[\w\d_\.]+\.[\w\d]+/',
23 "anything"=>'/.*/',
25 // format but optional
26 "optional name"=>'/[\w\d_\?!]*/',
27 "optional word"=>'/[\w\?!]*/',
28 "optional number"=>'/\d*/'
31 // functions
32 public static function route($request) {
33 // split $request into the components
34 $request = explode('/', ($request . ((substr($request, -1, 1) != '/') ? '/' : '')));
35 array_shift($request); // get rid of the first, tempty element
37 // get routes information from config file
38 $routes = Config::load('routes');
39 $default_route = $routes['default_route'];
40 $routes['default_route'] = null;
42 // loop through the $this->routes property, attempting to match a request
43 foreach($routes as $route) {
44 if($matched_route = self::match($request, $route)) break;
47 if(empty($matched_route)) $matched_route = $default_route;
49 // debugging
50 // crap // Debug::log(str_replace(' ', ' ', str_replace("\n", '', print_r($matched_route, true))), 'internal', 'info', 'Router');
52 return $matched_route;
55 public static function map($route) {
59 private static function match($request, $route) {
60 // create $router instance for properties
61 $router = new Router();
63 // split up route nodes (from '/controller/action' to an array of 'controller','action')
64 $route_nodes = explode('/', $route['route']);
66 // loop through route and requested resource... return if it doesn't match/meet validation requirements, et al
67 while((list(, $request_node) = each($request)) && (list(, $route_node) = each($route_nodes))) {
68 // set the route name (if route just gives name and not explicit value, take out ':' in name and assign it to $route_name
69 if($route_node[0] == ':') $route_name = substr($route_node, 1); else $route_name = $route_node;
70 // if there's no specific validation spacified, set the validation pattern to a pre-defined constant for a valid string
71 if($route_node[0] == ':') $route_node = !empty($route['validate'][$route_name]) ? $router->validators[$route['validate'][$route_name]] : $router->validators['name'];
72 // handle missing/empty parameters (should be fixed to be more flexible!!!)
73 if(empty($request_node) && $route_name == "action") $request_node = "index";
74 if(empty($request_node) && $route_name == "id") $request_node = "null";
75 if(empty($request_node)) $request_node = !empty($route['default'][$route_name]) ? $route['default'][$route_name] : null;
76 // if its a plaintext entry in the route (for only working with certain requests), add /s around the word to make it a valid PCRE regex pattern
77 if($route_node[0] != '/') $route_node = "/{$route_node}/";
79 // debugging
80 // this is really crappy // Debug::log("route name: '{$route_name}', route node: '{$route_node}', request node: '{$request_node}';", 'internal', 'low', 'Router');
82 // test to see if it matches; if not, return null to signify that the request did not match the route, or return the actual route
83 if($request_node == null || preg_match($route_node, $request_node) != 1) {
84 return null; // no match
85 } else {
86 // if it's an ID with the value of "null", convert it to the actual null value
87 if($route_name == "id" && $request_node == "null") $request_node = null;
88 // add data to route array if it matched alright
89 $matched_route[$route_name] = $request_node;
93 // add the routing map to the matched route
94 $matched_route['_map'] = $route;
96 // return matched route
97 return $matched_route;
101 class RoutesException extends StdException {}