parseUri($uri); } } /** * @param string $uri */ protected function parseUri($uri): void { if (false === ($uriParts = parse_url($uri))) { // congratulations if you've managed to get parse_url to fail, // it seems to always return some semblance of a parsed url no matter what throw new InvalidArgumentException("Invalid URI: $uri"); } if (!isset($uriParts['scheme'])) { throw new InvalidArgumentException('Invalid URI: http|https scheme required'); } $this->scheme = $uriParts['scheme']; $this->host = $uriParts['host']; if (isset($uriParts['port'])) { $this->port = $uriParts['port']; $this->explicitPortSpecified = true; } else { $this->port = strcmp('https', $uriParts['scheme']) ? 80 : 443; $this->explicitPortSpecified = false; } if (isset($uriParts['path'])) { $this->path = $uriParts['path']; if ('/' === $uriParts['path']) { $this->explicitTrailingHostSlash = true; } } else { $this->path = '/'; } $this->query = $uriParts['query'] ?? ''; $this->fragment = $uriParts['fragment'] ?? ''; $userInfo = ''; if (!empty($uriParts['user'])) { $userInfo .= $uriParts['user']; } if ($userInfo && !empty($uriParts['pass'])) { $userInfo .= ':' . $uriParts['pass']; } $this->setUserInfo($userInfo); } /** * @param string $rawUserInfo * * @return string */ protected function protectUserInfo($rawUserInfo) { $colonPos = strpos($rawUserInfo, ':'); // rfc3986-3.2.1 | http://tools.ietf.org/html/rfc3986#section-3.2 // "Applications should not render as clear text any data // after the first colon (":") character found within a userinfo // subcomponent unless the data after the colon is the empty string // (indicating no password)" if ($colonPos !== false && strlen($rawUserInfo) - 1 > $colonPos) { return substr($rawUserInfo, 0, $colonPos) . ':********'; } return $rawUserInfo; } /** * @return string */ public function getScheme() { return $this->scheme; } /** * @return string */ public function getUserInfo() { return $this->userInfo; } /** * @return string */ public function getRawUserInfo() { return $this->rawUserInfo; } /** * @return string */ public function getHost() { return $this->host; } /** * @return int */ public function getPort() { return $this->port; } /** * @return string */ public function getPath() { return $this->path; } /** * @return string */ public function getQuery() { return $this->query; } /** * @return string */ public function getFragment() { return $this->fragment; } /** * Uses protected user info by default as per rfc3986-3.2.1 * Uri::getRawAuthority() is available if plain-text password information is desirable. * * @return string */ public function getAuthority() { $authority = $this->userInfo ? $this->userInfo . '@' : ''; $authority .= $this->host; if ($this->explicitPortSpecified) { $authority .= ":{$this->port}"; } return $authority; } /** * @return string */ public function getRawAuthority() { $authority = $this->rawUserInfo ? $this->rawUserInfo . '@' : ''; $authority .= $this->host; if ($this->explicitPortSpecified) { $authority .= ":{$this->port}"; } return $authority; } /** * @return string */ public function getAbsoluteUri() { $uri = $this->scheme . '://' . $this->getRawAuthority(); if ('/' === $this->path) { $uri .= $this->explicitTrailingHostSlash ? '/' : ''; } else { $uri .= $this->path; } if (!empty($this->query)) { $uri .= "?{$this->query}"; } if (!empty($this->fragment)) { $uri .= "#{$this->fragment}"; } return $uri; } /** * @return string */ public function getRelativeUri() { $uri = ''; if ('/' === $this->path) { $uri .= $this->explicitTrailingHostSlash ? '/' : ''; } else { $uri .= $this->path; } return $uri; } /** * Uses protected user info by default as per rfc3986-3.2.1 * Uri::getAbsoluteUri() is available if plain-text password information is desirable. * * @return string */ public function __toString() { $uri = $this->scheme . '://' . $this->getAuthority(); if ('/' === $this->path) { $uri .= $this->explicitTrailingHostSlash ? '/' : ''; } else { $uri .= $this->path; } if (!empty($this->query)) { $uri .= "?{$this->query}"; } if (!empty($this->fragment)) { $uri .= "#{$this->fragment}"; } return $uri; } /** * @param $path */ public function setPath($path): void { if (empty($path)) { $this->path = '/'; $this->explicitTrailingHostSlash = false; } else { $this->path = $path; if ('/' === $this->path) { $this->explicitTrailingHostSlash = true; } } } /** * @param string $query */ public function setQuery($query): void { $this->query = $query; } /** * @param string $var * @param string $val */ public function addToQuery($var, $val): void { if (strlen($this->query) > 0) { $this->query .= '&'; } $this->query .= http_build_query([$var => $val], '', '&'); } /** * @param string $fragment */ public function setFragment($fragment): void { $this->fragment = $fragment; } /** * @param string $scheme */ public function setScheme($scheme): void { $this->scheme = $scheme; } /** * @param string $userInfo */ public function setUserInfo($userInfo): void { $this->userInfo = $userInfo ? $this->protectUserInfo($userInfo) : ''; $this->rawUserInfo = $userInfo; } /** * @param int $port */ public function setPort($port): void { $this->port = (int) $port; if (('https' === $this->scheme && $this->port === 443) || ('http' === $this->scheme && $this->port === 80)) { $this->explicitPortSpecified = false; } else { $this->explicitPortSpecified = true; } } /** * @param string $host */ public function setHost($host): void { $this->host = $host; } /** * @return bool */ public function hasExplicitTrailingHostSlash() { return $this->explicitTrailingHostSlash; } /** * @return bool */ public function hasExplicitPortSpecified() { return $this->explicitPortSpecified; } }