Skip to content

Commit a4d68b9

Browse files
committed
1
1 parent 9ddbe92 commit a4d68b9

File tree

936 files changed

+119368
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

936 files changed

+119368
-0
lines changed

frameworks/AltoRouter/composer.lock

Lines changed: 73 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
<?php
2+
3+
class AltoRouter {
4+
5+
/**
6+
* @var array Array of all routes (incl. named routes).
7+
*/
8+
protected $routes = array();
9+
10+
/**
11+
* @var array Array of all named routes.
12+
*/
13+
protected $namedRoutes = array();
14+
15+
/**
16+
* @var string Can be used to ignore leading part of the Request URL (if main file lives in subdirectory of host)
17+
*/
18+
protected $basePath = '';
19+
20+
/**
21+
* @var array Array of default match types (regex helpers)
22+
*/
23+
protected $matchTypes = array(
24+
'i' => '[0-9]++',
25+
'a' => '[0-9A-Za-z]++',
26+
'h' => '[0-9A-Fa-f]++',
27+
'*' => '.+?',
28+
'**' => '.++',
29+
'' => '[^/\.]++'
30+
);
31+
32+
/**
33+
* Create router in one call from config.
34+
*
35+
* @param array $routes
36+
* @param string $basePath
37+
* @param array $matchTypes
38+
*/
39+
public function __construct( $routes = array(), $basePath = '', $matchTypes = array() ) {
40+
$this->addRoutes($routes);
41+
$this->setBasePath($basePath);
42+
$this->addMatchTypes($matchTypes);
43+
}
44+
45+
/**
46+
* Retrieves all routes.
47+
* Useful if you want to process or display routes.
48+
* @return array All routes.
49+
*/
50+
public function getRoutes() {
51+
return $this->routes;
52+
}
53+
54+
/**
55+
* Add multiple routes at once from array in the following format:
56+
*
57+
* $routes = array(
58+
* array($method, $route, $target, $name)
59+
* );
60+
*
61+
* @param array $routes
62+
* @return void
63+
* @author Koen Punt
64+
* @throws Exception
65+
*/
66+
public function addRoutes($routes){
67+
if(!is_array($routes) && !$routes instanceof Traversable) {
68+
throw new \Exception('Routes should be an array or an instance of Traversable');
69+
}
70+
foreach($routes as $route) {
71+
call_user_func_array(array($this, 'map'), $route);
72+
}
73+
}
74+
75+
/**
76+
* Set the base path.
77+
* Useful if you are running your application from a subdirectory.
78+
*/
79+
public function setBasePath($basePath) {
80+
$this->basePath = $basePath;
81+
}
82+
83+
/**
84+
* Add named match types. It uses array_merge so keys can be overwritten.
85+
*
86+
* @param array $matchTypes The key is the name and the value is the regex.
87+
*/
88+
public function addMatchTypes($matchTypes) {
89+
$this->matchTypes = array_merge($this->matchTypes, $matchTypes);
90+
}
91+
92+
/**
93+
* Map a route to a target
94+
*
95+
* @param string $method One of 5 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PATCH|PUT|DELETE)
96+
* @param string $route The route regex, custom regex must start with an @. You can use multiple pre-set regex filters, like [i:id]
97+
* @param mixed $target The target where this route should point to. Can be anything.
98+
* @param string $name Optional name of this route. Supply if you want to reverse route this url in your application.
99+
* @throws Exception
100+
*/
101+
public function map($method, $route, $target, $name = null) {
102+
103+
$this->routes[] = array($method, $route, $target, $name);
104+
105+
if($name) {
106+
if(isset($this->namedRoutes[$name])) {
107+
throw new \Exception("Can not redeclare route '{$name}'");
108+
} else {
109+
$this->namedRoutes[$name] = $route;
110+
}
111+
112+
}
113+
114+
return;
115+
}
116+
117+
/**
118+
* Reversed routing
119+
*
120+
* Generate the URL for a named route. Replace regexes with supplied parameters
121+
*
122+
* @param string $routeName The name of the route.
123+
* @param array @params Associative array of parameters to replace placeholders with.
124+
* @return string The URL of the route with named parameters in place.
125+
* @throws Exception
126+
*/
127+
public function generate($routeName, array $params = array()) {
128+
129+
// Check if named route exists
130+
if(!isset($this->namedRoutes[$routeName])) {
131+
throw new \Exception("Route '{$routeName}' does not exist.");
132+
}
133+
134+
// Replace named parameters
135+
$route = $this->namedRoutes[$routeName];
136+
137+
// prepend base path to route url again
138+
$url = $this->basePath . $route;
139+
140+
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
141+
142+
foreach($matches as $match) {
143+
list($block, $pre, $type, $param, $optional) = $match;
144+
145+
if ($pre) {
146+
$block = substr($block, 1);
147+
}
148+
149+
if(isset($params[$param])) {
150+
$url = str_replace($block, $params[$param], $url);
151+
} elseif ($optional) {
152+
$url = str_replace($pre . $block, '', $url);
153+
}
154+
}
155+
156+
157+
}
158+
159+
return $url;
160+
}
161+
162+
/**
163+
* Match a given Request Url against stored routes
164+
* @param string $requestUrl
165+
* @param string $requestMethod
166+
* @return array|boolean Array with route information on success, false on failure (no match).
167+
*/
168+
public function match($requestUrl = null, $requestMethod = null) {
169+
170+
$params = array();
171+
$match = false;
172+
173+
// set Request Url if it isn't passed as parameter
174+
if($requestUrl === null) {
175+
$requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
176+
}
177+
178+
// strip base path from request url
179+
$requestUrl = substr($requestUrl, strlen($this->basePath));
180+
181+
// Strip query string (?a=b) from Request Url
182+
if (($strpos = strpos($requestUrl, '?')) !== false) {
183+
$requestUrl = substr($requestUrl, 0, $strpos);
184+
}
185+
186+
// set Request Method if it isn't passed as a parameter
187+
if($requestMethod === null) {
188+
$requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
189+
}
190+
191+
foreach($this->routes as $handler) {
192+
list($method, $_route, $target, $name) = $handler;
193+
194+
$methods = explode('|', $method);
195+
$method_match = false;
196+
197+
// Check if request method matches. If not, abandon early. (CHEAP)
198+
foreach($methods as $method) {
199+
if (strcasecmp($requestMethod, $method) === 0) {
200+
$method_match = true;
201+
break;
202+
}
203+
}
204+
205+
// Method did not match, continue to next route.
206+
if(!$method_match) continue;
207+
208+
// Check for a wildcard (matches all)
209+
if ($_route === '*') {
210+
$match = true;
211+
} elseif (isset($_route[0]) && $_route[0] === '@') {
212+
$pattern = '`' . substr($_route, 1) . '`u';
213+
$match = preg_match($pattern, $requestUrl, $params);
214+
} else {
215+
$route = null;
216+
$regex = false;
217+
$j = 0;
218+
$n = isset($_route[0]) ? $_route[0] : null;
219+
$i = 0;
220+
221+
// Find the longest non-regex substring and match it against the URI
222+
while (true) {
223+
if (!isset($_route[$i])) {
224+
break;
225+
} elseif (false === $regex) {
226+
$c = $n;
227+
$regex = $c === '[' || $c === '(' || $c === '.';
228+
if (false === $regex && false !== isset($_route[$i+1])) {
229+
$n = $_route[$i + 1];
230+
$regex = $n === '?' || $n === '+' || $n === '*' || $n === '{';
231+
}
232+
if (false === $regex && $c !== '/' && (!isset($requestUrl[$j]) || $c !== $requestUrl[$j])) {
233+
continue 2;
234+
}
235+
$j++;
236+
}
237+
$route .= $_route[$i++];
238+
}
239+
240+
$regex = $this->compileRoute($route);
241+
$match = preg_match($regex, $requestUrl, $params);
242+
}
243+
244+
if(($match == true || $match > 0)) {
245+
246+
if($params) {
247+
foreach($params as $key => $value) {
248+
if(is_numeric($key)) unset($params[$key]);
249+
}
250+
}
251+
252+
return array(
253+
'target' => $target,
254+
'params' => $params,
255+
'name' => $name
256+
);
257+
}
258+
}
259+
return false;
260+
}
261+
262+
/**
263+
* Compile the regex for a given route (EXPENSIVE)
264+
*/
265+
private function compileRoute($route) {
266+
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
267+
268+
$matchTypes = $this->matchTypes;
269+
foreach($matches as $match) {
270+
list($block, $pre, $type, $param, $optional) = $match;
271+
272+
if (isset($matchTypes[$type])) {
273+
$type = $matchTypes[$type];
274+
}
275+
if ($pre === '.') {
276+
$pre = '\.';
277+
}
278+
279+
//Older versions of PCRE require the 'P' in (?P<named>)
280+
$pattern = '(?:'
281+
. ($pre !== '' ? $pre : null)
282+
. '('
283+
. ($param !== '' ? "?P<$param>" : null)
284+
. $type
285+
. '))'
286+
. ($optional !== '' ? '?' : null);
287+
288+
$route = str_replace($block, $pattern, $route);
289+
}
290+
291+
}
292+
return "`^$route$`u";
293+
}
294+
}

0 commit comments

Comments
 (0)