)(?=[^\w/._\-&])~s PATTERN; public const URLIZE_PATTERN_EMAIL = <<urlBase = trim($urlBase, "/"); $this->assetToken = $assetToken; $this->appUrl = $appUrl; $this->forceSSL = $forceSSL; } public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token, array $options = []) : string { switch($token) { case "asset": return "compiler->getExtensionFromToken('$token')->buildAssetUrl($arguments) ?>"; case "route": return "compiler->getExtensionFromToken('$token')->buildRouteUrl($arguments) ?>"; case "url": if ( in_array('parameters', $options) ) { return "compiler->getExtensionFromToken('$token')->setUrlParameters($arguments) ?>"; } elseif ( in_array('current', $options) ) { return "compiler->getExtensionFromToken('$token')->currentUrl($arguments) ?>"; } return "compiler->getExtensionFromToken('$token')->buildUrl($arguments) ?>"; case "slug": return "compiler->getExtensionFromToken('$token')->slug($arguments) ?>"; } throw new \ParseExtensionException("Unknown token given $token in UrlExtension."); } public function exportFunctions(): array { return [ "url" => [ $this, 'buildUrl' ], "current_url" => [ $this, 'currentUrl' ], "asset" => [ $this, 'buildAssetUrl' ], "route" => [ $this, 'buildRouteUrl' ], "slug" => [ $this, 'slug' ], "urlize" => [ $this, 'urlize' ] ]; } public function getRouteList(bool $full = false) : array { return $this->routes; } public function setUrlParameters(string $url = "", array $parameters = []) : string { return $url . ( $parameters ? "?" . http_build_query($parameters) : "" ); } public function urlize(string $string) : string { # Normal URL patterns $string = preg_replace(static::URLIZE_PATTERN_URL, '$0', $string); # Email patterns $string = preg_replace(static::URLIZE_PATTERN_EMAIL, '$0', $string); return $string; } public function currentUrl(array $parameters = []) : string { return $this->buildUrl($this->uri(), $parameters); } public function buildUrl(string $uri = "", array $parameters = [], bool $appendVersion = false) : string { return $this->setUrlParameters($this->url() . "/" . ltrim($uri, "/"), $appendVersion ? array_replace([ 'v' => $this->assetToken ], $parameters) : $parameters); } public function buildAssetUrl(string $uri, array $parameters = [], bool $appendVersion = true) : string { return $this->buildUrl($uri, $parameters, $appendVersion); } public function buildRouteUrl(string $name, array $parameters = [], bool $appendVersion = false) : string { if ( false !== ( $route = $this->routes[$name] ?? false ) ) { return $this->buildUrl($this->prepareRoute($route, $parameters), $parameters, $appendVersion); } $routeList = json_encode($this->routes, \JSON_PRETTY_PRINT); throw new \RuntimeException(" Given route `$name` could not be found within current route list: $routeList "); } public function url() : string { return $this->scheme() . $this->domain() . $this->base(); } # src: https://stackoverflow.com/a/14550919 public static function slug(string $text, string $separator = '-') : string { $clean = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text); $clean = preg_replace("/[^a-zA-Z0-9\/_| -]/", '', $clean); return preg_replace("/[\/_| -]+/", $separator, strtolower(trim($clean, '-'))); } public function registerRoute(string $name, string $route, string $class, string $method, array $routeMethods) : void { $this->routes[$name] = [ 'route' => $route, 'routeMethods' => $routeMethods, 'class' => $class, 'classMethod' => $method, ]; } /** * Return URI formatted */ protected function uri(bool $queryString = false) : string { $uri = ( $_SERVER['REQUEST_URI'] ?? $_SERVER['PHP_SELF'] ?? "" ) . ( $queryString ? $this->queryString() : "" ); if ( ! $queryString ) { $uri = explode('?', $uri, 2)[0]; } return '/' . ltrim(substr($uri, strlen($this->base())), '/'); } /** * Return query string */ protected function queryString() : string { return isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ""; } protected function scheme() : string { return ( $this->forceSSL || $this->isHttps() ? "https" : "http" ) . "://"; } protected function base() : string { return $this->urlBase ? "/" . $this->urlBase : ""; } /** * Check if Uri's segments are valid * @param array $segments The uri's segments */ protected function secure($segments = []) : array { return array_diff($segments, [ '..', '://' ]); } protected function domain() : string { if ( ! empty($_SERVER['HTTP_X_FORWARDED_PROTO']) || ! empty($_SERVER['HTTP_X_FORWARDED_SSL']) ) { $port = ""; } else { $port = $this->isDefaultPort() ? "" : ":" . $_SERVER['SERVER_PORT']; } return strtolower($this->appUrl ? $this->appUrl . $port : $_SERVER['HTTP_HOST']); } protected function isDefaultPort() : bool { return in_array($_SERVER['SERVER_PORT'], [ 80, 443 ]); } protected function isHttps() : bool { $https = in_array('https', array_map("strtolower", [ $_SERVER['REQUEST_SCHEME'] ?? "", $_SERVER['X-Url-Scheme'] ?? "", $_SERVER['Front-End-Https'] ?? "", $_SERVER['X-Forwarded-Proto'] ?? "", $_SERVER['X-Forwarded-Protocol'] ?? "", $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? "", $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] ?? "", ])) || isset($_SERVER['HTTP_X_ARR_SSL']); return $https || ( "443" === ( $_SERVER['SERVER_PORT'] ?? "" ) ) || ( "443" === ( $_SERVER['HTTP_X_FORWARDED_PORT'] ?? "" ) ) || ( "off" !== ( strtolower($_SERVER['HTTPS'] ?? $_SERVER['HTTP_X_FORWARDED_SSL'] ?? $_SERVER['X-Forwarded-Ssl'] ?? "off")) ); } protected function prepareRoute(array $routeParam, array &$arguments) { $route = $routeParam['route']; if ( preg_match_all('~{(.*?)}~si', $route, $matches, PREG_SET_ORDER) ) { $search = []; foreach($matches as $item) { $default = null; $variable = $item[1]; # Handles default if (strpos($variable, "=") !== false) { list($variable, $default) = explode('=', $item[1]); } # Handles type if (strpos($variable, ":") !== false) { list($variable, ) = explode(':', $item[1]); } if ( array_key_exists($variable, $arguments) ) { $value = $arguments[ $variable ]; unset($arguments[ $variable ]); } else { if ($default ?? false) { $value = $default; } elseif ($this->enforceExistingArguments) {dump($routeParam); throw new \RuntimeException(sprintf("Error while preparing route %s : could not match variable '%s' into given arguments ( %s ) from %s::%s", $route, $variable, json_encode($arguments), $routeParam['class'], $routeParam['classMethod'])); } } $search[$item[0]] = $value ?? ""; } $route = str_replace(array_keys($search), array_values($search), $route); } /* * @TODO - must also take into account that recursivity is possible here [/test[/another[/still]]], * so the regex must be adjusted (or simply using another method could also be a possiblity) * while(strpos($route, '[') !== false && strpos($route, ']') !== false ) { if ( preg_match_all('~[(.*?)]~si', $route, $matches, PREG_SET_ORDER) ) { } }*/ $route = trim(str_replace([ '[', ']' ], '', $route), '/'); return $route; } }