Added Canvas 1.1.0, originally not under SCM so no historical development records...
[canvas.git] / library / Router2.php
blobf52891ede9bf68263edf1c642612dde84bbde56d
1 <?php
2 // Router v2 (a Canvas component)
3 // @author Matt Todd
4 // @email <mtodd@clayton.edu>
5 // @created_on 23 Apr 2006
7 class Router2 {
8 // internal static variables
9 private static $routes = array(); // the routes provided to route against
10 private static $route = null; // the route for the request
12 // regular expressions for validations
13 private static $validations = array(
14 // primary
15 'controller'=>'([\w_]+)',
16 'action'=>'([\w_]+)',
17 'id'=>'([\d]*)?', // makes the :id optional
19 // character/text
20 'word'=>'([\w\d_+-\.\?]+)',
22 // numeric
23 'numeric'=>'([\d]+)',
25 // specialty
26 'multiple'=>'((%s\/?)+)',
28 // dates
29 'date'=>'(\d\d\d\d-(0[1-9]|1[1-2])-([0-2][0-9]|3[0-2]))',
30 'year'=>'(\d\d\d\d)',
31 'month'=>'(0?[1-9]|1[1-2])',
32 'day'=>'(0?[1-9]|[1-2][0-9]|3[0-2])'
35 // route request
36 public static function route($request) {
37 // compare the request against the available routes
38 foreach(self::$routes as $route=>$options) {
39 if(preg_match("/^{$route}$/", $request, $matches) == 1) break;
42 // map values into $map
43 foreach($options['values'] as $name=>$value) {
44 $map[$name] = $value;
46 array_shift($matches);
47 foreach($matches as $key=>$match) {
48 if($options['names'][$key][strlen($options['names'][$key]) - 1] != '*') {
49 // not a multiple name... (it's not sucking up the rest of the values)
50 $map[$options['names'][$key]] = $match;
51 } else {
52 $map[substr($options['names'][$key], 0, -1)] = explode('/', $match);
53 break;
57 // create new Route object and give it to self::$route, then return it
58 self::$route = new Route($route, $map);
59 return self::$route;
62 // add route
63 public static function map($route, $values = array(), $validates = array()) {
64 // @description Takes a route, like ':controller/:action/:id', and values to include in the route when not specified
65 // bythe actual route itself. This is handy for complex requests with simplified routes.
67 $request = $route;
69 // parse $route into regex
70 if($route[0] == '/') $route = substr($route, 1); // strip out initial slash
71 $route = explode('/', $route); // pull route into its parts
72 foreach($route as $key=>$part) {
73 // pull out names (start with a ':')
74 if($part[0] == ':') {
75 $name = substr($part, 1);
76 $names[] = $name;
77 } else {
78 // literal value
79 $name = $part;
82 // put in regular expression pieces (opting for whatever is in $validates, using 'word' as default unless it's a literal)
83 if(self::$validations[$name]) $test = self::$validations[$name]; // default values
84 if($validates[$name]) $test = self::$validations[$validates[$name]]; // use specific validations as often as possible
85 if($part[0] != ':') $test = $part; // just keep literals
86 if(empty($test)) $test = self::$validations['word']; // last resort
87 if($name[strlen($name) - 1] == '*') $test = sprintf(self::$validations['multiple'], $test);
88 $route[$key] = $test;
89 $test = ''; // reset $test;
92 // assemble new regular expression route for testing
93 $route = implode('\/', $route);
95 // assign $routes with appropriate data
96 self::$routes[$route] = array('route'=>$request, 'names'=>$names, 'values'=>$values, 'validates'=>$validates);
98 return $route;
101 // generate a route, given a Route object
102 public static function url_for($request = array()) {
103 // loop through routes, matching them
104 foreach(self::$routes as $route=>$options) {
105 // default to none found
106 $found = false;
108 // find an appropriate route
109 if(is_array($request)) {
110 // if it's an array
111 foreach(array_keys($request) as $key) {
112 if((is_array($options['names']) && array_search($key, $options['names']) !== false) || ($options['values'][$key] == $request[$key])) {
113 $found = true;
114 } else {
115 $found = false;
116 break;
119 if($found == true) break;
120 } else {
121 // if it's a string, return the routing data
122 if(preg_match("/^{$route}$/", $request) > 0) {
123 return array($route=>$options);
128 if($found) {
129 // create new URL
131 // get the route (to form the URL)
132 $url = $options['route']; // the route
134 // loop through the names, inserting the values into the route/URL
135 foreach($options['names'] as $name) {
136 $url = str_replace(":{$name}", $request[$name], $url);
139 // that's it! the Request or Response objects will handle the domain data (because that's their domain!)
141 return $url;
142 } else {
143 // return failure
148 // the tangible, active part of the routing system, representing an actual route
149 class Route {
150 // variables
151 private $map = array(); // the route mapping
152 private $route = ''; // the route requested
154 // magic functions
155 public function __get($name) {
156 return $this->map[$name];
158 public function __set($name, $value) {
159 return $this->map[$name] = $value;
162 // give the route's mapping
163 public function mapping() {
164 return array_keys($this->map);
167 // constructor
168 public function __construct($route, $map) {
169 $this->route = $route;
170 $this->map = $map;