[ Index ]

PHP Cross Reference of phpBB-3.3.3-deutsch

title

Body

[close]

/vendor/guzzlehttp/psr7/src/ -> Uri.php (source)

   1  <?php
   2  
   3  namespace GuzzleHttp\Psr7;
   4  
   5  use Psr\Http\Message\UriInterface;
   6  
   7  /**
   8   * PSR-7 URI implementation.
   9   *
  10   * @author Michael Dowling
  11   * @author Tobias Schultze
  12   * @author Matthew Weier O'Phinney
  13   */
  14  class Uri implements UriInterface
  15  {
  16      /**
  17       * Absolute http and https URIs require a host per RFC 7230 Section 2.7
  18       * but in generic URIs the host can be empty. So for http(s) URIs
  19       * we apply this default host when no host is given yet to form a
  20       * valid URI.
  21       */
  22      const HTTP_DEFAULT_HOST = 'localhost';
  23  
  24      private static $defaultPorts = [
  25          'http'  => 80,
  26          'https' => 443,
  27          'ftp' => 21,
  28          'gopher' => 70,
  29          'nntp' => 119,
  30          'news' => 119,
  31          'telnet' => 23,
  32          'tn3270' => 23,
  33          'imap' => 143,
  34          'pop' => 110,
  35          'ldap' => 389,
  36      ];
  37  
  38      private static $charUnreserved = 'a-zA-Z0-9_\-\.~';
  39      private static $charSubDelims = '!\$&\'\(\)\*\+,;=';
  40      private static $replaceQuery = ['=' => '%3D', '&' => '%26'];
  41  
  42      /** @var string Uri scheme. */
  43      private $scheme = '';
  44  
  45      /** @var string Uri user info. */
  46      private $userInfo = '';
  47  
  48      /** @var string Uri host. */
  49      private $host = '';
  50  
  51      /** @var int|null Uri port. */
  52      private $port;
  53  
  54      /** @var string Uri path. */
  55      private $path = '';
  56  
  57      /** @var string Uri query string. */
  58      private $query = '';
  59  
  60      /** @var string Uri fragment. */
  61      private $fragment = '';
  62  
  63      /**
  64       * @param string $uri URI to parse
  65       */
  66      public function __construct($uri = '')
  67      {
  68          // weak type check to also accept null until we can add scalar type hints
  69          if ($uri != '') {
  70              $parts = parse_url($uri);
  71              if ($parts === false) {
  72                  throw new \InvalidArgumentException("Unable to parse URI: $uri");
  73              }
  74              $this->applyParts($parts);
  75          }
  76      }
  77  
  78      public function __toString()
  79      {
  80          return self::composeComponents(
  81              $this->scheme,
  82              $this->getAuthority(),
  83              $this->path,
  84              $this->query,
  85              $this->fragment
  86          );
  87      }
  88  
  89      /**
  90       * Composes a URI reference string from its various components.
  91       *
  92       * Usually this method does not need to be called manually but instead is used indirectly via
  93       * `Psr\Http\Message\UriInterface::__toString`.
  94       *
  95       * PSR-7 UriInterface treats an empty component the same as a missing component as
  96       * getQuery(), getFragment() etc. always return a string. This explains the slight
  97       * difference to RFC 3986 Section 5.3.
  98       *
  99       * Another adjustment is that the authority separator is added even when the authority is missing/empty
 100       * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
 101       * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
 102       * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
 103       * that format).
 104       *
 105       * @param string $scheme
 106       * @param string $authority
 107       * @param string $path
 108       * @param string $query
 109       * @param string $fragment
 110       *
 111       * @return string
 112       *
 113       * @link https://tools.ietf.org/html/rfc3986#section-5.3
 114       */
 115      public static function composeComponents($scheme, $authority, $path, $query, $fragment)
 116      {
 117          $uri = '';
 118  
 119          // weak type checks to also accept null until we can add scalar type hints
 120          if ($scheme != '') {
 121              $uri .= $scheme . ':';
 122          }
 123  
 124          if ($authority != ''|| $scheme === 'file') {
 125              $uri .= '//' . $authority;
 126          }
 127  
 128          $uri .= $path;
 129  
 130          if ($query != '') {
 131              $uri .= '?' . $query;
 132          }
 133  
 134          if ($fragment != '') {
 135              $uri .= '#' . $fragment;
 136          }
 137  
 138          return $uri;
 139      }
 140  
 141      /**
 142       * Whether the URI has the default port of the current scheme.
 143       *
 144       * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
 145       * independently of the implementation.
 146       *
 147       * @param UriInterface $uri
 148       *
 149       * @return bool
 150       */
 151      public static function isDefaultPort(UriInterface $uri)
 152      {
 153          return $uri->getPort() === null
 154              || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]);
 155      }
 156  
 157      /**
 158       * Whether the URI is absolute, i.e. it has a scheme.
 159       *
 160       * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
 161       * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
 162       * to another URI, the base URI. Relative references can be divided into several forms:
 163       * - network-path references, e.g. '//example.com/path'
 164       * - absolute-path references, e.g. '/path'
 165       * - relative-path references, e.g. 'subpath'
 166       *
 167       * @param UriInterface $uri
 168       *
 169       * @return bool
 170       * @see Uri::isNetworkPathReference
 171       * @see Uri::isAbsolutePathReference
 172       * @see Uri::isRelativePathReference
 173       * @link https://tools.ietf.org/html/rfc3986#section-4
 174       */
 175      public static function isAbsolute(UriInterface $uri)
 176      {
 177          return $uri->getScheme() !== '';
 178      }
 179  
 180      /**
 181       * Whether the URI is a network-path reference.
 182       *
 183       * A relative reference that begins with two slash characters is termed an network-path reference.
 184       *
 185       * @param UriInterface $uri
 186       *
 187       * @return bool
 188       * @link https://tools.ietf.org/html/rfc3986#section-4.2
 189       */
 190      public static function isNetworkPathReference(UriInterface $uri)
 191      {
 192          return $uri->getScheme() === '' && $uri->getAuthority() !== '';
 193      }
 194  
 195      /**
 196       * Whether the URI is a absolute-path reference.
 197       *
 198       * A relative reference that begins with a single slash character is termed an absolute-path reference.
 199       *
 200       * @param UriInterface $uri
 201       *
 202       * @return bool
 203       * @link https://tools.ietf.org/html/rfc3986#section-4.2
 204       */
 205      public static function isAbsolutePathReference(UriInterface $uri)
 206      {
 207          return $uri->getScheme() === ''
 208              && $uri->getAuthority() === ''
 209              && isset($uri->getPath()[0])
 210              && $uri->getPath()[0] === '/';
 211      }
 212  
 213      /**
 214       * Whether the URI is a relative-path reference.
 215       *
 216       * A relative reference that does not begin with a slash character is termed a relative-path reference.
 217       *
 218       * @param UriInterface $uri
 219       *
 220       * @return bool
 221       * @link https://tools.ietf.org/html/rfc3986#section-4.2
 222       */
 223      public static function isRelativePathReference(UriInterface $uri)
 224      {
 225          return $uri->getScheme() === ''
 226              && $uri->getAuthority() === ''
 227              && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
 228      }
 229  
 230      /**
 231       * Whether the URI is a same-document reference.
 232       *
 233       * A same-document reference refers to a URI that is, aside from its fragment
 234       * component, identical to the base URI. When no base URI is given, only an empty
 235       * URI reference (apart from its fragment) is considered a same-document reference.
 236       *
 237       * @param UriInterface      $uri  The URI to check
 238       * @param UriInterface|null $base An optional base URI to compare against
 239       *
 240       * @return bool
 241       * @link https://tools.ietf.org/html/rfc3986#section-4.4
 242       */
 243      public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null)
 244      {
 245          if ($base !== null) {
 246              $uri = UriResolver::resolve($base, $uri);
 247  
 248              return ($uri->getScheme() === $base->getScheme())
 249                  && ($uri->getAuthority() === $base->getAuthority())
 250                  && ($uri->getPath() === $base->getPath())
 251                  && ($uri->getQuery() === $base->getQuery());
 252          }
 253  
 254          return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
 255      }
 256  
 257      /**
 258       * Removes dot segments from a path and returns the new path.
 259       *
 260       * @param string $path
 261       *
 262       * @return string
 263       *
 264       * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead.
 265       * @see UriResolver::removeDotSegments
 266       */
 267      public static function removeDotSegments($path)
 268      {
 269          return UriResolver::removeDotSegments($path);
 270      }
 271  
 272      /**
 273       * Converts the relative URI into a new URI that is resolved against the base URI.
 274       *
 275       * @param UriInterface        $base Base URI
 276       * @param string|UriInterface $rel  Relative URI
 277       *
 278       * @return UriInterface
 279       *
 280       * @deprecated since version 1.4. Use UriResolver::resolve instead.
 281       * @see UriResolver::resolve
 282       */
 283      public static function resolve(UriInterface $base, $rel)
 284      {
 285          if (!($rel instanceof UriInterface)) {
 286              $rel = new self($rel);
 287          }
 288  
 289          return UriResolver::resolve($base, $rel);
 290      }
 291  
 292      /**
 293       * Creates a new URI with a specific query string value removed.
 294       *
 295       * Any existing query string values that exactly match the provided key are
 296       * removed.
 297       *
 298       * @param UriInterface $uri URI to use as a base.
 299       * @param string       $key Query string key to remove.
 300       *
 301       * @return UriInterface
 302       */
 303      public static function withoutQueryValue(UriInterface $uri, $key)
 304      {
 305          $result = self::getFilteredQueryString($uri, [$key]);
 306  
 307          return $uri->withQuery(implode('&', $result));
 308      }
 309  
 310      /**
 311       * Creates a new URI with a specific query string value.
 312       *
 313       * Any existing query string values that exactly match the provided key are
 314       * removed and replaced with the given key value pair.
 315       *
 316       * A value of null will set the query string key without a value, e.g. "key"
 317       * instead of "key=value".
 318       *
 319       * @param UriInterface $uri   URI to use as a base.
 320       * @param string       $key   Key to set.
 321       * @param string|null  $value Value to set
 322       *
 323       * @return UriInterface
 324       */
 325      public static function withQueryValue(UriInterface $uri, $key, $value)
 326      {
 327          $result = self::getFilteredQueryString($uri, [$key]);
 328  
 329          $result[] = self::generateQueryString($key, $value);
 330  
 331          return $uri->withQuery(implode('&', $result));
 332      }
 333  
 334      /**
 335       * Creates a new URI with multiple specific query string values.
 336       *
 337       * It has the same behavior as withQueryValue() but for an associative array of key => value.
 338       *
 339       * @param UriInterface $uri           URI to use as a base.
 340       * @param array        $keyValueArray Associative array of key and values
 341       *
 342       * @return UriInterface
 343       */
 344      public static function withQueryValues(UriInterface $uri, array $keyValueArray)
 345      {
 346          $result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
 347  
 348          foreach ($keyValueArray as $key => $value) {
 349              $result[] = self::generateQueryString($key, $value);
 350          }
 351  
 352          return $uri->withQuery(implode('&', $result));
 353      }
 354  
 355      /**
 356       * Creates a URI from a hash of `parse_url` components.
 357       *
 358       * @param array $parts
 359       *
 360       * @return UriInterface
 361       * @link http://php.net/manual/en/function.parse-url.php
 362       *
 363       * @throws \InvalidArgumentException If the components do not form a valid URI.
 364       */
 365      public static function fromParts(array $parts)
 366      {
 367          $uri = new self();
 368          $uri->applyParts($parts);
 369          $uri->validateState();
 370  
 371          return $uri;
 372      }
 373  
 374      public function getScheme()
 375      {
 376          return $this->scheme;
 377      }
 378  
 379      public function getAuthority()
 380      {
 381          $authority = $this->host;
 382          if ($this->userInfo !== '') {
 383              $authority = $this->userInfo . '@' . $authority;
 384          }
 385  
 386          if ($this->port !== null) {
 387              $authority .= ':' . $this->port;
 388          }
 389  
 390          return $authority;
 391      }
 392  
 393      public function getUserInfo()
 394      {
 395          return $this->userInfo;
 396      }
 397  
 398      public function getHost()
 399      {
 400          return $this->host;
 401      }
 402  
 403      public function getPort()
 404      {
 405          return $this->port;
 406      }
 407  
 408      public function getPath()
 409      {
 410          return $this->path;
 411      }
 412  
 413      public function getQuery()
 414      {
 415          return $this->query;
 416      }
 417  
 418      public function getFragment()
 419      {
 420          return $this->fragment;
 421      }
 422  
 423      public function withScheme($scheme)
 424      {
 425          $scheme = $this->filterScheme($scheme);
 426  
 427          if ($this->scheme === $scheme) {
 428              return $this;
 429          }
 430  
 431          $new = clone $this;
 432          $new->scheme = $scheme;
 433          $new->removeDefaultPort();
 434          $new->validateState();
 435  
 436          return $new;
 437      }
 438  
 439      public function withUserInfo($user, $password = null)
 440      {
 441          $info = $this->filterUserInfoComponent($user);
 442          if ($password !== null) {
 443              $info .= ':' . $this->filterUserInfoComponent($password);
 444          }
 445  
 446          if ($this->userInfo === $info) {
 447              return $this;
 448          }
 449  
 450          $new = clone $this;
 451          $new->userInfo = $info;
 452          $new->validateState();
 453  
 454          return $new;
 455      }
 456  
 457      public function withHost($host)
 458      {
 459          $host = $this->filterHost($host);
 460  
 461          if ($this->host === $host) {
 462              return $this;
 463          }
 464  
 465          $new = clone $this;
 466          $new->host = $host;
 467          $new->validateState();
 468  
 469          return $new;
 470      }
 471  
 472      public function withPort($port)
 473      {
 474          $port = $this->filterPort($port);
 475  
 476          if ($this->port === $port) {
 477              return $this;
 478          }
 479  
 480          $new = clone $this;
 481          $new->port = $port;
 482          $new->removeDefaultPort();
 483          $new->validateState();
 484  
 485          return $new;
 486      }
 487  
 488      public function withPath($path)
 489      {
 490          $path = $this->filterPath($path);
 491  
 492          if ($this->path === $path) {
 493              return $this;
 494          }
 495  
 496          $new = clone $this;
 497          $new->path = $path;
 498          $new->validateState();
 499  
 500          return $new;
 501      }
 502  
 503      public function withQuery($query)
 504      {
 505          $query = $this->filterQueryAndFragment($query);
 506  
 507          if ($this->query === $query) {
 508              return $this;
 509          }
 510  
 511          $new = clone $this;
 512          $new->query = $query;
 513  
 514          return $new;
 515      }
 516  
 517      public function withFragment($fragment)
 518      {
 519          $fragment = $this->filterQueryAndFragment($fragment);
 520  
 521          if ($this->fragment === $fragment) {
 522              return $this;
 523          }
 524  
 525          $new = clone $this;
 526          $new->fragment = $fragment;
 527  
 528          return $new;
 529      }
 530  
 531      /**
 532       * Apply parse_url parts to a URI.
 533       *
 534       * @param array $parts Array of parse_url parts to apply.
 535       */
 536      private function applyParts(array $parts)
 537      {
 538          $this->scheme = isset($parts['scheme'])
 539              ? $this->filterScheme($parts['scheme'])
 540              : '';
 541          $this->userInfo = isset($parts['user'])
 542              ? $this->filterUserInfoComponent($parts['user'])
 543              : '';
 544          $this->host = isset($parts['host'])
 545              ? $this->filterHost($parts['host'])
 546              : '';
 547          $this->port = isset($parts['port'])
 548              ? $this->filterPort($parts['port'])
 549              : null;
 550          $this->path = isset($parts['path'])
 551              ? $this->filterPath($parts['path'])
 552              : '';
 553          $this->query = isset($parts['query'])
 554              ? $this->filterQueryAndFragment($parts['query'])
 555              : '';
 556          $this->fragment = isset($parts['fragment'])
 557              ? $this->filterQueryAndFragment($parts['fragment'])
 558              : '';
 559          if (isset($parts['pass'])) {
 560              $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']);
 561          }
 562  
 563          $this->removeDefaultPort();
 564      }
 565  
 566      /**
 567       * @param string $scheme
 568       *
 569       * @return string
 570       *
 571       * @throws \InvalidArgumentException If the scheme is invalid.
 572       */
 573      private function filterScheme($scheme)
 574      {
 575          if (!is_string($scheme)) {
 576              throw new \InvalidArgumentException('Scheme must be a string');
 577          }
 578  
 579          return strtolower($scheme);
 580      }
 581  
 582      /**
 583       * @param string $component
 584       *
 585       * @return string
 586       *
 587       * @throws \InvalidArgumentException If the user info is invalid.
 588       */
 589      private function filterUserInfoComponent($component)
 590      {
 591          if (!is_string($component)) {
 592              throw new \InvalidArgumentException('User info must be a string');
 593          }
 594  
 595          return preg_replace_callback(
 596              '/(?:[^%' . self::$charUnreserved . self::$charSubDelims . ']+|%(?![A-Fa-f0-9]{2}))/',
 597              [$this, 'rawurlencodeMatchZero'],
 598              $component
 599          );
 600      }
 601  
 602      /**
 603       * @param string $host
 604       *
 605       * @return string
 606       *
 607       * @throws \InvalidArgumentException If the host is invalid.
 608       */
 609      private function filterHost($host)
 610      {
 611          if (!is_string($host)) {
 612              throw new \InvalidArgumentException('Host must be a string');
 613          }
 614  
 615          return strtolower($host);
 616      }
 617  
 618      /**
 619       * @param int|null $port
 620       *
 621       * @return int|null
 622       *
 623       * @throws \InvalidArgumentException If the port is invalid.
 624       */
 625      private function filterPort($port)
 626      {
 627          if ($port === null) {
 628              return null;
 629          }
 630  
 631          $port = (int) $port;
 632          if (0 > $port || 0xffff < $port) {
 633              throw new \InvalidArgumentException(
 634                  sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
 635              );
 636          }
 637  
 638          return $port;
 639      }
 640  
 641      /**
 642       * @param UriInterface $uri
 643       * @param array        $keys
 644       * 
 645       * @return array
 646       */
 647      private static function getFilteredQueryString(UriInterface $uri, array $keys)
 648      {
 649          $current = $uri->getQuery();
 650  
 651          if ($current === '') {
 652              return [];
 653          }
 654  
 655          $decodedKeys = array_map('rawurldecode', $keys);
 656  
 657          return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
 658              return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
 659          });
 660      }
 661  
 662      /**
 663       * @param string      $key
 664       * @param string|null $value
 665       * 
 666       * @return string
 667       */
 668      private static function generateQueryString($key, $value)
 669      {
 670          // Query string separators ("=", "&") within the key or value need to be encoded
 671          // (while preventing double-encoding) before setting the query string. All other
 672          // chars that need percent-encoding will be encoded by withQuery().
 673          $queryString = strtr($key, self::$replaceQuery);
 674  
 675          if ($value !== null) {
 676              $queryString .= '=' . strtr($value, self::$replaceQuery);
 677          }
 678  
 679          return $queryString;
 680      }
 681  
 682      private function removeDefaultPort()
 683      {
 684          if ($this->port !== null && self::isDefaultPort($this)) {
 685              $this->port = null;
 686          }
 687      }
 688  
 689      /**
 690       * Filters the path of a URI
 691       *
 692       * @param string $path
 693       *
 694       * @return string
 695       *
 696       * @throws \InvalidArgumentException If the path is invalid.
 697       */
 698      private function filterPath($path)
 699      {
 700          if (!is_string($path)) {
 701              throw new \InvalidArgumentException('Path must be a string');
 702          }
 703  
 704          return preg_replace_callback(
 705              '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
 706              [$this, 'rawurlencodeMatchZero'],
 707              $path
 708          );
 709      }
 710  
 711      /**
 712       * Filters the query string or fragment of a URI.
 713       *
 714       * @param string $str
 715       *
 716       * @return string
 717       *
 718       * @throws \InvalidArgumentException If the query or fragment is invalid.
 719       */
 720      private function filterQueryAndFragment($str)
 721      {
 722          if (!is_string($str)) {
 723              throw new \InvalidArgumentException('Query and fragment must be a string');
 724          }
 725  
 726          return preg_replace_callback(
 727              '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
 728              [$this, 'rawurlencodeMatchZero'],
 729              $str
 730          );
 731      }
 732  
 733      private function rawurlencodeMatchZero(array $match)
 734      {
 735          return rawurlencode($match[0]);
 736      }
 737  
 738      private function validateState()
 739      {
 740          if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
 741              $this->host = self::HTTP_DEFAULT_HOST;
 742          }
 743  
 744          if ($this->getAuthority() === '') {
 745              if (0 === strpos($this->path, '//')) {
 746                  throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"');
 747              }
 748              if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
 749                  throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon');
 750              }
 751          } elseif (isset($this->path[0]) && $this->path[0] !== '/') {
 752              @trigger_error(
 753                  'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' .
 754                  'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.',
 755                  E_USER_DEPRECATED
 756              );
 757              $this->path = '/'. $this->path;
 758              //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty');
 759          }
 760      }
 761  }


Generated: Sun Feb 14 20:08:31 2021 Cross-referenced by PHPXref 0.7.1