You are here

class Uri in Zircon Profile 8.0

Same name in this branch
  1. 8.0 vendor/zendframework/zend-diactoros/src/Uri.php \Zend\Diactoros\Uri
  2. 8.0 vendor/zendframework/zend-feed/src/Uri.php \Zend\Feed\Uri
  3. 8.0 vendor/guzzlehttp/psr7/src/Uri.php \GuzzleHttp\Psr7\Uri
  4. 8.0 core/lib/Drupal/Core/TypedData/Plugin/DataType/Uri.php \Drupal\Core\TypedData\Plugin\DataType\Uri
Same name and namespace in other branches
  1. 8 vendor/zendframework/zend-diactoros/src/Uri.php \Zend\Diactoros\Uri

Implementation of Psr\Http\UriInterface.

Provides a value object representing a URI for HTTP requests.

Instances of this class are considered immutable; all methods that might change state are implemented such that they retain the internal state of the current instance and return a new instance that contains the changed state.

Hierarchy

Expanded class hierarchy of Uri

2 files declare their use of Uri
Serializer.php in vendor/zendframework/zend-diactoros/src/Request/Serializer.php
WebTestBase.php in core/modules/simpletest/src/WebTestBase.php
Contains \Drupal\simpletest\WebTestBase.
1 string reference to 'Uri'
core.data_types.schema.yml in core/config/schema/core.data_types.schema.yml
core/config/schema/core.data_types.schema.yml

File

vendor/zendframework/zend-diactoros/src/Uri.php, line 25

Namespace

Zend\Diactoros
View source
class Uri implements UriInterface {

  /**
   * Sub-delimiters used in query strings and fragments.
   *
   * @const string
   */
  const CHAR_SUB_DELIMS = '!\\$&\'\\(\\)\\*\\+,;=';

  /**
   * Unreserved characters used in paths, query strings, and fragments.
   *
   * @const string
   */
  const CHAR_UNRESERVED = 'a-zA-Z0-9_\\-\\.~';

  /**
   * @var int[] Array indexed by valid scheme names to their corresponding ports.
   */
  protected $allowedSchemes = [
    'http' => 80,
    'https' => 443,
  ];

  /**
   * @var string
   */
  private $scheme = '';

  /**
   * @var string
   */
  private $userInfo = '';

  /**
   * @var string
   */
  private $host = '';

  /**
   * @var int
   */
  private $port;

  /**
   * @var string
   */
  private $path = '';

  /**
   * @var string
   */
  private $query = '';

  /**
   * @var string
   */
  private $fragment = '';

  /**
   * generated uri string cache
   * @var string|null
   */
  private $uriString;

  /**
   * @param string $uri
   * @throws InvalidArgumentException on non-string $uri argument
   */
  public function __construct($uri = '') {
    if (!is_string($uri)) {
      throw new InvalidArgumentException(sprintf('URI passed to constructor must be a string; received "%s"', is_object($uri) ? get_class($uri) : gettype($uri)));
    }
    if (!empty($uri)) {
      $this
        ->parseUri($uri);
    }
  }

  /**
   * Operations to perform on clone.
   *
   * Since cloning usually is for purposes of mutation, we reset the
   * $uriString property so it will be re-calculated.
   */
  public function __clone() {
    $this->uriString = null;
  }

  /**
   * {@inheritdoc}
   */
  public function __toString() {
    if (null !== $this->uriString) {
      return $this->uriString;
    }
    $this->uriString = static::createUriString($this->scheme, $this
      ->getAuthority(), $this
      ->getPath(), $this->query, $this->fragment);
    return $this->uriString;
  }

  /**
   * {@inheritdoc}
   */
  public function getScheme() {
    return $this->scheme;
  }

  /**
   * {@inheritdoc}
   */
  public function getAuthority() {
    if (empty($this->host)) {
      return '';
    }
    $authority = $this->host;
    if (!empty($this->userInfo)) {
      $authority = $this->userInfo . '@' . $authority;
    }
    if ($this
      ->isNonStandardPort($this->scheme, $this->host, $this->port)) {
      $authority .= ':' . $this->port;
    }
    return $authority;
  }

  /**
   * {@inheritdoc}
   */
  public function getUserInfo() {
    return $this->userInfo;
  }

  /**
   * {@inheritdoc}
   */
  public function getHost() {
    return $this->host;
  }

