diff --git a/CHANGELOG.md b/CHANGELOG.md index ab7434661..057157c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ 1. [](#new) * Added `Grav\Framework\Uri` classes extending PSR-7 `HTTP message UriInterface` implementation - * Added `$grav['uri]->getCurrentUri()` function to get `Grav\Framework\Uri` instance for the current URL. + * Added `Grav\Framework\Route` classes to allow route/link manipulation + * Added `$grav['uri]->getCurrentUri()` method to get `Grav\Framework\Uri\Uri` instance for the current URL + * Added `$grav['uri]->getCurrentRoute()` method to get `Grav\Framework\Route\Route` instance for the current URL * Added ability to have `php` version dependencies in GPM assets 1. [](#bugfix) * Fixed issue with remote PHP version determination for Grav updates diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index 7f651c964..51d904c27 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -8,7 +8,10 @@ namespace Grav\Common; +use Grav\Common\Config\Config; +use Grav\Common\Language\Language; use Grav\Common\Page\Page; +use Grav\Framework\Route\RouteFactory; use Grav\Framework\Uri\UriFactory; use Grav\Framework\Uri\UriPartsFilter; @@ -19,6 +22,9 @@ class Uri /** @var \Grav\Framework\Uri\Uri */ protected static $currentUri; + /** @var \Grav\Framework\Route\Route */ + protected static $currentRoute; + public $url; // Uri parts. @@ -109,7 +115,10 @@ class Uri { $grav = Grav::instance(); + /** @var Config $config */ $config = $grav['config']; + + /** @var Language $language */ $language = $grav['language']; // add the port to the base for non-standard ports @@ -193,6 +202,9 @@ class Uri $grav['base_url_absolute'] = $grav['config']->get('system.custom_base_url') ?: $this->rootUrl(true); $grav['base_url_relative'] = $this->rootUrl(false); $grav['base_url'] = $grav['config']->get('system.absolute_urls') ? $grav['base_url_absolute'] : $grav['base_url_relative']; + + RouteFactory::setRoot($this->root_path); + RouteFactory::setLanguage($language->getLanguageURLPrefix()); } /** @@ -614,6 +626,21 @@ class Uri return static::$currentUri; } + /** + * Returns current route. + * + * @return \Grav\Framework\Route\Route + */ + public static function getCurrentRoute() + { + if (!static::$currentRoute) { + $uri = Grav::instance()['uri']; + static::$currentRoute = RouteFactory::createFromParts($uri->toArray()); + } + + return static::$currentRoute; + } + /** * Is this an external URL? if it starts with `http` then yes, else false * diff --git a/system/src/Grav/Framework/Route/Route.php b/system/src/Grav/Framework/Route/Route.php new file mode 100644 index 000000000..9d841a861 --- /dev/null +++ b/system/src/Grav/Framework/Route/Route.php @@ -0,0 +1,279 @@ +initParts($parts); + } + + /** + * @return array + */ + public function getParts() + { + return [ + 'path' => $this->getUriPath(), + 'query' => $this->getUriQuery(), + 'grav' => [ + 'root' => $this->root, + 'language' => $this->language, + 'route' => $this->route, + 'grav_params' => $this->gravParams, + 'query_params' => $this->queryParams, + ], + ]; + } + + /** + * @return string + */ + public function getRootPrefix() + { + return $this->root; + } + + /** + * @return string + */ + public function getLanguagePrefix() + { + return $this->language !== '' ? '/' . $this->language : ''; + } + + /** + * @return string + */ + public function getRoute() + { + return '/' . $this->route; + } + + /** + * Return array of both query and Grav parameters. + * + * If a parameter exists in both, prefer Grav parameter. + * + * @return array + */ + public function getParams() + { + return $this->gravParams + $this->queryParams; + } + + /** + * @return array + */ + public function getGravParams() + { + return $this->gravParams; + } + + /** + * @return array + */ + public function getQueryParams() + { + return $this->queryParams; + } + + /** + * Return value of the parameter, looking into both Grav parameters and query parameters. + * + * If the parameter exists in both, return Grav parameter. + * + * @param string $param + * @return string|null + */ + public function getParam($param) + { + $value = $this->getGravParam($param); + if ($value === null) { + $value = $this->getQueryParam($param); + } + + return $value; + } + + /** + * @param string $param + * @return string|null + */ + public function getGravParam($param) + { + return isset($this->gravParams[$param]) ? $this->gravParams[$param] : null; + } + + /** + * @param string $param + * @return string|null + */ + public function getQueryParam($param) + { + return isset($this->queryParams[$param]) ? $this->queryParams[$param] : null; + } + + /** + * @param string $param + * @param mixed $value + * @return Route + */ + public function withGravParam($param, $value) + { + return $this->withParam('gravParams', $param, $value); + } + + /** + * @param string $param + * @param mixed $value + * @return Route + */ + public function withQueryParam($param, $value) + { + return $this->withParam('queryParams', $param, $value); + } + + /** + * @return \Grav\Framework\Uri\Uri + */ + public function getUri() + { + return UriFactory::createFromParts($this->getParts()); + } + + /** + * @return string + */ + public function __toString() + { + $url = $this->getUriPath(); + + if ($this->queryParams) { + $url .= '?' . $this->getUriQuery(); + } + + return $url; + } + + /** + * @param string $type + * @param string $param + * @param mixed $value + * @return static + */ + protected function withParam($type, $param, $value) + { + $oldValue = isset($this->{$type}[$param]) ? $this->{$type}[$param] : null; + $newValue = null !== $value ? (string)$value : null; + + if ($oldValue === $newValue) { + return $this; + } + + $new = clone $this; + if ($newValue === null) { + unset($new->{$type}[$param]); + } else { + $new->{$type}[$param] = $newValue; + } + + return $new; + } + + /** + * @return string + */ + protected function getUriPath() + { + $parts = [$this->root]; + + if ($this->language !== '') { + $parts[] = $this->language; + } + + if ($this->route !== '') { + $parts[] = $this->route; + } + + if ($this->gravParams) { + $parts[] = RouteFactory::buildParams($this->gravParams); + } + + return implode('/', $parts); + } + + /** + * @return string + */ + protected function getUriQuery() + { + return UriFactory::buildQuery($this->queryParams); + } + + /** + * @param array $parts + */ + protected function initParts(array $parts) + { + if (isset($parts['grav'])) { + $gravParts = $parts['grav']; + $this->root = $gravParts['root']; + $this->language = $gravParts['language']; + $this->route = $gravParts['route']; + $this->gravParams = $gravParts['params']; + $this->queryParams = $parts['query_params']; + + } else { + $this->root = RouteFactory::getRoot(); + $this->language = RouteFactory::getLanguage(); + + $path = isset($parts['path']) ? $parts['path'] : '/'; + if (isset($parts['params'])) { + $this->route = trim(rawurldecode($path), '/'); + $this->gravParams = $parts['params']; + } else { + $this->route = trim(RouteFactory::stripParams($path, true), '/'); + $this->gravParams = RouteFactory::getParams($path); + } + if (isset($parts['query'])) { + $this->queryParams = UriFactory::parseQuery($parts['query'], true); + } + } + } +} diff --git a/system/src/Grav/Framework/Route/RouteFactory.php b/system/src/Grav/Framework/Route/RouteFactory.php new file mode 100644 index 000000000..a406926cf --- /dev/null +++ b/system/src/Grav/Framework/Route/RouteFactory.php @@ -0,0 +1,131 @@ + $value) { + $output[] = "{$key}{$delimiter}{$value}"; + } + + return implode('/', $output); + } + + /** + * @param string $path + * @param bool $decode + * @return string + */ + public static function stripParams($path, $decode = false) + { + $pos = strpos($path, self::$delimiter); + + if ($pos === false) { + return $path; + } + + $path = dirname(substr($path, 0, $pos)); + if ($path === '.') { + return ''; + } + + return $decode ? rawurldecode($path) : $path; + } + + /** + * @param string $path + * @return array + */ + public static function getParams($path) + { + $params = ltrim(substr($path, strlen(static::stripParams($path))), '/'); + + return $params !== '' ? static::parseParams($params) : []; + } + + /** + * @param string $str + * @return array + */ + public static function parseParams($str) + { + $delimiter = self::$delimiter; + + $params = explode('/', $str); + foreach ($params as &$param) { + $parts = explode($delimiter, $param, 2); + if (isset($parts[1])) { + $param[rawurldecode($parts[0])] = rawurldecode($parts[1]); + } + } + + return $params; + } +}