PageRenderTime 23ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/Slim/Route.php

https://github.com/iRail/The-DataTank-GUI
PHP | 349 lines | 121 code | 33 blank | 195 comment | 14 complexity | 725eb3f070ee1613dbc24b0cb5d4007f MD5 | raw file
  1. <?php
  2. /**
  3. * Slim - a micro PHP 5 framework
  4. *
  5. * @author Josh Lockhart <info@joshlockhart.com>
  6. * @copyright 2011 Josh Lockhart
  7. * @link http://www.slimframework.com
  8. * @license http://www.slimframework.com/license
  9. * @version 1.5.0
  10. *
  11. * MIT LICENSE
  12. *
  13. * Permission is hereby granted, free of charge, to any person obtaining
  14. * a copy of this software and associated documentation files (the
  15. * "Software"), to deal in the Software without restriction, including
  16. * without limitation the rights to use, copy, modify, merge, publish,
  17. * distribute, sublicense, and/or sell copies of the Software, and to
  18. * permit persons to whom the Software is furnished to do so, subject to
  19. * the following conditions:
  20. *
  21. * The above copyright notice and this permission notice shall be
  22. * included in all copies or substantial portions of the Software.
  23. *
  24. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  28. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  29. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  30. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  31. */
  32. /**
  33. * Route
  34. *
  35. * @package Slim
  36. * @author Josh Lockhart <info@joshlockhart.com>
  37. * @since Version 1.0
  38. */
  39. class Slim_Route {
  40. /**
  41. * @var string The route pattern (ie. "/books/:id")
  42. */
  43. protected $pattern;
  44. /**
  45. * @var mixed The callable associated with this route
  46. */
  47. protected $callable;
  48. /**
  49. * @var array Conditions for this route's URL parameters
  50. */
  51. protected $conditions = array();
  52. /**
  53. * @var array Default conditions applied to all Route instances
  54. */
  55. protected static $defaultConditions = array();
  56. /**
  57. * @var string The name of this route (optional)
  58. */
  59. protected $name;
  60. /**
  61. * @var array Key-value array of URL parameters
  62. */
  63. protected $params = array();
  64. /**
  65. * @var Slim_Router The Router to which this Route belongs
  66. */
  67. protected $router;
  68. /**
  69. * @var array[Callable] Middleware
  70. */
  71. protected $middleware = array();
  72. /**
  73. * Constructor
  74. * @param string $pattern The URL pattern (ie. "/books/:id")
  75. * @param mixed $callable Anything that returns TRUE for is_callable()
  76. */
  77. public function __construct( $pattern, $callable ) {
  78. $this->setPattern($pattern);
  79. $this->setCallable($callable);
  80. $this->setConditions(self::getDefaultConditions());
  81. }
  82. /**
  83. * Set default route conditions for all instances
  84. * @param array $defaultConditions
  85. * @return void
  86. */
  87. public static function setDefaultConditions( array $defaultConditions ) {
  88. self::$defaultConditions = $defaultConditions;
  89. }
  90. /**
  91. * Get default route conditions for all instances
  92. * @return array
  93. */
  94. public static function getDefaultConditions() {
  95. return self::$defaultConditions;
  96. }
  97. /**
  98. * Get route pattern
  99. * @return string
  100. */
  101. public function getPattern() {
  102. return $this->pattern;
  103. }
  104. /**
  105. * Set route pattern
  106. * @param string $pattern
  107. * @return void
  108. */
  109. public function setPattern( $pattern ) {
  110. $this->pattern = str_replace(')', ')?', (string)$pattern);
  111. }
  112. /**
  113. * Get route callable
  114. * @return mixed
  115. */
  116. public function getCallable() {
  117. return $this->callable;
  118. }
  119. /**
  120. * Set route callable
  121. * @param mixed $callable
  122. * @return void
  123. */
  124. public function setCallable($callable) {
  125. $this->callable = $callable;
  126. }
  127. /**
  128. * Get route conditions
  129. * @return array
  130. */
  131. public function getConditions() {
  132. return $this->conditions;
  133. }
  134. /**
  135. * Set route conditions
  136. * @param array $conditions
  137. * @return void
  138. */
  139. public function setConditions( array $conditions ) {
  140. $this->conditions = $conditions;
  141. }
  142. /**
  143. * Get route name
  144. * @return string|null
  145. */
  146. public function getName() {
  147. return $this->name;
  148. }
  149. /**
  150. * Set route name
  151. * @param string $name
  152. * @return void
  153. */
  154. public function setName( $name ) {
  155. $this->name = (string)$name;
  156. $this->router->cacheNamedRoute($this->name, $this);
  157. }
  158. /**
  159. * Get route parameters
  160. * @return array
  161. */
  162. public function getParams() {
  163. return $this->params;
  164. }
  165. /**
  166. * Get router
  167. * @return Slim_Router
  168. */
  169. public function getRouter() {
  170. return $this->router;
  171. }
  172. /**
  173. * Set router
  174. * @param Slim_Router $router
  175. * @return void
  176. */
  177. public function setRouter( Slim_Router $router ) {
  178. $this->router = $router;
  179. }
  180. /**
  181. * Get middleware
  182. * @return array[Callable]
  183. */
  184. public function getMiddleware() {
  185. return $this->middleware;
  186. }
  187. /**
  188. * Set middleware
  189. *
  190. * This method allows middleware to be assigned to a specific Route.
  191. * If the method argument `is_callable` (including callable arrays!),
  192. * we directly append the argument to `$this->middleware`. Else, we
  193. * assume the argument is an array of callables and merge the array
  194. * with `$this->middleware`. Even if non-callables are included in the
  195. * argument array, we still merge them; we lazily check each item
  196. * against `is_callable` during Route::dispatch().
  197. *
  198. * @param Callable|array[Callable]
  199. * @return Slim_Route
  200. * @throws InvalidArgumentException If argument is not callable or not an array
  201. */
  202. public function setMiddleware( $middleware ) {
  203. if ( is_callable($middleware) ) {
  204. $this->middleware[] = $middleware;
  205. } else if ( is_array($middleware) ) {
  206. $this->middleware = array_merge($this->middleware, $middleware);
  207. } else {
  208. throw new InvalidArgumentException('Route middleware must be callable or an array of callables');
  209. }
  210. return $this;
  211. }
  212. /**
  213. * Matches URI?
  214. *
  215. * Parse this route's pattern, and then compare it to an HTTP resource URI
  216. * This method was modeled after the techniques demonstrated by Dan Sosedoff at:
  217. *
  218. * http://blog.sosedoff.com/2009/09/20/rails-like-php-url-router/
  219. *
  220. * @param string $resourceUri A Request URI
  221. * @return bool
  222. */
  223. public function matches( $resourceUri ) {
  224. //Extract URL params
  225. preg_match_all('@:([\w]+)@', $this->pattern, $paramNames, PREG_PATTERN_ORDER);
  226. $paramNames = $paramNames[0];
  227. //Convert URL params into regex patterns, construct a regex for this route
  228. $patternAsRegex = preg_replace_callback('@:[\w]+@', array($this, 'convertPatternToRegex'), $this->pattern);
  229. if ( substr($this->pattern, -1) === '/' ) {
  230. $patternAsRegex = $patternAsRegex . '?';
  231. }
  232. $patternAsRegex = '@^' . $patternAsRegex . '$@';
  233. //Cache URL params' names and values if this route matches the current HTTP request
  234. if ( preg_match($patternAsRegex, $resourceUri, $paramValues) ) {
  235. array_shift($paramValues);
  236. foreach ( $paramNames as $index => $value ) {
  237. $val = substr($value, 1);
  238. if ( isset($paramValues[$val]) ) {
  239. $this->params[$val] = urldecode($paramValues[$val]);
  240. }
  241. }
  242. return true;
  243. } else {
  244. return false;
  245. }
  246. }
  247. /**
  248. * Convert a URL parameter (ie. ":id") into a regular expression
  249. * @param array URL parameters
  250. * @return string Regular expression for URL parameter
  251. */
  252. protected function convertPatternToRegex( $matches ) {
  253. $key = str_replace(':', '', $matches[0]);
  254. if ( array_key_exists($key, $this->conditions) ) {
  255. return '(?P<' . $key . '>' . $this->conditions[$key] . ')';
  256. } else {
  257. return '(?P<' . $key . '>[a-zA-Z0-9_\-\.\!\~\*\\\'\(\)\:\@\&\=\$\+,%]+)';
  258. }
  259. }
  260. /**
  261. * Set route name
  262. * @param string $name The name of the route
  263. * @return Slim_Route
  264. */
  265. public function name( $name ) {
  266. $this->setName($name);
  267. return $this;
  268. }
  269. /**
  270. * Merge route conditions
  271. * @param array $conditions Key-value array of URL parameter conditions
  272. * @return Slim_Route
  273. */
  274. public function conditions( array $conditions ) {
  275. $this->conditions = array_merge($this->conditions, $conditions);
  276. return $this;
  277. }
  278. /**
  279. * Dispatch route
  280. *
  281. * This method invokes this route's callable. If middleware is
  282. * registered for this route, each callable middleware is invoked in
  283. * the order specified.
  284. *
  285. * This method is smart about trailing slashes on the route pattern.
  286. * If this route's pattern is defined with a trailing slash, and if the
  287. * current request URI does not have a trailing slash but otherwise
  288. * matches this route's pattern, a Slim_Exception_RequestSlash
  289. * will be thrown triggering an HTTP 301 Permanent Redirect to the same
  290. * URI _with_ a trailing slash. This Exception is caught in the
  291. * `Slim::run` loop. If this route's pattern is defined without a
  292. * trailing slash, and if the current request URI does have a trailing
  293. * slash, this route will not be matched and a 404 Not Found
  294. * response will be sent if no subsequent matching routes are found.
  295. *
  296. * @return bool Was route callable invoked successfully?
  297. * @throws Slim_Exception_RequestSlash
  298. */
  299. public function dispatch() {
  300. if ( substr($this->pattern, -1) === '/' && substr($this->router->getRequest()->getResourceUri(), -1) !== '/' ) {
  301. throw new Slim_Exception_RequestSlash();
  302. }
  303. //Invoke middleware
  304. foreach ( $this->middleware as $mw ) {
  305. if ( is_callable($mw) ) {
  306. call_user_func($mw);
  307. }
  308. }
  309. //Invoke callable
  310. if ( is_callable($this->getCallable()) ) {
  311. call_user_func_array($this->callable, array_values($this->params));
  312. return true;
  313. }
  314. return false;
  315. }
  316. }