  /**
   * {@inheritdoc}
   */
  public function getPort() {
    return $this
      ->isNonStandardPort($this->scheme, $this->host, $this->port) ? $this->port : null;
  }

  /**
   * {@inheritdoc}
   */
  public function getPath() {
    return $this->path;
  }

  /**
   * {@inheritdoc}
   */
  public function getQuery() {
    return $this->query;
  }

  /**
   * {@inheritdoc}
   */
  public function getFragment() {
    return $this->fragment;
  }

  /**
   * {@inheritdoc}
   */
  public function withScheme($scheme) {
    if (!is_string($scheme)) {
      throw new InvalidArgumentException(sprintf('%s expects a string argument; received %s', __METHOD__, is_object($scheme) ? get_class($scheme) : gettype($scheme)));
    }
    $scheme = $this
      ->filterScheme($scheme);
    if ($scheme === $this->scheme) {

      // Do nothing if no change was made.
      return clone $this;
    }
    $new = clone $this;
    $new->scheme = $scheme;
    return $new;
  }

  /**
   * {@inheritdoc}
   */
  public function withUserInfo($user, $password = null) {
    if (!is_string($user)) {
      throw new InvalidArgumentException(sprintf('%s expects a string user argument; received %s', __METHOD__, is_object($user) ? get_class($user) : gettype($user)));
    }
    if (null !== $password && !is_string($password)) {
      throw new InvalidArgumentException(sprintf('%s expects a string password argument; received %s', __METHOD__, is_object($password) ? get_class($password) : gettype($password)));
    }
    $info = $user;
    if ($password) {
      $info .= ':' . $password;
    }
    if ($info === $this->userInfo) {

      // Do nothing if no change was made.
      return clone $this;
    }
    $new = clone $this;
    $new->userInfo = $info;
    return $new;
  }

  /**
   * {@inheritdoc}
   */
  public function withHost($host) {
    if (!is_string($host)) {
      throw new InvalidArgumentException(sprintf('%s expects a string argument; received %s', __METHOD__, is_object($host) ? get_class($host) : gettype($host)));
    }
    if ($host === $this->host) {

      // Do nothing if no change was made.
      return clone $this;
    }
    $new = clone $this;
    $new->host = $host;
    return $new;
  }

  /**
   * {@inheritdoc}
   */
  public function withPort($port) {
    if (!is_numeric($port)) {
      throw new InvalidArgumentException(sprintf('Invalid port "%s" specified; must be an integer or integer string', is_object($port) ? get_class($port) : gettype($port)));
    }
    $port = (int) $port;
    if ($port === $this->port) {

      // Do nothing if no change was made.
      return clone $this;
    }
    if ($port < 1 || $port > 65535) {
      throw new InvalidArgumentException(sprintf('Invalid port "%d" specified; must be a valid TCP/UDP port', $port));
    }
    $new = clone $this;
    $new->port = $port;
    return $new;
  }

  /**
   * {@inheritdoc}
   */
  public function withPath($path) {
    if (!is_string($path)) {
      throw new InvalidArgumentException('Invalid path provided; must be a string');
    }
    if (strpos($path, '?') !== false) {
      throw new InvalidArgumentException('Invalid path provided; must not contain a query string');
    }
    if (strpos($path, '#') !== false) {
      throw new InvalidArgumentException('Invalid path provided; must not contain a URI fragment');
    }
    $path = $this
      ->filterPath($path);
    if ($path === $this->path) {

      // Do nothing if no change was made.
      return clone $this;
    }
    $new = clone $this;
    $new->path = $path;
    return $new;
  }

  /**
   * {@inheritdoc}
   */
  public function withQuery($query) {
    if (!is_string($query)) {
      throw new InvalidArgumentException('Query string must be a string');
    }
    if (strpos($query, '#') !== false) {
      throw new InvalidArgumentException('Query string must not include a URI fragment');
    }
    $query = $this
      ->filterQuery($query);
    if ($query === $this->query) {

      // Do nothing if no change was made.
      return clone $this;
    }
    $new = clone $this;
    $new->query = $query;
    return $new;
  }

