Route.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. <?php
  2. /**
  3. * @package Grav\Framework\Route
  4. *
  5. * @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Framework\Route;
  9. use Grav\Framework\Uri\UriFactory;
  10. /**
  11. * Implements Grav Route.
  12. *
  13. * @package Grav\Framework\Route
  14. */
  15. class Route
  16. {
  17. /** @var string */
  18. private $root = '';
  19. /** @var string */
  20. private $language = '';
  21. /** @var string */
  22. private $route = '';
  23. /** @var string */
  24. private $extension = '';
  25. /** @var array */
  26. private $gravParams = [];
  27. /** @var array */
  28. private $queryParams = [];
  29. /**
  30. * You can use `RouteFactory` functions to create new `Route` objects.
  31. *
  32. * @param array $parts
  33. * @throws \InvalidArgumentException
  34. */
  35. public function __construct(array $parts = [])
  36. {
  37. $this->initParts($parts);
  38. }
  39. /**
  40. * @return array
  41. */
  42. public function getParts()
  43. {
  44. return [
  45. 'path' => $this->getUriPath(true),
  46. 'query' => $this->getUriQuery(),
  47. 'grav' => [
  48. 'root' => $this->root,
  49. 'language' => $this->language,
  50. 'route' => $this->route,
  51. 'extension' => $this->extension,
  52. 'grav_params' => $this->gravParams,
  53. 'query_params' => $this->queryParams,
  54. ],
  55. ];
  56. }
  57. /**
  58. * @return string
  59. */
  60. public function getRootPrefix()
  61. {
  62. return $this->root;
  63. }
  64. /**
  65. * @return string
  66. */
  67. public function getLanguagePrefix()
  68. {
  69. return $this->language !== '' ? '/' . $this->language : '';
  70. }
  71. /**
  72. * @param int $offset
  73. * @param int|null $length
  74. * @return string
  75. */
  76. public function getRoute($offset = 0, $length = null)
  77. {
  78. if ($offset !== 0 || $length !== null) {
  79. return ($offset === 0 ? '/' : '') . implode('/', $this->getRouteParts($offset, $length));
  80. }
  81. return '/' . $this->route;
  82. }
  83. /**
  84. * @return string
  85. */
  86. public function getExtension()
  87. {
  88. return $this->extension;
  89. }
  90. /**
  91. * @param int $offset
  92. * @param int|null $length
  93. * @return array
  94. */
  95. public function getRouteParts($offset = 0, $length = null)
  96. {
  97. $parts = explode('/', $this->route);
  98. if ($offset !== 0 || $length !== null) {
  99. $parts = \array_slice($parts, $offset, $length);
  100. }
  101. return $parts;
  102. }
  103. /**
  104. * Return array of both query and Grav parameters.
  105. *
  106. * If a parameter exists in both, prefer Grav parameter.
  107. *
  108. * @return array
  109. */
  110. public function getParams()
  111. {
  112. return $this->gravParams + $this->queryParams;
  113. }
  114. /**
  115. * @return array
  116. */
  117. public function getGravParams()
  118. {
  119. return $this->gravParams;
  120. }
  121. /**
  122. * @return array
  123. */
  124. public function getQueryParams()
  125. {
  126. return $this->queryParams;
  127. }
  128. /**
  129. * Return value of the parameter, looking into both Grav parameters and query parameters.
  130. *
  131. * If the parameter exists in both, return Grav parameter.
  132. *
  133. * @param string $param
  134. * @return string|array|null
  135. */
  136. public function getParam($param)
  137. {
  138. return $this->getGravParam($param) ?? $this->getQueryParam($param);
  139. }
  140. /**
  141. * @param string $param
  142. * @return string|null
  143. */
  144. public function getGravParam($param)
  145. {
  146. return $this->gravParams[$param] ?? null;
  147. }
  148. /**
  149. * @param string $param
  150. * @return string|array|null
  151. */
  152. public function getQueryParam($param)
  153. {
  154. return $this->queryParams[$param] ?? null;
  155. }
  156. /**
  157. * Allow the ability to set the route to something else
  158. *
  159. * @param string $route
  160. * @return $this
  161. */
  162. public function withRoute($route)
  163. {
  164. $this->route = $route;
  165. return $this;
  166. }
  167. /**
  168. * Allow the ability to set the root to something else
  169. *
  170. * @param string $root
  171. * @return $this
  172. */
  173. public function withRoot($root)
  174. {
  175. $this->root = $root;
  176. return $this;
  177. }
  178. /**
  179. * @param string $path
  180. * @return Route
  181. */
  182. public function withAddedPath($path)
  183. {
  184. $this->route .= '/' . ltrim($path, '/');
  185. return $this;
  186. }
  187. /**
  188. * @param string $extension
  189. * @return Route
  190. */
  191. public function withExtension($extension)
  192. {
  193. $this->extension = $extension;
  194. return $this;
  195. }
  196. /**
  197. * @param string $param
  198. * @param mixed $value
  199. * @return Route
  200. */
  201. public function withGravParam($param, $value)
  202. {
  203. return $this->withParam('gravParams', $param, null !== $value ? (string)$value : null);
  204. }
  205. /**
  206. * @param string $param
  207. * @param mixed $value
  208. * @return Route
  209. */
  210. public function withQueryParam($param, $value)
  211. {
  212. return $this->withParam('queryParams', $param, $value);
  213. }
  214. public function withoutParams()
  215. {
  216. return $this->withoutGravParams()->withoutQueryParams();
  217. }
  218. public function withoutGravParams()
  219. {
  220. $this->gravParams = [];
  221. return $this;
  222. }
  223. public function withoutQueryParams()
  224. {
  225. $this->queryParams = [];
  226. return $this;
  227. }
  228. /**
  229. * @return \Grav\Framework\Uri\Uri
  230. */
  231. public function getUri()
  232. {
  233. return UriFactory::createFromParts($this->getParts());
  234. }
  235. /**
  236. * @param bool $includeRoot
  237. * @return string
  238. */
  239. public function toString(bool $includeRoot = false)
  240. {
  241. $url = $this->getUriPath($includeRoot);
  242. if ($this->queryParams) {
  243. $url .= '?' . $this->getUriQuery();
  244. }
  245. return $url;
  246. }
  247. /**
  248. * @return string
  249. * @deprecated 1.6 Use ->toString(true) or ->getUri() instead.
  250. */
  251. public function __toString()
  252. {
  253. user_error(__CLASS__ . '::' . __FUNCTION__ . '() will change in the future to return route, not relative url: use ->toString(true) or ->getUri() instead.', E_USER_DEPRECATED);
  254. return $this->toString(true);
  255. }
  256. /**
  257. * @param string $type
  258. * @param string $param
  259. * @param mixed $value
  260. * @return static
  261. */
  262. protected function withParam($type, $param, $value)
  263. {
  264. $values = $this->{$type} ?? [];
  265. $oldValue = $values[$param] ?? null;
  266. if ($oldValue === $value) {
  267. return $this;
  268. }
  269. $new = $this->copy();
  270. if ($value === null) {
  271. unset($values[$param]);
  272. } else {
  273. $values[$param] = $value;
  274. }
  275. $new->{$type} = $values;
  276. return $new;
  277. }
  278. protected function copy()
  279. {
  280. return clone $this;
  281. }
  282. /**
  283. * @param bool $includeRoot
  284. * @return string
  285. */
  286. protected function getUriPath($includeRoot = false)
  287. {
  288. $parts = $includeRoot ? [$this->root] : [''];
  289. if ($this->language !== '') {
  290. $parts[] = $this->language;
  291. }
  292. if ($this->route !== '') {
  293. $parts[] = $this->extension ? $this->route . '.' . $this->extension : $this->route;
  294. }
  295. if ($this->gravParams) {
  296. $parts[] = RouteFactory::buildParams($this->gravParams);
  297. }
  298. return implode('/', $parts);
  299. }
  300. /**
  301. * @return string
  302. */
  303. protected function getUriQuery()
  304. {
  305. return UriFactory::buildQuery($this->queryParams);
  306. }
  307. /**
  308. * @param array $parts
  309. */
  310. protected function initParts(array $parts)
  311. {
  312. if (isset($parts['grav'])) {
  313. $gravParts = $parts['grav'];
  314. $this->root = $gravParts['root'];
  315. $this->language = $gravParts['language'];
  316. $this->route = $gravParts['route'];
  317. $this->extension = $gravParts['extension'] ?? '';
  318. $this->gravParams = $gravParts['params'] ?: [];
  319. $this->queryParams = $parts['query_params'] ?: [];
  320. } else {
  321. $this->root = RouteFactory::getRoot();
  322. $this->language = RouteFactory::getLanguage();
  323. $path = $parts['path'] ?? '/';
  324. if (isset($parts['params'])) {
  325. $this->route = trim(rawurldecode($path), '/');
  326. $this->gravParams = $parts['params'];
  327. } else {
  328. $this->route = trim(RouteFactory::stripParams($path, true), '/');
  329. $this->gravParams = RouteFactory::getParams($path);
  330. }
  331. if (isset($parts['query'])) {
  332. $this->queryParams = UriFactory::parseQuery($parts['query']);
  333. }
  334. }
  335. }
  336. }