PageRenderTime 45ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/library/XenForo/Router.php

https://github.com/hanguyenhuu/DTUI_201105
PHP | 317 lines | 161 code | 33 blank | 123 comment | 22 complexity | 58994ba1243e0539a2fe2f2466555702 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, BSD-3-Clause
  1. <?php
  2. /**
  3. * Class that resolves a path in the URI or other part of the request to a controller/action.
  4. * Also allows the type of response that is desired to be controlled based on input.
  5. * Individual matches can make modifications to the routing path to be passed to other rules.
  6. *
  7. * Rules will continue matching until a {@link XenForo_RouteMatch} object is returned that
  8. * has a {@link XenForo_RouteMatch::$controllerName controller name} specified.
  9. *
  10. * @package XenForo_Mvc
  11. */
  12. class XenForo_Router
  13. {
  14. /**
  15. * Stack of rules to match against. Once a match is found, further matching stops.
  16. *
  17. * @see addRules()
  18. * @var array
  19. */
  20. protected $_rules = array();
  21. /**
  22. * The default response type, if it's not modified based on input.
  23. *
  24. * @var string
  25. */
  26. protected $_defaultResponseType = 'html';
  27. /**
  28. * The route path to apply if the path ever becomes empty.
  29. *
  30. * @var string
  31. */
  32. protected $_routePathIfEmpty = 'index';
  33. /**
  34. * Match against the rules stack. If no match can be found, the {@link getNotFoundError()}
  35. * handler is invoked.
  36. *
  37. * @param Zend_Controller_Request_Http Request object
  38. *
  39. * @return XenForo_RouteMatch|false Final information (including controller and action) about where to route to
  40. */
  41. public function match(Zend_Controller_Request_Http $request)
  42. {
  43. $routePath = $this->getRoutePath($request);
  44. $request->setParam('_origRoutePath', $routePath);
  45. $responseType = $this->_defaultResponseType;
  46. $finalRouteMatch = null;
  47. foreach ($this->_rules AS $rule)
  48. {
  49. if ($routePath === '')
  50. {
  51. // must always have a route path of some sort
  52. $routePath = $this->_routePathIfEmpty;
  53. }
  54. if (!$match = $rule->match($routePath, $request, $this))
  55. {
  56. continue;
  57. }
  58. if ($match->getResponseType())
  59. {
  60. $responseType = $match->getResponseType();
  61. }
  62. if ($match->getControllerName())
  63. {
  64. $finalRouteMatch = $match;
  65. $request->setParam('_matchedRoutePath', $routePath);
  66. break;
  67. }
  68. if ($match->getModifiedRoutePath() !== null)
  69. {
  70. $routePath = $match->getModifiedRoutePath();
  71. }
  72. }
  73. // a bit of magic - if the query string specifies a _xfResponseType parameter,
  74. // use THAT as the extension, overriding anything else.
  75. if ($request->has('_xfResponseType'))
  76. {
  77. $responseType = $request->get('_xfResponseType');
  78. }
  79. if ($finalRouteMatch)
  80. {
  81. $finalRouteMatch->setResponseType($responseType);
  82. return $finalRouteMatch;
  83. }
  84. else
  85. {
  86. $match = $this->getRouteMatch();
  87. $match->setResponseType($responseType);
  88. return $match;
  89. }
  90. }
  91. /**
  92. * Gets the path the to be routed based on the URL of the request
  93. *
  94. * @param Zend_Controller_Request_Http Request object
  95. *
  96. * @return string Routing path
  97. */
  98. public function getRoutePath(Zend_Controller_Request_Http $request)
  99. {
  100. $baseUrl = $request->getBaseUrl();
  101. $requestUri = $request->getRequestUri();
  102. if (substr($requestUri, 0, strlen($baseUrl)) == $baseUrl)
  103. {
  104. $routeBase = substr($requestUri, strlen($baseUrl));
  105. if (preg_match('#^/([^?]+)(\?|$)#U', $routeBase, $match))
  106. {
  107. // rewrite approach (starts with /). Must be non-empty rewrite up to query string.
  108. return urldecode($match[1]);
  109. }
  110. else if (preg_match('#\?([^=&]+)(&|$)#U', $routeBase, $match))
  111. {
  112. // query string approach. Must start with non-empty, non-named param.
  113. return urldecode($match[1]);
  114. }
  115. }
  116. if (($namedRouteVar = $request->getParam('_')) !== null)
  117. {
  118. return $namedRouteVar;
  119. }
  120. return '';
  121. }
  122. /**
  123. * Adds a new routing rule to the end of the chain or overwrites an existing rule by name.
  124. *
  125. * @param XenForo_Route_Interface Routing rule
  126. * @param string Name of the rule. If it already exists, it is overwritten.
  127. *
  128. * @return XenForo_Router Fluent interface ($this)
  129. */
  130. public function addRule(XenForo_Route_Interface $route, $name)
  131. {
  132. $this->_rules[$name] = $route;
  133. return $this;
  134. }
  135. /**
  136. * Get the current routing rules
  137. *
  138. * @return array
  139. */
  140. public function getRules()
  141. {
  142. return $this->_rules;
  143. }
  144. /**
  145. * Reset (remove) all routing rules.
  146. *
  147. * @return XenForo_Router Fluent interface ($this)
  148. */
  149. public function resetRules()
  150. {
  151. $this->_rules = array();
  152. return $this;
  153. }
  154. /**
  155. * Resolves the action from a route that looks like name.123/action and sets
  156. * the "123" param into the specified parameter name.
  157. *
  158. * Supports name.123/action1/action2 (returns "action1/action2"). If given
  159. * "action1/action2", this will return the full string as the action as long as action1
  160. * does not have a "." in it.
  161. *
  162. * @param string $routePath Full path to route against. This should not include a prefix.
  163. * @param Zend_Controller_Request_Http $request Request object
  164. * @param string $paramName Name of the parameter to be registered with the request object (if found)
  165. * @param string $defaultActionWithParam If there's no action and there is an int param, use this as the default action
  166. *
  167. * @return string The requested action
  168. */
  169. public function resolveActionWithIntegerParam($routePath, Zend_Controller_Request_Http $request, $paramName, $defaultActionWithParam = '')
  170. {
  171. $parts = explode('/', $routePath, 2);
  172. $action = isset($parts[1]) ? $parts[1] : '';
  173. $paramParts = explode(XenForo_Application::URL_ID_DELIMITER, $parts[0]);
  174. $paramId = end($paramParts);
  175. if (count($paramParts) > 1
  176. || $paramId === strval(intval($paramId))
  177. )
  178. {
  179. $request->setParam($paramName, intval($paramId));
  180. if ($action === '')
  181. {
  182. $action = $defaultActionWithParam;
  183. }
  184. return $action;
  185. }
  186. else
  187. {
  188. return $routePath;
  189. }
  190. }
  191. /**
  192. * Resolves an action with an integer or a string parameter, such as in situations
  193. * where a slug is an optional component. Note that <title>.<int>/<action> and
  194. * <string>/<action> will work, but <action> on its own will be matched as a string.
  195. *
  196. * This method is not an ideal function to use when you are not guaranteed to have data.
  197. *
  198. * @param strign $routePath
  199. * @param Zend_Controller_Request_Http $request
  200. * @param string $intParamName Name of the parameter to set if int is found
  201. * @param string $stringParamName Name of the parameter to set if string is found
  202. */
  203. public function resolveActionWithIntegerOrStringParam($routePath, Zend_Controller_Request_Http $request, $intParamName, $stringParamName)
  204. {
  205. $parts = explode('/', $routePath, 2);
  206. $action = isset($parts[1]) ? $parts[1] : '';
  207. $paramParts = explode(XenForo_Application::URL_ID_DELIMITER, $parts[0]);
  208. $paramId = end($paramParts);
  209. if (count($paramParts) > 1
  210. || $paramId === strval(intval($paramId))
  211. )
  212. {
  213. $request->setParam($intParamName, intval($paramId));
  214. return $action;
  215. }
  216. else
  217. {
  218. if ($paramId != '-') // special case: not set
  219. {
  220. $request->setParam($stringParamName, $paramId);
  221. }
  222. return $action;
  223. }
  224. }
  225. /**
  226. * Resolves the action from a route that may have a string parameter. If there
  227. * are no slashes, then an action is assumed. If there is a slash, then the first
  228. * item is considered the string param.
  229. *
  230. * For example: list => "list" action. blah/list => "list" action, with "blah" param.
  231. *
  232. * @param string $routePath Full path to route against. This should not include a prefix.
  233. * @param Zend_Controller_Request_Http $request Request object
  234. * @param string $paramName Name of the parameter to be registered with the request object (if found)
  235. *
  236. * @return string The requested action
  237. */
  238. public function resolveActionWithStringParam($routePath, Zend_Controller_Request_Http $request, $paramName)
  239. {
  240. $components = explode('/', $routePath);
  241. if (isset($components[1]))
  242. {
  243. // param comes first but we have an action: <param>/<action...>
  244. $request->setParam($paramName, $components[0]);
  245. unset($components[0]);
  246. return implode('', $components);
  247. }
  248. else
  249. {
  250. return $routePath;
  251. }
  252. }
  253. /**
  254. * Checks for the presence of 'page-x' as the action component of a route
  255. *
  256. * @param string $action
  257. * @param Zend_Controller_Request_Http $request
  258. *
  259. * @return string
  260. */
  261. public function resolveActionAsPageNumber($action, Zend_Controller_Request_Http $request)
  262. {
  263. if (preg_match('#^page-(\d+)$#i', $action, $match))
  264. {
  265. $action = '';
  266. $request->setParam('page', $match[1]);
  267. }
  268. return $action;
  269. }
  270. /**
  271. * Gets a route match object.
  272. *
  273. * @param string $controllerName
  274. * @param string|false $action
  275. * @param string $majorSection
  276. * @param string $minorSection
  277. *
  278. * @return XenForo_RouteMatch
  279. */
  280. public function getRouteMatch($controllerName = '', $action = false, $majorSection = '', $minorSection = '')
  281. {
  282. return new XenForo_RouteMatch($controllerName, $action, $majorSection, $minorSection);
  283. }
  284. }