  /**
   * {@inheritdoc}
   */
  public function withFragment($fragment) {
    if (!is_string($fragment)) {
      throw new InvalidArgumentException(sprintf('%s expects a string argument; received %s', __METHOD__, is_object($fragment) ? get_class($fragment) : gettype($fragment)));
    }
    $fragment = $this
      ->filterFragment($fragment);
    if ($fragment === $this->fragment) {

      // Do nothing if no change was made.
      return clone $this;
    }
    $new = clone $this;
    $new->fragment = $fragment;
    return $new;
  }

  /**
   * Parse a URI into its parts, and set the properties
   *
   * @param string $uri
   */
  private function parseUri($uri) {
    $parts = parse_url($uri);
    if (false === $parts) {
      throw new \InvalidArgumentException('The source URI string appears to be malformed');
    }
    $this->scheme = isset($parts['scheme']) ? $this
      ->filterScheme($parts['scheme']) : '';
    $this->userInfo = isset($parts['user']) ? $parts['user'] : '';
    $this->host = isset($parts['host']) ? $parts['host'] : '';
    $this->port = isset($parts['port']) ? $parts['port'] : null;
    $this->path = isset($parts['path']) ? $this
      ->filterPath($parts['path']) : '';
    $this->query = isset($parts['query']) ? $this
      ->filterQuery($parts['query']) : '';
    $this->fragment = isset($parts['fragment']) ? $this
      ->filterFragment($parts['fragment']) : '';
    if (isset($parts['pass'])) {
      $this->userInfo .= ':' . $parts['pass'];
    }
  }

  /**
   * Create a URI string from its various parts
   *
   * @param string $scheme
   * @param string $authority
   * @param string $path
   * @param string $query
   * @param string $fragment
   * @return string
   */
  private static function createUriString($scheme, $authority, $path, $query, $fragment) {
    $uri = '';
    if (!empty($scheme)) {
      $uri .= sprintf('%s://', $scheme);
    }
    if (!empty($authority)) {
      $uri .= $authority;
    }
    if ($path) {
      if (empty($path) || '/' !== substr($path, 0, 1)) {
        $path = '/' . $path;
      }
      $uri .= $path;
    }
    if ($query) {
      $uri .= sprintf('?%s', $query);
    }
    if ($fragment) {
      $uri .= sprintf('#%s', $fragment);
    }
    return $uri;
  }

  /**
   * Is a given port non-standard for the current scheme?
   *
   * @param string $scheme
   * @param string $host
   * @param int $port
   * @return bool
   */
  private function isNonStandardPort($scheme, $host, $port) {
    if (!$scheme) {
      return true;
    }
    if (!$host || !$port) {
      return false;
    }
    return !isset($this->allowedSchemes[$scheme]) || $port !== $this->allowedSchemes[$scheme];
  }

  /**
   * Filters the scheme to ensure it is a valid scheme.
   *
   * @param string $scheme Scheme name.
   *
   * @return string Filtered scheme.
   */
  private function filterScheme($scheme) {
    $scheme = strtolower($scheme);
    $scheme = preg_replace('#:(//)?$#', '', $scheme);
    if (empty($scheme)) {
      return '';
    }
    if (!array_key_exists($scheme, $this->allowedSchemes)) {
      throw new InvalidArgumentException(sprintf('Unsupported scheme "%s"; must be any empty string or in the set (%s)', $scheme, implode(', ', array_keys($this->allowedSchemes))));
    }
    return $scheme;
  }

  /**
   * Filters the path of a URI to ensure it is properly encoded.
   *
   * @param string $path
   * @return string
   */
  private function filterPath($path) {
    $path = preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . ':@&=\\+\\$,\\/;%]+|%(?![A-Fa-f0-9]{2}))/', [
      $this,
      'urlEncodeChar',
    ], $path);
    if (empty($path)) {

      // No path
      return $path;
    }
    if ($path[0] !== '/') {

      // Relative path
      return $path;
    }

