PageRenderTime 64ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 0ms

/src/frapi/library/Frapi/Router.php

https://github.com/FranckErnewein/citron
PHP | 202 lines | 95 code | 25 blank | 82 comment | 21 complexity | 12b570dcbebfb9d134cbc5006fb9d046 MD5 | raw file
  1. <?php
  2. /**
  3. * Router Class
  4. *
  5. *
  6. * LICENSE
  7. *
  8. * This source file is subject to the new BSD license that is bundled
  9. * with this package in the file LICENSE.txt.
  10. * It is also available through the world-wide-web at this URL:
  11. * http://getfrapi.com/license/new-bsd
  12. * If you did not receive a copy of the license and are unable to
  13. * obtain it through the world-wide-web, please send an email
  14. * to license@getfrapi.com so we can send you a copy immediately.
  15. *
  16. * Two main ways to use:
  17. * 1. Manually Prepare and Set Routes. Best used when we have our routes in array, or from file.
  18. * <?php
  19. * $router = new Frapi_Router();
  20. * $router->setPreparedRoutes(Frapi_Router::prepareRoutes($routes));
  21. * $result = $router->match('/example-object/189/edit');
  22. * ?>
  23. *
  24. * 2. Let Frapi_Router load and prepare routes
  25. * <?php
  26. * $router = new Frapi_Router();
  27. * $router->loadAndPrepareRoutes(); //Load prepared routes from APC, else load from DB and prepare.
  28. * $result = $router->match('/example-object/189/edit');
  29. * ?>
  30. *
  31. * @license New BSD
  32. * @copyright echolibre ltd.
  33. * @package frapi
  34. */
  35. class Frapi_Router
  36. {
  37. /**
  38. * Prepared Routes
  39. *
  40. * @var Array
  41. */
  42. protected $preparedRoutes = array();
  43. /**
  44. * A list of the reserved routes that Frapi may make use
  45. * of at some given point or another.
  46. *
  47. * @var array An array of reservedRoutes
  48. */
  49. protected $reservedRoutes = array(
  50. '/_oauth', '/_xauth'
  51. );
  52. /**
  53. * Load routes from APC, database etc.
  54. * Prepare routes if necessary!
  55. *
  56. * @return void
  57. */
  58. public function loadAndPrepareRoutes()
  59. {
  60. if ($routes = Frapi_Internal::getCached('Router.routes-prepared')) {
  61. $this->setPreparedRoutes($routes);
  62. } else {
  63. $routes = array();
  64. $ret = Frapi_Internal::getConfiguration('actions');
  65. $rows = $ret->getAll('action');
  66. foreach ($rows as $row) {
  67. if (isset($row['route']) && !empty($row['route'])) {
  68. $routes[$row['name']] = $row['route'];
  69. }
  70. }
  71. $this->setPreparedRoutes($preparedRoutes = self::prepareRoutes($routes));
  72. Frapi_Internal::setCached('Router.routes-prepared', $preparedRoutes);
  73. }
  74. }
  75. /**
  76. * Prepare routes
  77. *
  78. * Turns array of routes into optimized arrays
  79. * which can be quickly looked up.
  80. *
  81. * @param array $routes The routes to parse.
  82. * @return array $routes
  83. */
  84. public static function prepareRoutes($routes)
  85. {
  86. $optimizedRoutes = array();
  87. foreach ($routes as $action => $route) {
  88. $parsedRoute = self::parseSegments($route);
  89. if ($parsedRoute[0][0] != ':') {
  90. if (!isset($optimizedRoutes[current($parsedRoute)])) {
  91. $optimizedRoutes[current($parsedRoute)] = array();
  92. }
  93. $optimizedRoutes[current($parsedRoute)][] =
  94. array('segments'=>array_slice($parsedRoute, 1), 'action'=>$action);
  95. }
  96. }
  97. return $optimizedRoutes;
  98. }
  99. /**
  100. * Parse route or path in array segments.
  101. *
  102. * @param string $route Parse the segments of a route
  103. * @return Array Segments.
  104. */
  105. public static function parseSegments($route)
  106. {
  107. $route = trim($route, ' /');
  108. $exploded = preg_split('@[/]+@', $route);
  109. return $exploded;
  110. }
  111. /**
  112. * Given a query path, match against URL segments
  113. *
  114. * @param $queryPath The query path we want to route.
  115. * @return mixed Array|false array('params'=>array(), 'action'=>String)
  116. */
  117. public function match($queryPath)
  118. {
  119. if (empty($this->preparedRoutes)) {
  120. return false;
  121. }
  122. $matches = array('static' => array(), 'dynamic' => array());
  123. $explodedPath = self::parseSegments($queryPath);
  124. $firstPathSegment = current($explodedPath);
  125. if (isset($this->preparedRoutes[$firstPathSegment])) {
  126. foreach ($this->preparedRoutes[$firstPathSegment] as $route) {
  127. $type = "static";
  128. // Wildcard match. Prioritized over anything else.
  129. if (isset($route['segments'][count($route['segments'])-1]) && $route['segments'][count($route['segments'])-1] == '*') {
  130. $rest = array_slice($explodedPath, count($route['segments']), count($explodedPath)+1);
  131. $type = 'dynamic';
  132. $matches[$type][] = array(
  133. 'params' => array('*' => implode('/', $rest)),
  134. 'action' => $route['action']
  135. );
  136. continue;
  137. }
  138. if (count($route['segments']) + 1 == count($explodedPath)) {
  139. $params = array();
  140. foreach (array_slice($explodedPath, 1) as $pathSegment) {
  141. $routeSegment = current($route['segments']);
  142. if ($routeSegment[0] == ':') {
  143. $params[substr($routeSegment, 1)] = $pathSegment;
  144. $type = "dynamic";
  145. } elseif ($routeSegment != $pathSegment) {
  146. continue 2;
  147. }
  148. next($route['segments']);
  149. }
  150. //To reach here, all segments of query must have matched
  151. //so we store for later, and we'll choose static over dynamic results.
  152. $matches[$type][] = array('params'=>$params, 'action'=>$route['action']);
  153. }
  154. }
  155. } else {
  156. return false;
  157. }
  158. if (!empty($matches['static'])) {
  159. return current($matches['static']);
  160. } else if (!empty($matches['dynamic'])) {
  161. return current($matches['dynamic']);
  162. }
  163. return false;
  164. }
  165. /**
  166. * Set prepared routes
  167. *
  168. * This method sets the prepared routes
  169. *
  170. * @param array $route An array of routes that have been prepared
  171. * @return void
  172. */
  173. public function setPreparedRoutes($routes)
  174. {
  175. $this->preparedRoutes = $routes;
  176. }
  177. }