PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/cake/libs/route/cake_route.php

https://github.com/v2ninad/nm_cake
PHP | 376 lines | 211 code | 33 blank | 132 comment | 46 complexity | 91d797d1542e7e056baac7b220374edd MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * A single Route used by the Router to connect requests to
  4. * parameter maps.
  5. *
  6. * Not normally created as a standalone. Use Router::connect() to create
  7. * Routes for your application.
  8. *
  9. * PHP5
  10. *
  11. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  12. * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  13. *
  14. * Licensed under The MIT License
  15. * Redistributions of files must retain the above copyright notice.
  16. *
  17. * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  18. * @link http://cakephp.org CakePHP(tm) Project
  19. * @package cake.libs.route
  20. * @since CakePHP(tm) v 1.3
  21. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  22. */
  23. class CakeRoute {
  24. /**
  25. * An array of named segments in a Route.
  26. * `/:controller/:action/:id` has 3 key elements
  27. *
  28. * @var array
  29. * @access public
  30. */
  31. public $keys = array();
  32. /**
  33. * An array of additional parameters for the Route.
  34. *
  35. * @var array
  36. * @access public
  37. */
  38. public $options = array();
  39. /**
  40. * Default parameters for a Route
  41. *
  42. * @var array
  43. * @access public
  44. */
  45. public $defaults = array();
  46. /**
  47. * The routes template string.
  48. *
  49. * @var string
  50. * @access public
  51. */
  52. public $template = null;
  53. /**
  54. * Is this route a greedy route? Greedy routes have a `/*` in their
  55. * template
  56. *
  57. * @var string
  58. * @access protected
  59. */
  60. protected $_greedy = false;
  61. /**
  62. * The compiled route regular expresssion
  63. *
  64. * @var string
  65. * @access protected
  66. */
  67. protected $_compiledRoute = null;
  68. /**
  69. * HTTP header shortcut map. Used for evaluating header-based route expressions.
  70. *
  71. * @var array
  72. * @access private
  73. */
  74. private $__headerMap = array(
  75. 'type' => 'content_type',
  76. 'method' => 'request_method',
  77. 'server' => 'server_name'
  78. );
  79. /**
  80. * Constructor for a Route
  81. *
  82. * @param string $template Template string with parameter placeholders
  83. * @param array $defaults Array of defaults for the route.
  84. * @param string $params Array of parameters and additional options for the Route
  85. * @return void
  86. */
  87. public function __construct($template, $defaults = array(), $options = array()) {
  88. $this->template = $template;
  89. $this->defaults = (array)$defaults;
  90. $this->options = (array)$options;
  91. }
  92. /**
  93. * Check if a Route has been compiled into a regular expression.
  94. *
  95. * @return boolean
  96. */
  97. public function compiled() {
  98. return !empty($this->_compiledRoute);
  99. }
  100. /**
  101. * Compiles the route's regular expression. Modifies defaults property so all necessary keys are set
  102. * and populates $this->names with the named routing elements.
  103. *
  104. * @return array Returns a string regular expression of the compiled route.
  105. */
  106. public function compile() {
  107. if ($this->compiled()) {
  108. return $this->_compiledRoute;
  109. }
  110. $this->_writeRoute();
  111. return $this->_compiledRoute;
  112. }
  113. /**
  114. * Builds a route regular expression. Uses the template, defaults and options
  115. * properties to compile a regular expression that can be used to parse request strings.
  116. *
  117. * @return void
  118. */
  119. protected function _writeRoute() {
  120. if (empty($this->template) || ($this->template === '/')) {
  121. $this->_compiledRoute = '#^/*$#';
  122. $this->keys = array();
  123. return;
  124. }
  125. $route = $this->template;
  126. $names = $routeParams = array();
  127. $parsed = preg_quote($this->template, '#');
  128. preg_match_all('#:([A-Za-z0-9_-]+[A-Z0-9a-z])#', $route, $namedElements);
  129. foreach ($namedElements[1] as $i => $name) {
  130. $search = '\\' . $namedElements[0][$i];
  131. if (isset($this->options[$name])) {
  132. $option = null;
  133. if ($name !== 'plugin' && array_key_exists($name, $this->defaults)) {
  134. $option = '?';
  135. }
  136. $slashParam = '/\\' . $namedElements[0][$i];
  137. if (strpos($parsed, $slashParam) !== false) {
  138. $routeParams[$slashParam] = '(?:/(?P<' . $name . '>' . $this->options[$name] . ')' . $option . ')' . $option;
  139. } else {
  140. $routeParams[$search] = '(?:(?P<' . $name . '>' . $this->options[$name] . ')' . $option . ')' . $option;
  141. }
  142. } else {
  143. $routeParams[$search] = '(?:(?P<' . $name . '>[^/]+))';
  144. }
  145. $names[] = $name;
  146. }
  147. if (preg_match('#\/\*$#', $route, $m)) {
  148. $parsed = preg_replace('#/\\\\\*$#', '(?:/(?P<_args_>.*))?', $parsed);
  149. $this->_greedy = true;
  150. }
  151. krsort($routeParams);
  152. $parsed = str_replace(array_keys($routeParams), array_values($routeParams), $parsed);
  153. $this->_compiledRoute = '#^' . $parsed . '[/]*$#';
  154. $this->keys = $names;
  155. }
  156. /**
  157. * Checks to see if the given URL can be parsed by this route.
  158. * If the route can be parsed an array of parameters will be returned if not
  159. * false will be returned. String urls are parsed if they match a routes regular expression.
  160. *
  161. * @param string $url The url to attempt to parse.
  162. * @return mixed Boolean false on failure, otherwise an array or parameters
  163. */
  164. public function parse($url) {
  165. if (!$this->compiled()) {
  166. $this->compile();
  167. }
  168. if (!preg_match($this->_compiledRoute, $url, $route)) {
  169. return false;
  170. } else {
  171. foreach ($this->defaults as $key => $val) {
  172. if ($key[0] === '[' && preg_match('/^\[(\w+)\]$/', $key, $header)) {
  173. if (isset($this->__headerMap[$header[1]])) {
  174. $header = $this->__headerMap[$header[1]];
  175. } else {
  176. $header = 'http_' . $header[1];
  177. }
  178. $val = (array)$val;
  179. $h = false;
  180. foreach ($val as $v) {
  181. if (env(strtoupper($header)) === $v) {
  182. $h = true;
  183. }
  184. }
  185. if (!$h) {
  186. return false;
  187. }
  188. }
  189. }
  190. array_shift($route);
  191. $count = count($this->keys);
  192. for ($i = 0; $i <= $count; $i++) {
  193. unset($route[$i]);
  194. }
  195. $route['pass'] = $route['named'] = array();
  196. $route += $this->defaults;
  197. //move numerically indexed elements from the defaults into pass.
  198. foreach ($route as $key => $value) {
  199. if (is_integer($key)) {
  200. $route['pass'][] = $value;
  201. unset($route[$key]);
  202. }
  203. }
  204. return $route;
  205. }
  206. }
  207. /**
  208. * Apply persistent parameters to a url array. Persistant parameters are a special
  209. * key used during route creation to force route parameters to persist when omitted from
  210. * a url array.
  211. *
  212. * @param array $url The array to apply persistent parameters to.
  213. * @param array $params An array of persistent values to replace persistent ones.
  214. * @return array An array with persistent parameters applied.
  215. */
  216. public function persistParams($url, $params) {
  217. foreach ($this->options['persist'] as $persistKey) {
  218. if (array_key_exists($persistKey, $params) && !isset($url[$persistKey])) {
  219. $url[$persistKey] = $params[$persistKey];
  220. }
  221. }
  222. return $url;
  223. }
  224. /**
  225. * Attempt to match a url array. If the url matches the route parameters + settings, then
  226. * return a generated string url. If the url doesn't match the route parameters false will be returned.
  227. * This method handles the reverse routing or conversion of url arrays into string urls.
  228. *
  229. * @param array $url An array of parameters to check matching with.
  230. * @return mixed Either a string url for the parameters if they match or false.
  231. */
  232. public function match($url) {
  233. if (!$this->compiled()) {
  234. $this->compile();
  235. }
  236. $defaults = $this->defaults;
  237. if (isset($defaults['prefix'])) {
  238. $url['prefix'] = $defaults['prefix'];
  239. }
  240. //check that all the key names are in the url
  241. $keyNames = array_flip($this->keys);
  242. if (array_intersect_key($keyNames, $url) != $keyNames) {
  243. return false;
  244. }
  245. $diffUnfiltered = Set::diff($url, $defaults);
  246. $diff = array();
  247. foreach ($diffUnfiltered as $key => $var) {
  248. if ($var === 0 || $var === '0' || !empty($var)) {
  249. $diff[$key] = $var;
  250. }
  251. }
  252. //if a not a greedy route, no extra params are allowed.
  253. if (!$this->_greedy && array_diff_key($diff, $keyNames) != array()) {
  254. return false;
  255. }
  256. //remove defaults that are also keys. They can cause match failures
  257. foreach ($this->keys as $key) {
  258. unset($defaults[$key]);
  259. }
  260. $filteredDefaults = array_filter($defaults);
  261. //if the difference between the url diff and defaults contains keys from defaults its not a match
  262. if (array_intersect_key($filteredDefaults, $diffUnfiltered) !== array()) {
  263. return false;
  264. }
  265. $passedArgsAndParams = array_diff_key($diff, $filteredDefaults, $keyNames);
  266. list($named, $params) = Router::getNamedElements($passedArgsAndParams, $url['controller'], $url['action']);
  267. //remove any pass params, they have numeric indexes, skip any params that are in the defaults
  268. $pass = array();
  269. $i = 0;
  270. while (isset($url[$i])) {
  271. if (!isset($diff[$i])) {
  272. $i++;
  273. continue;
  274. }
  275. $pass[] = $url[$i];
  276. unset($url[$i], $params[$i]);
  277. $i++;
  278. }
  279. //still some left over parameters that weren't named or passed args, bail.
  280. if (!empty($params)) {
  281. return false;
  282. }
  283. //check patterns for routed params
  284. if (!empty($this->options)) {
  285. foreach ($this->options as $key => $pattern) {
  286. if (array_key_exists($key, $url) && !preg_match('#^' . $pattern . '$#', $url[$key])) {
  287. return false;
  288. }
  289. }
  290. }
  291. return $this->_writeUrl(array_merge($url, compact('pass', 'named')));
  292. }
  293. /**
  294. * Converts a matching route array into a url string. Composes the string url using the template
  295. * used to create the route.
  296. *
  297. * @param array $params The params to convert to a string url.
  298. * @return string Composed route string.
  299. */
  300. protected function _writeUrl($params) {
  301. if (isset($params['prefix'], $params['action'])) {
  302. $params['action'] = str_replace($params['prefix'] . '_', '', $params['action']);
  303. unset($params['prefix']);
  304. }
  305. if (is_array($params['pass'])) {
  306. $params['pass'] = implode('/', $params['pass']);
  307. }
  308. $separator = Router::$named['separator'];
  309. if (!empty($params['named']) && is_array($params['named'])) {
  310. $named = array();
  311. foreach ($params['named'] as $key => $value) {
  312. if (is_array($value)) {
  313. foreach ($value as $namedKey => $namedValue) {
  314. $named[] = $key . "[$namedKey]" . $separator . $namedValue;
  315. }
  316. } else {
  317. $named[] = $key . $separator . $value;
  318. }
  319. }
  320. //custom by Ninad for pagination in search
  321. //$params['pass'] = $params['pass'] . '/' . implode('/', $named);
  322. $params['pass'] = implode('/', $named) . '/' . $params['pass'];
  323. }
  324. $out = $this->template;
  325. $search = $replace = array();
  326. foreach ($this->keys as $key) {
  327. $string = null;
  328. if (isset($params[$key])) {
  329. $string = $params[$key];
  330. } elseif (strpos($out, $key) != strlen($out) - strlen($key)) {
  331. $key .= '/';
  332. }
  333. $search[] = ':' . $key;
  334. $replace[] = $string;
  335. }
  336. $out = str_replace($search, $replace, $out);
  337. if (strpos($this->template, '*')) {
  338. $out = str_replace('*', $params['pass'], $out);
  339. }
  340. $out = str_replace('//', '/', $out);
  341. return $out;
  342. }
  343. }