    // Ensure only one leading slash, to prevent XSS attempts.
    return '/' . ltrim($path, '/');
  }

  /**
   * Filter a query string to ensure it is propertly encoded.
   *
   * Ensures that the values in the query string are properly urlencoded.
   *
   * @param string $query
   * @return string
   */
  private function filterQuery($query) {
    if (!empty($query) && strpos($query, '?') === 0) {
      $query = substr($query, 1);
    }
    $parts = explode('&', $query);
    foreach ($parts as $index => $part) {
      list($key, $value) = $this
        ->splitQueryValue($part);
      if ($value === null) {
        $parts[$index] = $this
          ->filterQueryOrFragment($key);
        continue;
      }
      $parts[$index] = sprintf('%s=%s', $this
        ->filterQueryOrFragment($key), $this
        ->filterQueryOrFragment($value));
    }
    return implode('&', $parts);
  }

  /**
   * Split a query value into a key/value tuple.
   *
   * @param string $value
   * @return array A value with exactly two elements, key and value
   */
  private function splitQueryValue($value) {
    $data = explode('=', $value, 2);
    if (1 === count($data)) {
      $data[] = null;
    }
    return $data;
  }

  /**
   * Filter a fragment value to ensure it is properly encoded.
   *
   * @param null|string $fragment
   * @return string
   */
  private function filterFragment($fragment) {
    if (!empty($fragment) && strpos($fragment, '#') === 0) {
      $fragment = substr($fragment, 1);
    }
    return $this
      ->filterQueryOrFragment($fragment);
  }

  /**
   * Filter a query string key or value, or a fragment.
   *
   * @param string $value
   * @return string
   */
  private function filterQueryOrFragment($value) {
    return preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\\/\\?]+|%(?![A-Fa-f0-9]{2}))/', [
      $this,
      'urlEncodeChar',
    ], $value);
  }

  /**
   * URL encode a character returned by a regex.
   *
   * @param array $matches
   * @return string
   */
  private function urlEncodeChar(array $matches) {
    return rawurlencode($matches[0]);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Uri::$allowedSchemes protected property
Uri::$fragment private property
Uri::$host private property
Uri::$path private property
Uri::$port private property
Uri::$query private property
Uri::$scheme private property
Uri::$uriString private property generated uri string cache
Uri::$userInfo private property
Uri::CHAR_SUB_DELIMS constant Sub-delimiters used in query strings and fragments.
Uri::CHAR_UNRESERVED constant Unreserved characters used in paths, query strings, and fragments.
Uri::createUriString private static function Create a URI string from its various parts
Uri::filterFragment private function Filter a fragment value to ensure it is properly encoded.
Uri::filterPath private function Filters the path of a URI to ensure it is properly encoded.
Uri::filterQuery private function Filter a query string to ensure it is propertly encoded.
Uri::filterQueryOrFragment private function Filter a query string key or value, or a fragment.
Uri::filterScheme private function Filters the scheme to ensure it is a valid scheme.
Uri::getAuthority public function Retrieve the authority component of the URI. Overrides UriInterface::getAuthority
Uri::getFragment public function Retrieve the fragment component of the URI. Overrides UriInterface::getFragment
Uri::getHost public function Retrieve the host component of the URI. Overrides UriInterface::getHost
Uri::getPath public function Retrieve the path component of the URI. Overrides UriInterface::getPath
Uri::getPort public function Retrieve the port component of the URI. Overrides UriInterface::getPort
Uri::getQuery public function Retrieve the query string of the URI. Overrides UriInterface::getQuery
Uri::getScheme public function Retrieve the scheme component of the URI. Overrides UriInterface::getScheme
Uri::getUserInfo public function Retrieve the user information component of the URI. Overrides UriInterface::getUserInfo
Uri::isNonStandardPort private function Is a given port non-standard for the current scheme?
Uri::parseUri private function Parse a URI into its parts, and set the properties
Uri::splitQueryValue private function Split a query value into a key/value tuple.
Uri::urlEncodeChar private function URL encode a character returned by a regex.
Uri::withFragment public function Return an instance with the specified URI fragment. Overrides UriInterface::withFragment
Uri::withHost public function Return an instance with the specified host. Overrides UriInterface::withHost
Uri::withPath public function Return an instance with the specified path. Overrides UriInterface::withPath
Uri::withPort public function Return an instance with the specified port. Overrides UriInterface::withPort
Uri::withQuery public function Return an instance with the specified query string. Overrides UriInterface::withQuery
Uri::withScheme public function Return an instance with the specified scheme. Overrides UriInterface::withScheme
Uri::withUserInfo public function Return an instance with the specified user information. Overrides UriInterface::withUserInfo
Uri::__clone public function Operations to perform on clone.
Uri::__construct public function
Uri::__toString public function Return the string representation as a URI reference. Overrides UriInterface::__toString