1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
<?php
/**
* Routing class to extract and parse request parameters from the URL.
* @package Core
*/
class Route {
/**
* Name of the route.
* @var string
* @access public
*/
public $name;
/**
* Rule for this route.
* @var mixed
* @access public
*/
public $rule;
/**
* Default parameters for this route.
* @var mixed
* @access public
*/
public $defaults;
/**
* Extracted parameters
* @var array
* @access public
*/
public $params=array();
/**
* Associative array of route rules.
* @var array
* @access private
* @static
*/
private static $rules=array();
/**
* Associative array of route instances.
* @var array
* @access private
* @static
*/
private static $routes = array();
/**
* Constructs a route.
*
* @param string $name Name of the route
* @param mixed $rule Rule for this route
* @param array $defaults Default parameters for the route
* @return Route Initialized Route
* @access protected
*/
protected function __construct($name, $rule, $defaults) {
$this->name = $name;
$this->rule = $rule;
$this->defaults = $defaults;
}
/**
* Generates a url for a route
*
* @param array $params Parameters to substitute in the route
* @param bool $absolute Whether to return an absolute url
* @param string $protocol Protocol to use for absolute url
* @return string Generated url
* @access public
*/
public function url($params = array(), $absolute = false, $protocol = 'http') {
if (is_callable($this->rule))
throw new Exception("The rule for '{$this->name}' route is a function and cannot be reversed");
$url = is_array($this->rule)?$this->rule[0]:$this->rule;
$replace = array();
$params = array_merge($this->defaults, $params);
foreach($params as $key => $value)
$replace["<{$key}>"] = $value;
$url = str_replace(array_keys($replace), array_values($replace), $url);
$count = 1;
$chars='[^\(\)]*?';
while($count>0)
$url = preg_replace("#\({$chars}<{$chars}>{$chars}\)#", '', $url, -1, $count);
$url = str_replace(array('(', ')'), '', $url);
if ($absolute)
$url = $protocol.'://'.$_SERVER['HTTP_HOST'].$url;
return $url;
}
/**
* Ads a route
*
* @param string $name Name of the route. Routes with the same name will override one another.
* @param mixed $rule Either an expression to match URI against or a function that will
* be passed the URI and must return either an associative array of
* extracted parameters (if it matches) or False.
* @param array $defaults An associated array of default values.
* @return void
* @access public
* @static
*/
public static function add($name, $rule, $defaults = array()) {
Route::$rules[$name]=array(
'rule'=>$rule,
'defaults'=>$defaults
);
}
/**
* Gets route by name
*
* @param string $name Route name
* @return Route
* @access public
* @throws Exception If specified route doesn't exist
* @static
*/
public static function get($name) {
if (!isset(Route::$rules[$name]))
throw new Exception("Route {$name} not found.");
if (!isset(Route::$routes[$name])) {
$rules = Route::$rules[$name];
Route::$routes[$name] = new static($name, $rules['rule'], $rules['defaults']);
}
return Route::$routes[$name];
}
/**
* Matches the URI against available routes to find the correct one.
*
* @param string $uri Request URI
* @return Route
* @access public
* @throws Exception If no route matches the URI
* @throws Exception If route matched but no Controller was defined for it
* @throws Exception If route matched but no action was defined for it
* @static
*/
public static function match($uri) {
$matched = false;
foreach(Route::$rules as $name=>$rule) {
$rule=$rule['rule'];
if (is_callable($rule)) {
if (($data = $rule($uri)) !== FALSE) {
$matched = $name;
break;
}
}else {
$pattern = is_array($rule)?$rule[0]:$rule;
$pattern = str_replace(')', ')?', $pattern);
$pattern=preg_replace_callback('/<.*?>/',
function($str) use ($rule){
$str=$str[0];
$regexp='[a-zA-Z0-9\-\._]+';
if(is_array($rule))
$regexp=Misc::arr($rule[1],str_replace(array('<','>'),'',$str),$regexp);
return '(?P'.$str.$regexp.')';
},$pattern);
preg_match('#^'.$pattern.'/?$#',$uri,$match);
if(!empty($match[0])){
$matched=$name;
$data=array();
foreach($match as $k=>$v)
if(!is_numeric($k))
$data[$k]=$v;
break;
}
}
}
if($matched==false)
throw new Exception('No route matched your request',404);
$rule=Route::$rules[$matched];
$params=array_merge($rule['defaults'],$data);
if(!isset($params['controller']))
throw new Exception("Route {$matched} matched, but no controller was defined for this route",404);
if(!isset($params['action']))
throw new Exception("Route {$matched} matched with controller {$params['controller']}, but no action was defined for this route",404);
$route=Route::get($matched);
$route->params=$params;
return $route;
}
}
|