/src/includes/classes/router.php

https://bitbucket.org/slogsdon/unamed · PHP · 264 lines · 142 code · 17 blank · 105 comment · 13 complexity · 57d4e55915db3b9a89e68f1a5fcfcc3e MD5 · raw file

  1. <?php
  2. /**
  3. * Unamed - a WordPress replacement
  4. *
  5. * @category CMS
  6. * @package Unamed
  7. * @author Shane Logsdon <shane.a.logsdon@gmail.com>
  8. * @license MIT http://mit.edu/
  9. * @link http://bitbucket.org/slogsdon/unamed
  10. */
  11. namespace Unamed\FrontController;
  12. use Unamed\Interfaces;
  13. {
  14. /**
  15. * Router implementation
  16. *
  17. * @category Class
  18. * @package Unamed
  19. * @author Shane Logsdon <shane.a.logsdon@gmail.com>
  20. * @license MIT http://mit.edu/
  21. * @link http://bitbucket.org/slogsdon/unamed
  22. * @since 1.0
  23. */
  24. class Router implements Interfaces\Router
  25. {
  26. /* Constants */
  27. const CONTROLLER_NAMESPACE = 'Unamed\Controllers';
  28. const DEFAULT_CONTROLLER = 'Post';
  29. const DEFAULT_ACTION = null;
  30. const DEFAULT_BASEPATH = null;
  31. /* Properties */
  32. protected $controller = self::DEFAULT_CONTROLLER;
  33. protected $action = self::DEFAULT_ACTION;
  34. protected $params = array();
  35. protected $basePath = self::DEFAULT_BASEPATH;
  36. protected $request = null;
  37. protected $routes = array();
  38. /* Methods */
  39. /**
  40. * __construct
  41. *
  42. * @param Request $request -
  43. * @param array $options - (opt.)
  44. */
  45. public function __construct(Request $request, array $options = array())
  46. {
  47. $this->request = $request;
  48. if (isset($options['basePath'])) {
  49. $this->basePath = $options['basePath'];
  50. unset($options['basePath']);
  51. }
  52. }
  53. /**
  54. * addRoute
  55. *
  56. * @param string $route -
  57. * @param string $controller -
  58. *
  59. * @return object(Router)
  60. */
  61. public function addRoute($route, $controller)
  62. {
  63. $this->routes[$route] = $controller;
  64. return $this;
  65. }
  66. /**
  67. * addRoutes
  68. *
  69. * @param array $routes -
  70. *
  71. * @return object(Router)
  72. */
  73. public function addRoutes(array $routes)
  74. {
  75. foreach ($routes as $route => $controller) {
  76. $this->addRoute($route, $controller);
  77. }
  78. return $this;
  79. }
  80. /**
  81. * comileRoute
  82. *
  83. * Compiles a route string to a regular expression
  84. * lifted from https://github.com/chriso/klein.php because i hate
  85. * dealing with regex
  86. *
  87. * @param string $route -
  88. *
  89. * @return string
  90. */
  91. protected function compileRoute($route)
  92. {
  93. if (preg_match_all(
  94. '`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`',
  95. $route,
  96. $matches,
  97. PREG_SET_ORDER
  98. )) {
  99. $match_types = array(
  100. 'i' => '[0-9]++',
  101. 'a' => '[0-9A-Za-z]++',
  102. 'h' => '[0-9A-Fa-f]++',
  103. '*' => '.+?',
  104. '**' => '.++',
  105. '' => '[^/]++'
  106. );
  107. foreach ($matches as $match) {
  108. list($block, $pre, $type, $param, $optional) = $match;
  109. if (isset($match_types[$type])) {
  110. $type = $match_types[$type];
  111. }
  112. if ($pre === '.') {
  113. $pre = '\.';
  114. }
  115. // Older versions of PCRE require the 'P' in (?P<named>)
  116. $pattern = '(?:'
  117. . ($pre !== '' ? $pre : null)
  118. . '('
  119. . ($param !== '' ? "?P<".$param.">" : null)
  120. . $type
  121. . '))'
  122. . ($optional !== '' ? '?' : null);
  123. $route = str_replace($block, $pattern, $route);
  124. }
  125. }
  126. return '`^'.$route.'$`';
  127. }
  128. /**
  129. * getData
  130. *
  131. * @return object
  132. */
  133. public function getData()
  134. {
  135. $this->parseUri();
  136. $o = new \stdClass();
  137. $o->controller = self::CONTROLLER_NAMESPACE
  138. . "\\" . $this->controller;
  139. $o->action = $this->action;
  140. $o->params = $this->params;
  141. return $o;
  142. }
  143. /**
  144. * parse
  145. *
  146. * run regex against request uri and pull out params (if matched)
  147. *
  148. * @param string $path - request uri
  149. * @param string $regex - compiled route
  150. *
  151. * @return int
  152. */
  153. protected function parse($path, $regex)
  154. {
  155. return preg_match($regex, $path, $this->params);
  156. }
  157. /**
  158. * parseUri
  159. *
  160. * tries to match the request uri to a route
  161. *
  162. * @return nothing
  163. */
  164. protected function parseUri()
  165. {
  166. $path = rtrim(parse_url($this->request->uri, PHP_URL_PATH), '/');
  167. if (!is_null($this->basePath)) {
  168. if (strpos($path, $this->basePath) === 0) {
  169. $path = substr($path, strlen($this->basePath));
  170. }
  171. }
  172. foreach ($this->routes as $route => $controller) {
  173. if (ENABLE_CACHE && \Unamed\Cache::isHit('route:' . $route)) {
  174. $routeRegEx = \Unamed\Cache::get('route:' . $route);
  175. } else {
  176. $routeRegEx = $this->compileRoute($route);
  177. }
  178. if ($this->parse($path, $routeRegEx) === 1) {
  179. $this->setController($controller);
  180. if (isset($this->params['action'])) {
  181. $this->setAction($this->params['action']);
  182. }
  183. continue;
  184. }
  185. }
  186. return;
  187. }
  188. /**
  189. * setAction
  190. *
  191. * @param bool $action - target action
  192. *
  193. * @return object(Router)
  194. */
  195. public function setAction($action)
  196. {
  197. $reflector = new \ReflectionClass(
  198. self::CONTROLLER_NAMESPACE . "\\" . $this->controller
  199. );
  200. if ($reflector->hasMethod($action)) {
  201. $this->action = $action;
  202. }
  203. return $this;
  204. }
  205. /**
  206. * setBasePath
  207. *
  208. * @param bool $basePath - any sub-directory structure
  209. *
  210. * @return object(Router)
  211. */
  212. public function setBasePath($basePath)
  213. {
  214. $this->basePath = $basePath;
  215. return $this;
  216. }
  217. /**
  218. * setController
  219. *
  220. * @param string $controller - target controller
  221. *
  222. * @return object(Router)
  223. */
  224. public function setController($controller)
  225. {
  226. $controller = ucfirst(strtolower($controller));
  227. if (class_exists(
  228. self::CONTROLLER_NAMESPACE . "\\" . $controller
  229. )) {
  230. $this->controller = $controller;
  231. }
  232. return $this;
  233. }
  234. /**
  235. * setParams
  236. *
  237. * @param array $params - route params
  238. *
  239. * @return object(Router)
  240. */
  241. public function setParams(array $params)
  242. {
  243. $this->params = $params;
  244. return $this;
  245. }
  246. };
  247. }