PageRenderTime 25ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/zendframework/zend-mvc/src/Router/Http/TreeRouteStack.php

https://gitlab.com/yousafsyed/easternglamor
PHP | 437 lines | 248 code | 68 blank | 121 comment | 55 complexity | b4cd804deb5f6bbb5f7878aa7dc6a4e1 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Mvc\Router\Http;
  10. use ArrayObject;
  11. use Traversable;
  12. use Zend\Mvc\Router\Exception;
  13. use Zend\Mvc\Router\SimpleRouteStack;
  14. use Zend\Stdlib\ArrayUtils;
  15. use Zend\Stdlib\RequestInterface as Request;
  16. use Zend\Uri\Http as HttpUri;
  17. /**
  18. * Tree search implementation.
  19. */
  20. class TreeRouteStack extends SimpleRouteStack
  21. {
  22. /**
  23. * Base URL.
  24. *
  25. * @var string
  26. */
  27. protected $baseUrl;
  28. /**
  29. * Request URI.
  30. *
  31. * @var HttpUri
  32. */
  33. protected $requestUri;
  34. /**
  35. * Prototype routes.
  36. *
  37. * We use an ArrayObject in this case so we can easily pass it down the tree
  38. * by reference.
  39. *
  40. * @var ArrayObject
  41. */
  42. protected $prototypes;
  43. /**
  44. * factory(): defined by RouteInterface interface.
  45. *
  46. * @see \Zend\Mvc\Router\RouteInterface::factory()
  47. * @param array|Traversable $options
  48. * @return SimpleRouteStack
  49. * @throws Exception\InvalidArgumentException
  50. */
  51. public static function factory($options = array())
  52. {
  53. if ($options instanceof Traversable) {
  54. $options = ArrayUtils::iteratorToArray($options);
  55. } elseif (!is_array($options)) {
  56. throw new Exception\InvalidArgumentException(__METHOD__ . ' expects an array or Traversable set of options');
  57. }
  58. $instance = parent::factory($options);
  59. if (isset($options['prototypes'])) {
  60. $instance->addPrototypes($options['prototypes']);
  61. }
  62. return $instance;
  63. }
  64. /**
  65. * init(): defined by SimpleRouteStack.
  66. *
  67. * @see SimpleRouteStack::init()
  68. */
  69. protected function init()
  70. {
  71. $this->prototypes = new ArrayObject;
  72. $routes = $this->routePluginManager;
  73. foreach (array(
  74. 'chain' => __NAMESPACE__ . '\Chain',
  75. 'hostname' => __NAMESPACE__ . '\Hostname',
  76. 'literal' => __NAMESPACE__ . '\Literal',
  77. 'method' => __NAMESPACE__ . '\Method',
  78. 'part' => __NAMESPACE__ . '\Part',
  79. 'query' => __NAMESPACE__ . '\Query',
  80. 'regex' => __NAMESPACE__ . '\Regex',
  81. 'scheme' => __NAMESPACE__ . '\Scheme',
  82. 'segment' => __NAMESPACE__ . '\Segment',
  83. 'wildcard' => __NAMESPACE__ . '\Wildcard',
  84. ) as $name => $class
  85. ) {
  86. $routes->setInvokableClass($name, $class);
  87. };
  88. }
  89. /**
  90. * addRoute(): defined by RouteStackInterface interface.
  91. *
  92. * @see RouteStackInterface::addRoute()
  93. * @param string $name
  94. * @param mixed $route
  95. * @param int $priority
  96. * @return TreeRouteStack
  97. */
  98. public function addRoute($name, $route, $priority = null)
  99. {
  100. if (!$route instanceof RouteInterface) {
  101. $route = $this->routeFromArray($route);
  102. }
  103. return parent::addRoute($name, $route, $priority);
  104. }
  105. /**
  106. * routeFromArray(): defined by SimpleRouteStack.
  107. *
  108. * @see SimpleRouteStack::routeFromArray()
  109. * @param string|array|Traversable $specs
  110. * @return RouteInterface
  111. * @throws Exception\InvalidArgumentException When route definition is not an array nor traversable
  112. * @throws Exception\InvalidArgumentException When chain routes are not an array nor traversable
  113. * @throws Exception\RuntimeException When a generated routes does not implement the HTTP route interface
  114. */
  115. protected function routeFromArray($specs)
  116. {
  117. if (is_string($specs)) {
  118. if (null === ($route = $this->getPrototype($specs))) {
  119. throw new Exception\RuntimeException(sprintf('Could not find prototype with name %s', $specs));
  120. }
  121. return $route;
  122. } elseif ($specs instanceof Traversable) {
  123. $specs = ArrayUtils::iteratorToArray($specs);
  124. } elseif (!is_array($specs)) {
  125. throw new Exception\InvalidArgumentException('Route definition must be an array or Traversable object');
  126. }
  127. if (isset($specs['chain_routes'])) {
  128. if (!is_array($specs['chain_routes'])) {
  129. throw new Exception\InvalidArgumentException('Chain routes must be an array or Traversable object');
  130. }
  131. $chainRoutes = array_merge(array($specs), $specs['chain_routes']);
  132. unset($chainRoutes[0]['chain_routes']);
  133. if (isset($specs['child_routes'])) {
  134. unset($chainRoutes[0]['child_routes']);
  135. }
  136. $options = array(
  137. 'routes' => $chainRoutes,
  138. 'route_plugins' => $this->routePluginManager,
  139. 'prototypes' => $this->prototypes,
  140. );
  141. $route = $this->routePluginManager->get('chain', $options);
  142. } else {
  143. $route = parent::routeFromArray($specs);
  144. }
  145. if (!$route instanceof RouteInterface) {
  146. throw new Exception\RuntimeException('Given route does not implement HTTP route interface');
  147. }
  148. if (isset($specs['child_routes'])) {
  149. $options = array(
  150. 'route' => $route,
  151. 'may_terminate' => (isset($specs['may_terminate']) && $specs['may_terminate']),
  152. 'child_routes' => $specs['child_routes'],
  153. 'route_plugins' => $this->routePluginManager,
  154. 'prototypes' => $this->prototypes,
  155. );
  156. $priority = (isset($route->priority) ? $route->priority : null);
  157. $route = $this->routePluginManager->get('part', $options);
  158. $route->priority = $priority;
  159. }
  160. return $route;
  161. }
  162. /**
  163. * Add multiple prototypes at once.
  164. *
  165. * @param Traversable $routes
  166. * @return TreeRouteStack
  167. * @throws Exception\InvalidArgumentException
  168. */
  169. public function addPrototypes($routes)
  170. {
  171. if (!is_array($routes) && !$routes instanceof Traversable) {
  172. throw new Exception\InvalidArgumentException('addPrototypes expects an array or Traversable set of routes');
  173. }
  174. foreach ($routes as $name => $route) {
  175. $this->addPrototype($name, $route);
  176. }
  177. return $this;
  178. }
  179. /**
  180. * Add a prototype.
  181. *
  182. * @param string $name
  183. * @param mixed $route
  184. * @return TreeRouteStack
  185. */
  186. public function addPrototype($name, $route)
  187. {
  188. if (!$route instanceof RouteInterface) {
  189. $route = $this->routeFromArray($route);
  190. }
  191. $this->prototypes[$name] = $route;
  192. return $this;
  193. }
  194. /**
  195. * Get a prototype.
  196. *
  197. * @param string $name
  198. * @return RouteInterface|null
  199. */
  200. public function getPrototype($name)
  201. {
  202. if (isset($this->prototypes[$name])) {
  203. return $this->prototypes[$name];
  204. }
  205. return;
  206. }
  207. /**
  208. * match(): defined by \Zend\Mvc\Router\RouteInterface
  209. *
  210. * @see \Zend\Mvc\Router\RouteInterface::match()
  211. * @param Request $request
  212. * @param integer|null $pathOffset
  213. * @param array $options
  214. * @return RouteMatch|null
  215. */
  216. public function match(Request $request, $pathOffset = null, array $options = array())
  217. {
  218. if (!method_exists($request, 'getUri')) {
  219. return;
  220. }
  221. if ($this->baseUrl === null && method_exists($request, 'getBaseUrl')) {
  222. $this->setBaseUrl($request->getBaseUrl());
  223. }
  224. $uri = $request->getUri();
  225. $baseUrlLength = strlen($this->baseUrl) ?: null;
  226. if ($pathOffset !== null) {
  227. $baseUrlLength += $pathOffset;
  228. }
  229. if ($this->requestUri === null) {
  230. $this->setRequestUri($uri);
  231. }
  232. if ($baseUrlLength !== null) {
  233. $pathLength = strlen($uri->getPath()) - $baseUrlLength;
  234. } else {
  235. $pathLength = null;
  236. }
  237. foreach ($this->routes as $name => $route) {
  238. if (
  239. ($match = $route->match($request, $baseUrlLength, $options)) instanceof RouteMatch
  240. && ($pathLength === null || $match->getLength() === $pathLength)
  241. ) {
  242. $match->setMatchedRouteName($name);
  243. foreach ($this->defaultParams as $paramName => $value) {
  244. if ($match->getParam($paramName) === null) {
  245. $match->setParam($paramName, $value);
  246. }
  247. }
  248. return $match;
  249. }
  250. }
  251. return;
  252. }
  253. /**
  254. * assemble(): defined by \Zend\Mvc\Router\RouteInterface interface.
  255. *
  256. * @see \Zend\Mvc\Router\RouteInterface::assemble()
  257. * @param array $params
  258. * @param array $options
  259. * @return mixed
  260. * @throws Exception\InvalidArgumentException
  261. * @throws Exception\RuntimeException
  262. */
  263. public function assemble(array $params = array(), array $options = array())
  264. {
  265. if (!isset($options['name'])) {
  266. throw new Exception\InvalidArgumentException('Missing "name" option');
  267. }
  268. $names = explode('/', $options['name'], 2);
  269. $route = $this->routes->get($names[0]);
  270. if (!$route) {
  271. throw new Exception\RuntimeException(sprintf('Route with name "%s" not found', $names[0]));
  272. }
  273. if (isset($names[1])) {
  274. if (!$route instanceof TreeRouteStack) {
  275. throw new Exception\RuntimeException(sprintf('Route with name "%s" does not have child routes', $names[0]));
  276. }
  277. $options['name'] = $names[1];
  278. } else {
  279. unset($options['name']);
  280. }
  281. if (isset($options['only_return_path']) && $options['only_return_path']) {
  282. return $this->baseUrl . $route->assemble(array_merge($this->defaultParams, $params), $options);
  283. }
  284. if (!isset($options['uri'])) {
  285. $uri = new HttpUri();
  286. if (isset($options['force_canonical']) && $options['force_canonical']) {
  287. if ($this->requestUri === null) {
  288. throw new Exception\RuntimeException('Request URI has not been set');
  289. }
  290. $uri->setScheme($this->requestUri->getScheme())
  291. ->setHost($this->requestUri->getHost())
  292. ->setPort($this->requestUri->getPort());
  293. }
  294. $options['uri'] = $uri;
  295. } else {
  296. $uri = $options['uri'];
  297. }
  298. $path = $this->baseUrl . $route->assemble(array_merge($this->defaultParams, $params), $options);
  299. if (isset($options['query'])) {
  300. $uri->setQuery($options['query']);
  301. }
  302. if (isset($options['fragment'])) {
  303. $uri->setFragment($options['fragment']);
  304. }
  305. if ((isset($options['force_canonical']) && $options['force_canonical']) || $uri->getHost() !== null || $uri->getScheme() !== null) {
  306. if (($uri->getHost() === null || $uri->getScheme() === null) && $this->requestUri === null) {
  307. throw new Exception\RuntimeException('Request URI has not been set');
  308. }
  309. if ($uri->getHost() === null) {
  310. $uri->setHost($this->requestUri->getHost());
  311. }
  312. if ($uri->getScheme() === null) {
  313. $uri->setScheme($this->requestUri->getScheme());
  314. }
  315. $uri->setPath($path);
  316. if (!isset($options['normalize_path']) || $options['normalize_path']) {
  317. $uri->normalize();
  318. }
  319. return $uri->toString();
  320. } elseif (!$uri->isAbsolute() && $uri->isValidRelative()) {
  321. $uri->setPath($path);
  322. if (!isset($options['normalize_path']) || $options['normalize_path']) {
  323. $uri->normalize();
  324. }
  325. return $uri->toString();
  326. }
  327. return $path;
  328. }
  329. /**
  330. * Set the base URL.
  331. *
  332. * @param string $baseUrl
  333. * @return self
  334. */
  335. public function setBaseUrl($baseUrl)
  336. {
  337. $this->baseUrl = rtrim($baseUrl, '/');
  338. return $this;
  339. }
  340. /**
  341. * Get the base URL.
  342. *
  343. * @return string
  344. */
  345. public function getBaseUrl()
  346. {
  347. return $this->baseUrl;
  348. }
  349. /**
  350. * Set the request URI.
  351. *
  352. * @param HttpUri $uri
  353. * @return TreeRouteStack
  354. */
  355. public function setRequestUri(HttpUri $uri)
  356. {
  357. $this->requestUri = $uri;
  358. return $this;
  359. }
  360. /**
  361. * Get the request URI.
  362. *
  363. * @return HttpUri
  364. */
  365. public function getRequestUri()
  366. {
  367. return $this->requestUri;
  368. }
  369. }