PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/apps/sweeper/system/classes/kohana/route.php

https://github.com/adammoore/Swiftriver
PHP | 352 lines | 159 code | 45 blank | 148 comment | 15 complexity | 32adbd388d9b9c48767df02d756b763f MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Routes are used to determine the controller and action for a requested URI.
  4. * Every route generates a regular expression which is used to match a URI
  5. * and a route. Routes may also contain keys which can be used to set the
  6. * controller, action, and parameters.
  7. *
  8. * Each <key> will be translated to a regular expression using a default
  9. * regular expression pattern. You can override the default pattern by providing
  10. * a pattern for the key:
  11. *
  12. * // This route will only match when <id> is a digit
  13. * Route::set('user/edit/<id>', array('id' => '\d+'));
  14. *
  15. * // This route will match when <path> is anything
  16. * Route::set('<path>', array('path' => '.*'));
  17. *
  18. * It is also possible to create optional segments by using parentheses in
  19. * the URI definition:
  20. *
  21. * // This is the standard default route, and no keys are required
  22. * Route::set('(<controller>(/<action>(/<id>)))');
  23. *
  24. * // This route only requires the <file> key
  25. * Route::set('(<path>/)<file>(<format>)', array('path' => '.*', 'format' => '\.\w+'));
  26. *
  27. * Routes also provide a way to generate URIs (called "reverse routing"), which
  28. * makes them an extremely powerful and flexible way to generate internal links.
  29. *
  30. * @package Kohana
  31. * @author Kohana Team
  32. * @copyright (c) 2008-2009 Kohana Team
  33. * @license http://kohanaphp.com/license
  34. */
  35. class Kohana_Route {
  36. const REGEX_KEY = '<([a-zA-Z0-9_]++)>';
  37. const REGEX_SEGMENT = '[^/.,;?\n]++';
  38. const REGEX_ESCAPE = '[.\\+*?[^\\]${}=!|]';
  39. /**
  40. * @var string default action for all routes
  41. */
  42. public static $default_action = 'index';
  43. // List of route objects
  44. protected static $_routes = array();
  45. /**
  46. * Stores a named route and returns it.
  47. *
  48. * @param string route name
  49. * @param string URI pattern
  50. * @param array regex patterns for route keys
  51. * @return Route
  52. */
  53. public static function set($name, $uri, array $regex = NULL)
  54. {
  55. return Route::$_routes[$name] = new Route($uri, $regex);
  56. }
  57. /**
  58. * Retrieves a named route.
  59. *
  60. * @param string route name
  61. * @return Route
  62. * @return FALSE when no route is found
  63. */
  64. public static function get($name)
  65. {
  66. if ( ! isset(Route::$_routes[$name]))
  67. {
  68. throw new Kohana_Exception('The requested route does not exist: :route',
  69. array(':route' => $name));
  70. }
  71. return Route::$_routes[$name];
  72. }
  73. /**
  74. * Retrieves all named routes.
  75. *
  76. * @return array named routes
  77. */
  78. public static function all()
  79. {
  80. return Route::$_routes;
  81. }
  82. /**
  83. * Get the name of a route.
  84. *
  85. * @return string
  86. */
  87. public static function name(Route $route)
  88. {
  89. return array_search($route, Route::$_routes);
  90. }
  91. /**
  92. * Saves or loads the route cache.
  93. *
  94. * @param boolean cache the current routes
  95. * @return void when saving routes
  96. * @return boolean when loading routes
  97. */
  98. public static function cache($save = FALSE)
  99. {
  100. if ($save === TRUE)
  101. {
  102. // Cache all defined routes
  103. Kohana::cache('Route::cache()', Route::$_routes);
  104. }
  105. else
  106. {
  107. if ($routes = Kohana::cache('Route::cache()'))
  108. {
  109. Route::$_routes = $routes;
  110. // Routes were cached
  111. return TRUE;
  112. }
  113. else
  114. {
  115. // Routes were not cached
  116. return FALSE;
  117. }
  118. }
  119. }
  120. // Route URI string
  121. protected $_uri = '';
  122. // Regular expressions for route keys
  123. protected $_regex = array();
  124. // Default values for route keys
  125. protected $_defaults = array('action' => 'index');
  126. // Compiled regex cache
  127. protected $_route_regex;
  128. /**
  129. * Creates a new route. Sets the URI and regular expressions for keys.
  130. *
  131. * @param string route URI pattern
  132. * @param array key patterns
  133. * @return void
  134. */
  135. public function __construct($uri = NULL, array $regex = NULL)
  136. {
  137. if ($uri === NULL)
  138. {
  139. // Assume the route is from cache
  140. return;
  141. }
  142. if ( ! empty($regex))
  143. {
  144. $this->_regex = $regex;
  145. }
  146. // Store the URI that this route will match
  147. $this->_uri = $uri;
  148. // Store the compiled regex locally
  149. $this->_route_regex = $this->_compile();
  150. }
  151. /**
  152. * Provides default values for keys when they are not present. The default
  153. * action will always be "index" unless it is overloaded here.
  154. *
  155. * $route->defaults(array('controller' => 'welcome', 'action' => 'index'));
  156. *
  157. * @param array key values
  158. * @return Route
  159. */
  160. public function defaults(array $defaults = NULL)
  161. {
  162. $this->_defaults = $defaults;
  163. return $this;
  164. }
  165. /**
  166. * Tests if the route matches a given URI. A successful match will return
  167. * all of the routed parameters as an array. A failed match will return
  168. * boolean FALSE.
  169. *
  170. * // This route will only match if the <controller>, <action>, and <id> exist
  171. * $params = Route::factory('<controller>/<action>/<id>', array('id' => '\d+'))
  172. * ->matches('users/edit/10');
  173. * // The parameters are now: controller = users, action = edit, id = 10
  174. *
  175. * This method should almost always be used within an if/else block:
  176. *
  177. * if ($params = $route->matches($uri))
  178. * {
  179. * // Parse the parameters
  180. * }
  181. *
  182. * @param string URI to match
  183. * @return array on success
  184. * @return FALSE on failure
  185. */
  186. public function matches($uri)
  187. {
  188. if ( ! preg_match($this->_route_regex, $uri, $matches))
  189. return FALSE;
  190. $params = array();
  191. foreach ($matches as $key => $value)
  192. {
  193. if (is_int($key))
  194. {
  195. // Skip all unnamed keys
  196. continue;
  197. }
  198. // Set the value for all matched keys
  199. $params[$key] = $value;
  200. }
  201. foreach ($this->_defaults as $key => $value)
  202. {
  203. if ( ! isset($params[$key]) OR $params[$key] === '')
  204. {
  205. // Set default values for any key that was not matched
  206. $params[$key] = $value;
  207. }
  208. }
  209. return $params;
  210. }
  211. /**
  212. * Generates a URI for the current route based on the parameters given.
  213. *
  214. * @param array URI parameters
  215. * @return string
  216. */
  217. public function uri(array $params = NULL)
  218. {
  219. if ($params === NULL)
  220. {
  221. // Use the default parameters
  222. $params = $this->_defaults;
  223. }
  224. else
  225. {
  226. // Add the default parameters
  227. $params += $this->_defaults;
  228. }
  229. // Start with the routed URI
  230. $uri = $this->_uri;
  231. if (strpos($uri, '<') === FALSE AND strpos($uri, '(') === FALSE)
  232. {
  233. // This is a static route, no need to replace anything
  234. return $uri;
  235. }
  236. while (preg_match('#\([^()]++\)#', $uri, $match))
  237. {
  238. // Search for the matched value
  239. $search = $match[0];
  240. // Remove the parenthesis from the match as the replace
  241. $replace = substr($match[0], 1, -1);
  242. while(preg_match('#'.Route::REGEX_KEY.'#', $replace, $match))
  243. {
  244. list($key, $param) = $match;
  245. if (isset($params[$param]))
  246. {
  247. // Replace the key with the parameter value
  248. $replace = str_replace($key, $params[$param], $replace);
  249. }
  250. else
  251. {
  252. // This group has missing parameters
  253. $replace = '';
  254. break;
  255. }
  256. }
  257. // Replace the group in the URI
  258. $uri = str_replace($search, $replace, $uri);
  259. }
  260. while(preg_match('#'.Route::REGEX_KEY.'#', $uri, $match))
  261. {
  262. list($key, $param) = $match;
  263. if ( ! isset($params[$param]))
  264. {
  265. // Ungrouped parameters are required
  266. throw new Kohana_Exception('Required route parameter not passed: :param',
  267. array(':param' => $param));
  268. }
  269. $uri = str_replace($key, $params[$param], $uri);
  270. }
  271. // Trim all extra slashes from the URI
  272. $uri = preg_replace('#//+#', '/', rtrim($uri, '/'));
  273. return $uri;
  274. }
  275. /**
  276. * Returns the compiled regular expression for the route. This translates
  277. * keys and optional groups to a proper PCRE regular expression.
  278. *
  279. * @access protected
  280. * @return string
  281. */
  282. protected function _compile()
  283. {
  284. // The URI should be considered literal except for keys and optional parts
  285. // Escape everything preg_quote would escape except for : ( ) < >
  286. $regex = preg_replace('#'.Route::REGEX_ESCAPE.'#', '\\\\$0', $this->_uri);
  287. if (strpos($regex, '(') !== FALSE)
  288. {
  289. // Make optional parts of the URI non-capturing and optional
  290. $regex = str_replace(array('(', ')'), array('(?:', ')?'), $regex);
  291. }
  292. // Insert default regex for keys
  293. $regex = str_replace(array('<', '>'), array('(?P<', '>'.Route::REGEX_SEGMENT.')'), $regex);
  294. if ( ! empty($this->_regex))
  295. {
  296. $search = $replace = array();
  297. foreach ($this->_regex as $key => $value)
  298. {
  299. $search[] = "<$key>".Route::REGEX_SEGMENT;
  300. $replace[] = "<$key>$value";
  301. }
  302. // Replace the default regex with the user-specified regex
  303. $regex = str_replace($search, $replace, $regex);
  304. }
  305. return '#^'.$regex.'$#uD';
  306. }
  307. } // End Route