Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Uri.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 22.36 KiB


001  <?php
002   
003  namespace GuzzleHttp\Psr7;
004   
005  use Psr\Http\Message\UriInterface;
006   
007  /**
008   * PSR-7 URI implementation.
009   *
010   * @author Michael Dowling
011   * @author Tobias Schultze
012   * @author Matthew Weier O'Phinney
013   */
014  class Uri implements UriInterface
015  {
016      /**
017       * Absolute http and https URIs require a host per RFC 7230 Section 2.7
018       * but in generic URIs the host can be empty. So for http(s) URIs
019       * we apply this default host when no host is given yet to form a
020       * valid URI.
021       */
022      const HTTP_DEFAULT_HOST = 'localhost';
023   
024      private static $defaultPorts = [
025          'http'  => 80,
026          'https' => 443,
027          'ftp' => 21,
028          'gopher' => 70,
029          'nntp' => 119,
030          'news' => 119,
031          'telnet' => 23,
032          'tn3270' => 23,
033          'imap' => 143,
034          'pop' => 110,
035          'ldap' => 389,
036      ];
037   
038      private static $charUnreserved = 'a-zA-Z0-9_\-\.~';
039      private static $charSubDelims = '!\$&\'\(\)\*\+,;=';
040      private static $replaceQuery = ['=' => '%3D', '&' => '%26'];
041   
042      /** @var string Uri scheme. */
043      private $scheme = '';
044   
045      /** @var string Uri user info. */
046      private $userInfo = '';
047   
048      /** @var string Uri host. */
049      private $host = '';
050   
051      /** @var int|null Uri port. */
052      private $port;
053   
054      /** @var string Uri path. */
055      private $path = '';
056   
057      /** @var string Uri query string. */
058      private $query = '';
059   
060      /** @var string Uri fragment. */
061      private $fragment = '';
062   
063      /**
064       * @param string $uri URI to parse
065       */
066      public function __construct($uri = '')
067      {
068          // weak type check to also accept null until we can add scalar type hints
069          if ($uri != '') {
070              $parts = self::parse($uri);
071              if ($parts === false) {
072                  throw new \InvalidArgumentException("Unable to parse URI: $uri");
073              }
074              $this->applyParts($parts);
075          }
076      }
077   
078      /**
079       * UTF-8 aware \parse_url() replacement.
080       *
081       * The internal function produces broken output for non ASCII domain names
082       * (IDN) when used with locales other than "C".
083       *
084       * On the other hand, cURL understands IDN correctly only when UTF-8 locale
085       * is configured ("C.UTF-8", "en_US.UTF-8", etc.).
086       *
087       * @see https://bugs.php.net/bug.php?id=52923
088       * @see https://www.php.net/manual/en/function.parse-url.php#114817
089       * @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING
090       *
091       * @param string $url
092       *
093       * @return array|false
094       */
095      private static function parse($url)
096      {
097          // If IPv6
098          $prefix = '';
099          if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) {
100              $prefix = $matches[1];
101              $url = $matches[2];
102          }
103   
104          $encodedUrl = preg_replace_callback(
105              '%[^:/@?&=#]+%usD',
106              static function ($matches) {
107                  return urlencode($matches[0]);
108              },
109              $url
110          );
111   
112          $result = parse_url($prefix . $encodedUrl);
113   
114          if ($result === false) {
115              return false;
116          }
117   
118          return array_map('urldecode', $result);
119      }
120   
121      public function __toString()
122      {
123          return self::composeComponents(
124              $this->scheme,
125              $this->getAuthority(),
126              $this->path,
127              $this->query,
128              $this->fragment
129          );
130      }
131   
132      /**
133       * Composes a URI reference string from its various components.
134       *
135       * Usually this method does not need to be called manually but instead is used indirectly via
136       * `Psr\Http\Message\UriInterface::__toString`.
137       *
138       * PSR-7 UriInterface treats an empty component the same as a missing component as
139       * getQuery(), getFragment() etc. always return a string. This explains the slight
140       * difference to RFC 3986 Section 5.3.
141       *
142       * Another adjustment is that the authority separator is added even when the authority is missing/empty
143       * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
144       * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
145       * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
146       * that format).
147       *
148       * @param string $scheme
149       * @param string $authority
150       * @param string $path
151       * @param string $query
152       * @param string $fragment
153       *
154       * @return string
155       *
156       * @link https://tools.ietf.org/html/rfc3986#section-5.3
157       */
158      public static function composeComponents($scheme, $authority, $path, $query, $fragment)
159      {
160          $uri = '';
161   
162          // weak type checks to also accept null until we can add scalar type hints
163          if ($scheme != '') {
164              $uri .= $scheme . ':';
165          }
166   
167          if ($authority != ''|| $scheme === 'file') {
168              $uri .= '//' . $authority;
169          }
170   
171          $uri .= $path;
172   
173          if ($query != '') {
174              $uri .= '?' . $query;
175          }
176   
177          if ($fragment != '') {
178              $uri .= '#' . $fragment;
179          }
180   
181          return $uri;
182      }
183   
184      /**
185       * Whether the URI has the default port of the current scheme.
186       *
187       * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
188       * independently of the implementation.
189       *
190       * @param UriInterface $uri
191       *
192       * @return bool
193       */
194      public static function isDefaultPort(UriInterface $uri)
195      {
196          return $uri->getPort() === null
197              || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]);
198      }
199   
200      /**
201       * Whether the URI is absolute, i.e. it has a scheme.
202       *
203       * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
204       * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
205       * to another URI, the base URI. Relative references can be divided into several forms:
206       * - network-path references, e.g. '//example.com/path'
207       * - absolute-path references, e.g. '/path'
208       * - relative-path references, e.g. 'subpath'
209       *
210       * @param UriInterface $uri
211       *
212       * @return bool
213       *
214       * @see Uri::isNetworkPathReference
215       * @see Uri::isAbsolutePathReference
216       * @see Uri::isRelativePathReference
217       * @link https://tools.ietf.org/html/rfc3986#section-4
218       */
219      public static function isAbsolute(UriInterface $uri)
220      {
221          return $uri->getScheme() !== '';
222      }
223   
224      /**
225       * Whether the URI is a network-path reference.
226       *
227       * A relative reference that begins with two slash characters is termed an network-path reference.
228       *
229       * @param UriInterface $uri
230       *
231       * @return bool
232       *
233       * @link https://tools.ietf.org/html/rfc3986#section-4.2
234       */
235      public static function isNetworkPathReference(UriInterface $uri)
236      {
237          return $uri->getScheme() === '' && $uri->getAuthority() !== '';
238      }
239   
240      /**
241       * Whether the URI is a absolute-path reference.
242       *
243       * A relative reference that begins with a single slash character is termed an absolute-path reference.
244       *
245       * @param UriInterface $uri
246       *
247       * @return bool
248       *
249       * @link https://tools.ietf.org/html/rfc3986#section-4.2
250       */
251      public static function isAbsolutePathReference(UriInterface $uri)
252      {
253          return $uri->getScheme() === ''
254              && $uri->getAuthority() === ''
255              && isset($uri->getPath()[0])
256              && $uri->getPath()[0] === '/';
257      }
258   
259      /**
260       * Whether the URI is a relative-path reference.
261       *
262       * A relative reference that does not begin with a slash character is termed a relative-path reference.
263       *
264       * @param UriInterface $uri
265       *
266       * @return bool
267       *
268       * @link https://tools.ietf.org/html/rfc3986#section-4.2
269       */
270      public static function isRelativePathReference(UriInterface $uri)
271      {
272          return $uri->getScheme() === ''
273              && $uri->getAuthority() === ''
274              && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
275      }
276   
277      /**
278       * Whether the URI is a same-document reference.
279       *
280       * A same-document reference refers to a URI that is, aside from its fragment
281       * component, identical to the base URI. When no base URI is given, only an empty
282       * URI reference (apart from its fragment) is considered a same-document reference.
283       *
284       * @param UriInterface      $uri  The URI to check
285       * @param UriInterface|null $base An optional base URI to compare against
286       *
287       * @return bool
288       *
289       * @link https://tools.ietf.org/html/rfc3986#section-4.4
290       */
291      public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null)
292      {
293          if ($base !== null) {
294              $uri = UriResolver::resolve($base, $uri);
295   
296              return ($uri->getScheme() === $base->getScheme())
297                  && ($uri->getAuthority() === $base->getAuthority())
298                  && ($uri->getPath() === $base->getPath())
299                  && ($uri->getQuery() === $base->getQuery());
300          }
301   
302          return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
303      }
304   
305      /**
306       * Removes dot segments from a path and returns the new path.
307       *
308       * @param string $path
309       *
310       * @return string
311       *
312       * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead.
313       * @see UriResolver::removeDotSegments
314       */
315      public static function removeDotSegments($path)
316      {
317          return UriResolver::removeDotSegments($path);
318      }
319   
320      /**
321       * Converts the relative URI into a new URI that is resolved against the base URI.
322       *
323       * @param UriInterface        $base Base URI
324       * @param string|UriInterface $rel  Relative URI
325       *
326       * @return UriInterface
327       *
328       * @deprecated since version 1.4. Use UriResolver::resolve instead.
329       * @see UriResolver::resolve
330       */
331      public static function resolve(UriInterface $base, $rel)
332      {
333          if (!($rel instanceof UriInterface)) {
334              $rel = new self($rel);
335          }
336   
337          return UriResolver::resolve($base, $rel);
338      }
339   
340      /**
341       * Creates a new URI with a specific query string value removed.
342       *
343       * Any existing query string values that exactly match the provided key are
344       * removed.
345       *
346       * @param UriInterface $uri URI to use as a base.
347       * @param string       $key Query string key to remove.
348       *
349       * @return UriInterface
350       */
351      public static function withoutQueryValue(UriInterface $uri, $key)
352      {
353          $result = self::getFilteredQueryString($uri, [$key]);
354   
355          return $uri->withQuery(implode('&', $result));
356      }
357   
358      /**
359       * Creates a new URI with a specific query string value.
360       *
361       * Any existing query string values that exactly match the provided key are
362       * removed and replaced with the given key value pair.
363       *
364       * A value of null will set the query string key without a value, e.g. "key"
365       * instead of "key=value".
366       *
367       * @param UriInterface $uri   URI to use as a base.
368       * @param string       $key   Key to set.
369       * @param string|null  $value Value to set
370       *
371       * @return UriInterface
372       */
373      public static function withQueryValue(UriInterface $uri, $key, $value)
374      {
375          $result = self::getFilteredQueryString($uri, [$key]);
376   
377          $result[] = self::generateQueryString($key, $value);
378   
379          return $uri->withQuery(implode('&', $result));
380      }
381   
382      /**
383       * Creates a new URI with multiple specific query string values.
384       *
385       * It has the same behavior as withQueryValue() but for an associative array of key => value.
386       *
387       * @param UriInterface $uri           URI to use as a base.
388       * @param array        $keyValueArray Associative array of key and values
389       *
390       * @return UriInterface
391       */
392      public static function withQueryValues(UriInterface $uri, array $keyValueArray)
393      {
394          $result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
395   
396          foreach ($keyValueArray as $key => $value) {
397              $result[] = self::generateQueryString($key, $value);
398          }
399   
400          return $uri->withQuery(implode('&', $result));
401      }
402   
403      /**
404       * Creates a URI from a hash of `parse_url` components.
405       *
406       * @param array $parts
407       *
408       * @return UriInterface
409       *
410       * @link http://php.net/manual/en/function.parse-url.php
411       *
412       * @throws \InvalidArgumentException If the components do not form a valid URI.
413       */
414      public static function fromParts(array $parts)
415      {
416          $uri = new self();
417          $uri->applyParts($parts);
418          $uri->validateState();
419   
420          return $uri;
421      }
422   
423      public function getScheme()
424      {
425          return $this->scheme;
426      }
427   
428      public function getAuthority()
429      {
430          $authority = $this->host;
431          if ($this->userInfo !== '') {
432              $authority = $this->userInfo . '@' . $authority;
433          }
434   
435          if ($this->port !== null) {
436              $authority .= ':' . $this->port;
437          }
438   
439          return $authority;
440      }
441   
442      public function getUserInfo()
443      {
444          return $this->userInfo;
445      }
446   
447      public function getHost()
448      {
449          return $this->host;
450      }
451   
452      public function getPort()
453      {
454          return $this->port;
455      }
456   
457      public function getPath()
458      {
459          return $this->path;
460      }
461   
462      public function getQuery()
463      {
464          return $this->query;
465      }
466   
467      public function getFragment()
468      {
469          return $this->fragment;
470      }
471   
472      public function withScheme($scheme)
473      {
474          $scheme = $this->filterScheme($scheme);
475   
476          if ($this->scheme === $scheme) {
477              return $this;
478          }
479   
480          $new = clone $this;
481          $new->scheme = $scheme;
482          $new->removeDefaultPort();
483          $new->validateState();
484   
485          return $new;
486      }
487   
488      public function withUserInfo($user, $password = null)
489      {
490          $info = $this->filterUserInfoComponent($user);
491          if ($password !== null) {
492              $info .= ':' . $this->filterUserInfoComponent($password);
493          }
494   
495          if ($this->userInfo === $info) {
496              return $this;
497          }
498   
499          $new = clone $this;
500          $new->userInfo = $info;
501          $new->validateState();
502   
503          return $new;
504      }
505   
506      public function withHost($host)
507      {
508          $host = $this->filterHost($host);
509   
510          if ($this->host === $host) {
511              return $this;
512          }
513   
514          $new = clone $this;
515          $new->host = $host;
516          $new->validateState();
517   
518          return $new;
519      }
520   
521      public function withPort($port)
522      {
523          $port = $this->filterPort($port);
524   
525          if ($this->port === $port) {
526              return $this;
527          }
528   
529          $new = clone $this;
530          $new->port = $port;
531          $new->removeDefaultPort();
532          $new->validateState();
533   
534          return $new;
535      }
536   
537      public function withPath($path)
538      {
539          $path = $this->filterPath($path);
540   
541          if ($this->path === $path) {
542              return $this;
543          }
544   
545          $new = clone $this;
546          $new->path = $path;
547          $new->validateState();
548   
549          return $new;
550      }
551   
552      public function withQuery($query)
553      {
554          $query = $this->filterQueryAndFragment($query);
555   
556          if ($this->query === $query) {
557              return $this;
558          }
559   
560          $new = clone $this;
561          $new->query = $query;
562   
563          return $new;
564      }
565   
566      public function withFragment($fragment)
567      {
568          $fragment = $this->filterQueryAndFragment($fragment);
569   
570          if ($this->fragment === $fragment) {
571              return $this;
572          }
573   
574          $new = clone $this;
575          $new->fragment = $fragment;
576   
577          return $new;
578      }
579   
580      /**
581       * Apply parse_url parts to a URI.
582       *
583       * @param array $parts Array of parse_url parts to apply.
584       */
585      private function applyParts(array $parts)
586      {
587          $this->scheme = isset($parts['scheme'])
588              ? $this->filterScheme($parts['scheme'])
589              : '';
590          $this->userInfo = isset($parts['user'])
591              ? $this->filterUserInfoComponent($parts['user'])
592              : '';
593          $this->host = isset($parts['host'])
594              ? $this->filterHost($parts['host'])
595              : '';
596          $this->port = isset($parts['port'])
597              ? $this->filterPort($parts['port'])
598              : null;
599          $this->path = isset($parts['path'])
600              ? $this->filterPath($parts['path'])
601              : '';
602          $this->query = isset($parts['query'])
603              ? $this->filterQueryAndFragment($parts['query'])
604              : '';
605          $this->fragment = isset($parts['fragment'])
606              ? $this->filterQueryAndFragment($parts['fragment'])
607              : '';
608          if (isset($parts['pass'])) {
609              $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']);
610          }
611   
612          $this->removeDefaultPort();
613      }
614   
615      /**
616       * @param string $scheme
617       *
618       * @return string
619       *
620       * @throws \InvalidArgumentException If the scheme is invalid.
621       */
622      private function filterScheme($scheme)
623      {
624          if (!is_string($scheme)) {
625              throw new \InvalidArgumentException('Scheme must be a string');
626          }
627   
628          return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
629      }
630   
631      /**
632       * @param string $component
633       *
634       * @return string
635       *
636       * @throws \InvalidArgumentException If the user info is invalid.
637       */
638      private function filterUserInfoComponent($component)
639      {
640          if (!is_string($component)) {
641              throw new \InvalidArgumentException('User info must be a string');
642          }
643   
644          return preg_replace_callback(
645              '/(?:[^%' . self::$charUnreserved . self::$charSubDelims . ']+|%(?![A-Fa-f0-9]{2}))/',
646              [$this, 'rawurlencodeMatchZero'],
647              $component
648          );
649      }
650   
651      /**
652       * @param string $host
653       *
654       * @return string
655       *
656       * @throws \InvalidArgumentException If the host is invalid.
657       */
658      private function filterHost($host)
659      {
660          if (!is_string($host)) {
661              throw new \InvalidArgumentException('Host must be a string');
662          }
663   
664          return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
665      }
666   
667      /**
668       * @param int|null $port
669       *
670       * @return int|null
671       *
672       * @throws \InvalidArgumentException If the port is invalid.
673       */
674      private function filterPort($port)
675      {
676          if ($port === null) {
677              return null;
678          }
679   
680          $port = (int) $port;
681          if (0 > $port || 0xffff < $port) {
682              throw new \InvalidArgumentException(
683                  sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
684              );
685          }
686   
687          return $port;
688      }
689   
690      /**
691       * @param UriInterface $uri
692       * @param array        $keys
693       *
694       * @return array
695       */
696      private static function getFilteredQueryString(UriInterface $uri, array $keys)
697      {
698          $current = $uri->getQuery();
699   
700          if ($current === '') {
701              return [];
702          }
703   
704          $decodedKeys = array_map('rawurldecode', $keys);
705   
706          return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
707              return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
708          });
709      }
710   
711      /**
712       * @param string      $key
713       * @param string|null $value
714       *
715       * @return string
716       */
717      private static function generateQueryString($key, $value)
718      {
719          // Query string separators ("=", "&") within the key or value need to be encoded
720          // (while preventing double-encoding) before setting the query string. All other
721          // chars that need percent-encoding will be encoded by withQuery().
722          $queryString = strtr($key, self::$replaceQuery);
723   
724          if ($value !== null) {
725              $queryString .= '=' . strtr($value, self::$replaceQuery);
726          }
727   
728          return $queryString;
729      }
730   
731      private function removeDefaultPort()
732      {
733          if ($this->port !== null && self::isDefaultPort($this)) {
734              $this->port = null;
735          }
736      }
737   
738      /**
739       * Filters the path of a URI
740       *
741       * @param string $path
742       *
743       * @return string
744       *
745       * @throws \InvalidArgumentException If the path is invalid.
746       */
747      private function filterPath($path)
748      {
749          if (!is_string($path)) {
750              throw new \InvalidArgumentException('Path must be a string');
751          }
752   
753          return preg_replace_callback(
754              '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
755              [$this, 'rawurlencodeMatchZero'],
756              $path
757          );
758      }
759   
760      /**
761       * Filters the query string or fragment of a URI.
762       *
763       * @param string $str
764       *
765       * @return string
766       *
767       * @throws \InvalidArgumentException If the query or fragment is invalid.
768       */
769      private function filterQueryAndFragment($str)
770      {
771          if (!is_string($str)) {
772              throw new \InvalidArgumentException('Query and fragment must be a string');
773          }
774   
775          return preg_replace_callback(
776              '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
777              [$this, 'rawurlencodeMatchZero'],
778              $str
779          );
780      }
781   
782      private function rawurlencodeMatchZero(array $match)
783      {
784          return rawurlencode($match[0]);
785      }
786   
787      private function validateState()
788      {
789          if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
790              $this->host = self::HTTP_DEFAULT_HOST;
791          }
792   
793          if ($this->getAuthority() === '') {
794              if (0 === strpos($this->path, '//')) {
795                  throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"');
796              }
797              if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
798                  throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon');
799              }
800          } elseif (isset($this->path[0]) && $this->path[0] !== '/') {
801              @trigger_error(
802                  'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' .
803                  'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.',
804                  E_USER_DEPRECATED
805              );
806              $this->path = '/' . $this->path;
807              //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty');
808          }
809      }
810  }
811