Rewrite.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <?php
  2. /**
  3. *
  4. * Cube Framework $Id$ g7GTI4QmKgRt7X9nB6RlXOGrwAd9ek35ZXUkjipREEA=
  5. *
  6. * @link http://codecu.be/framework
  7. * @copyright Copyright (c) 2017 CodeCube SRL
  8. * @license http://codecu.be/framework/license Commercial License
  9. *
  10. * @version 1.9 [rev.1.9.01]
  11. */
  12. /**
  13. * routes management class, for when mod rewrite is available
  14. *
  15. * - routes that are defined will always be fixed (as in new params are added after the question mark)
  16. * - routes that are assembled as standard routes only contain GET params after the question mark
  17. *
  18. * TODO: for routing urls where module, controller & action is not specified, remove variables already included in the hardcoded route from the GET component
  19. */
  20. namespace Cube\Controller\Router;
  21. use Cube\Controller\Request\AbstractRequest,
  22. Cube\Controller\Front,
  23. Cube\Cache\Adapter\AbstractAdapter as CacheAdapter;
  24. class Rewrite extends AbstractRouter
  25. {
  26. const URI_DELIMITER = '/';
  27. const DEFAULT_CONTROLLER = 'Index';
  28. const DEFAULT_ACTION = 'Index';
  29. /**
  30. *
  31. * defined routes
  32. *
  33. * @var array
  34. */
  35. protected $_routes = array();
  36. /**
  37. *
  38. * the route class that corresponds to this router
  39. *
  40. * @var string
  41. */
  42. protected $_routeClass = '\Cube\Controller\Router\Route\Rewrite';
  43. /**
  44. *
  45. * cache an url only if the params in the array are included
  46. *
  47. * @var array
  48. */
  49. protected $_cacheableParams = array(
  50. 'module', 'controller', 'action'
  51. );
  52. /**
  53. *
  54. * add a single route to the routes array
  55. *
  56. * @param array|\Cube\Controller\Router\Route\RouteInterface $route can be an array from the config array or an instance of RouteInterface
  57. *
  58. * @return $this
  59. * @throws \BadMethodCallException
  60. */
  61. public function addRoute($route)
  62. {
  63. if ($route instanceof Route\RouteInterface) {
  64. $this->_routes[] = $route;
  65. }
  66. else if (is_array($route)) {
  67. $route = $this->_setRouteFromArray($route);
  68. if ($route !== false) {
  69. $this->_routes[] = $route;
  70. }
  71. }
  72. else {
  73. throw new \BadMethodCallException('The route object must be an instance of Cube\Controller\Router\Route\AbstractRoute or an array');
  74. }
  75. return $this;
  76. }
  77. /**
  78. *
  79. * add multiple routes to the routes array
  80. *
  81. * @param array $routes
  82. *
  83. * @return $this
  84. */
  85. public function addRoutes(array $routes)
  86. {
  87. foreach ($routes as $route) {
  88. $this->addRoute($route);
  89. }
  90. return $this;
  91. }
  92. /**
  93. *
  94. * retrieve the routes array
  95. *
  96. * @return array
  97. */
  98. public function getRoutes()
  99. {
  100. return $this->_routes;
  101. }
  102. /**
  103. *
  104. * get a route object by its name
  105. *
  106. * @param string $name
  107. *
  108. * @return \Cube\Controller\Router\Route\AbstractRoute
  109. * @throws \OutOfBoundsException
  110. */
  111. public function getRoute($name)
  112. {
  113. foreach ($this->_routes as $route) {
  114. /** @var \Cube\Controller\Router\Route\AbstractRoute $route */
  115. if ($route->getName() == $name) {
  116. return $route;
  117. }
  118. }
  119. throw new \OutOfBoundsException(
  120. sprintf("The route named '%s' does not exist.", $name));
  121. }
  122. /**
  123. *
  124. * get cacheable params
  125. *
  126. * @return array
  127. */
  128. public function getCacheableParams()
  129. {
  130. return $this->_cacheableParams;
  131. }
  132. /**
  133. *
  134. * set cacheable params
  135. *
  136. * @param array $cacheableParams
  137. *
  138. * @return $this
  139. */
  140. public function setCacheableParams($cacheableParams)
  141. {
  142. $this->_cacheableParams = $cacheableParams;
  143. return $this;
  144. }
  145. /**
  146. *
  147. * routes a request and returns the routed request object
  148. * can match multiple routes, and will return the one that was matched last
  149. *
  150. * from the route's defaults array
  151. *
  152. * @param \Cube\Controller\Request\AbstractRequest $request
  153. *
  154. * @return \Cube\Controller\Request\AbstractRequest returns the routed request
  155. */
  156. public function route(AbstractRequest $request)
  157. {
  158. $requestUri = $request->getRequestUri();
  159. $matched = false;
  160. foreach ($this->_routes as $route) {
  161. /** @var \Cube\Controller\Router\Route\AbstractRoute $route */
  162. if ($route->match($requestUri) === true) {
  163. $params = array_merge($route->getDefaults(), $route->getParams());
  164. $request->setModule($route->getModule())
  165. ->setController($route->getController())
  166. ->setAction($route->getAction())
  167. ->setParams($params)
  168. ->setQuery($params);
  169. $matched = true;
  170. }
  171. }
  172. if ($matched === false) {
  173. $request = $this->_getDefaultRoute($request);
  174. }
  175. return $request;
  176. }
  177. /**
  178. *
  179. * return a url string after processing the params and matching them to one of the existing routes
  180. * if a string is given, return it unmodified
  181. * if no route is specified
  182. *
  183. * @param mixed $params
  184. * @param string $name the name of a specific route to use
  185. * @param bool $addBaseUrl flag to add the base url param to the assembled route
  186. * @param bool $addGetParams whether to attach params resulted from a previous get operation to the url
  187. * @param array $skipParams an array of params to be omitted when constructing the url
  188. *
  189. * @return string
  190. */
  191. public function assemble($params, $name = null, $addBaseUrl = true, $addGetParams = false, array $skipParams = null)
  192. {
  193. $cacheRoutes = false;
  194. $cacheFile = null;
  195. $url = null;
  196. // check if we have a named route or if params is a string
  197. // named of string routes are not cached
  198. if ($name !== null) {
  199. $route = $this->getRoute($name);
  200. $url = $route->assemble($params, true);
  201. }
  202. else if (is_string($params)) {
  203. $url = $params;
  204. }
  205. // use default route assembler first
  206. if ($url === null) {
  207. $paramsKeys = array_keys((array)$params);
  208. $params = $this->_getDefaultParams($params, $addGetParams, $skipParams);
  209. if (count(array_intersect($paramsKeys, $this->_cacheableParams)) > 0) {
  210. $cacheFileName = json_encode(array(
  211. $params, $name, $addBaseUrl, $addGetParams, $skipParams
  212. ));
  213. // get assembled route from cache
  214. $cacheRoutes = $this->_getCache('cacheRoutes');
  215. if ($cacheRoutes !== false) {
  216. $cacheFile = md5($cacheFileName);
  217. if (($cachedUrl = $this->_cache->read($cacheFile, CacheAdapter::ROUTES)) !== false) {
  218. return $cachedUrl;
  219. }
  220. }
  221. }
  222. // if no cache, generate route
  223. foreach ($this->_routes as $route) {
  224. /** @var \Cube\Controller\Router\Route\AbstractRoute $route */
  225. $assembled = $route->assemble($params);
  226. if ($assembled !== null) {
  227. $url = $assembled;
  228. }
  229. }
  230. }
  231. if (!isset($url)) {
  232. $url = $this->_defaultRouteAssemble($params);
  233. }
  234. if (!preg_match('#^[a-z]+://#', $url)) {
  235. $url = (($addBaseUrl) ? Front::getInstance()->getRequest()->getBaseUrl() : '')
  236. . self::URI_DELIMITER
  237. . ltrim($url, self::URI_DELIMITER);
  238. }
  239. // add assembled route to the cache
  240. if (!empty($url) && $cacheRoutes !== false) {
  241. $this->_cache->write($cacheFile, CacheAdapter::ROUTES, $url);
  242. }
  243. return $url;
  244. }
  245. /**
  246. *
  247. * create a route object from an input array
  248. *
  249. * @param array $route the route in array format
  250. *
  251. * @return \Cube\Controller\Router\Route\AbstractRoute|false return a route object or false if invalid data was provided
  252. */
  253. protected function _setRouteFromArray(array $route)
  254. {
  255. if (!empty($route[0])) {
  256. return new Route\Rewrite($route[0], $route[1], $route[2]);
  257. }
  258. return false;
  259. }
  260. /**
  261. *
  262. * assemble a request when no routes match
  263. * for array params, they wont be added in the url, but after the ? character
  264. *
  265. * if no module, controller and action is specified in the params, return the current uri + params after ? character
  266. *
  267. * @param array $params
  268. *
  269. * @return string the routed uri
  270. */
  271. protected function _defaultRouteAssemble(array $params = null)
  272. {
  273. $url = array();
  274. $get = array();
  275. foreach ((array)$params as $key => $value) {
  276. if (preg_match('#^[a-zA-Z0-9_-]+$#', $key)) {
  277. if (!is_array($value)) {
  278. if (!in_array($key, array('module', 'controller', 'action'))) {
  279. array_push($url, $key);
  280. }
  281. array_push($url, $value);
  282. }
  283. else {
  284. foreach ((array)$value as $val) {
  285. if (!empty($val)) {
  286. $get[] = $key . '[]=' . $val;
  287. }
  288. }
  289. }
  290. }
  291. }
  292. $uri = implode(self::URI_DELIMITER, $url);
  293. if (count($get) > 0) {
  294. $uri .= self::URI_DELIMITER . '?' . implode('&', $get);
  295. }
  296. return $uri;
  297. }
  298. }