TYPO3 CMS  TYPO3_8-7
Uri.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Http;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
18 
26 class Uri implements UriInterface
27 {
33  const SUBDELIMITER_CHARLIST = '!\$&\'\(\)\*\+,;=';
34 
40  const UNRESERVED_CHARLIST = 'a-zA-Z0-9_\-\.~';
41 
46  protected $scheme;
47 
51  protected $supportedSchemes = [
52  'http' => 80,
53  'https' => 443
54  ];
55 
60  protected $authority = '';
61 
66  protected $userInfo = '';
67 
72  protected $host = '';
73 
78  protected $port = null;
79 
84  protected $path = '';
85 
90  protected $query = '';
91 
96  protected $fragment;
97 
102  public function __construct($uri = '')
103  {
104  if (!is_string($uri)) {
105  $argumentType = is_object($uri) ? get_class($uri) : gettype($uri);
106  throw new \InvalidArgumentException('URI passed must be a string, but is of type "' . $argumentType . '"', 1436717320);
107  }
108  if (!empty($uri)) {
109  $this->parseUri($uri);
110  }
111  }
112 
118  protected function parseUri($uri)
119  {
120  $uriParts = parse_url($uri);
121 
122  if ($uriParts === false) {
123  throw new \InvalidArgumentException('The parsedUri string appears to be malformed', 1436717322);
124  }
125 
126  if (isset($uriParts['scheme'])) {
127  $this->scheme = $this->sanitizeScheme($uriParts['scheme']);
128  }
129 
130  if (isset($uriParts['user'])) {
131  $this->userInfo = $uriParts['user'];
132  if (isset($uriParts['pass'])) {
133  $this->userInfo .= ':' . $uriParts['pass'];
134  }
135  }
136 
137  if (isset($uriParts['host'])) {
138  $this->host = $uriParts['host'];
139  }
140 
141  if (isset($uriParts['port'])) {
142  $this->port = (int)$uriParts['port'];
143  }
144 
145  if (isset($uriParts['path'])) {
146  $this->path = $this->sanitizePath($uriParts['path']);
147  }
148 
149  if (isset($uriParts['query'])) {
150  $this->query = $this->sanitizeQuery($uriParts['query']);
151  }
152 
153  if (isset($uriParts['fragment'])) {
154  $this->fragment = $this->sanitizeFragment($uriParts['fragment']);
155  }
156  }
157 
172  public function getScheme()
173  {
174  return $this->scheme;
175  }
176 
195  public function getAuthority()
196  {
197  if (empty($this->host)) {
198  return '';
199  }
200 
202  if (!empty($this->userInfo)) {
203  $authority = $this->userInfo . '@' . $authority;
204  }
205 
206  if ($this->isNonStandardPort($this->scheme, $this->host, $this->port)) {
207  $authority .= ':' . $this->port;
208  }
209 
210  return $authority;
211  }
212 
228  public function getUserInfo()
229  {
230  return $this->userInfo;
231  }
232 
244  public function getHost()
245  {
246  return $this->host;
247  }
248 
264  public function getPort()
265  {
266  return $this->isNonStandardPort($this->scheme, $this->host, $this->port) ? $this->port : null;
267  }
268 
294  public function getPath()
295  {
296  return $this->path;
297  }
298 
319  public function getQuery()
320  {
321  return $this->query;
322  }
323 
340  public function getFragment()
341  {
342  return $this->fragment;
343  }
344 
361  public function withScheme($scheme)
362  {
363  $scheme = $this->sanitizeScheme($scheme);
364 
365  $clonedObject = clone $this;
366  $clonedObject->scheme = $scheme;
367  return $clonedObject;
368  }
369 
385  public function withUserInfo($user, $password = null)
386  {
387  $userInfo = $user;
388  if (!empty($password)) {
389  $userInfo .= ':' . $password;
390  }
391 
392  $clonedObject = clone $this;
393  $clonedObject->userInfo = $userInfo;
394  return $clonedObject;
395  }
396 
410  public function withHost($host)
411  {
412  $clonedObject = clone $this;
413  $clonedObject->host = $host;
414  return $clonedObject;
415  }
416 
435  public function withPort($port)
436  {
437  if ($port !== null) {
438  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($port) === false) {
439  $argumentType = is_object($port) ? get_class($port) : gettype($port);
440  throw new \InvalidArgumentException('Invalid port "' . $argumentType . '" specified, must be an integer.', 1436717324);
441  }
442 
443  $port = (int)$port;
444  if ($port < 1 || $port > 65535) {
445  throw new \InvalidArgumentException('Invalid port "' . $port . '" specified, must be a valid TCP/UDP port.', 1436717326);
446  }
447  }
448 
449  $clonedObject = clone $this;
450  $clonedObject->port = $port;
451  return $clonedObject;
452  }
453 
477  public function withPath($path)
478  {
479  if (!is_string($path)) {
480  throw new \InvalidArgumentException('Invalid path provided. Must be of type string.', 1436717328);
481  }
482 
483  if (strpos($path, '?') !== false) {
484  throw new \InvalidArgumentException('Invalid path provided. Must not contain a query string.', 1436717330);
485  }
486 
487  if (strpos($path, '#') !== false) {
488  throw new \InvalidArgumentException('Invalid path provided; must not contain a URI fragment', 1436717332);
489  }
490 
491  $path = $this->sanitizePath($path);
492  $clonedObject = clone $this;
493  $clonedObject->path = $path;
494  return $clonedObject;
495  }
496 
513  public function withQuery($query)
514  {
515  if (!is_string($query)) {
516  throw new \InvalidArgumentException('Query string must be a string.', 1436717334);
517  }
518 
519  if (strpos($query, '#') !== false) {
520  throw new \InvalidArgumentException('Query string must not include a URI fragment.', 1436717336);
521  }
522 
523  $query = $this->sanitizeQuery($query);
524  $clonedObject = clone $this;
525  $clonedObject->query = $query;
526  return $clonedObject;
527  }
528 
544  public function withFragment($fragment)
545  {
547  $clonedObject = clone $this;
548  $clonedObject->fragment = $fragment;
549  return $clonedObject;
550  }
551 
575  public function __toString()
576  {
577  $uri = '';
578 
579  if (!empty($this->scheme)) {
580  $uri .= $this->scheme . '://';
581  }
582 
583  $authority = $this->getAuthority();
584  if (!empty($authority)) {
585  $uri .= $authority;
586  }
587 
588  $path = $this->getPath();
589  if (!empty($path)) {
590  $uri .= '/' . ltrim($path, '/');
591  }
592 
593  if ($this->query) {
594  $uri .= '?' . $this->query;
595  }
596  if ($this->fragment) {
597  $uri .= '#' . $this->fragment;
598  }
599  return $uri;
600  }
601 
610  protected function isNonStandardPort($scheme, $host, $port)
611  {
612  if (empty($scheme)) {
613  return true;
614  }
615 
616  if (empty($host) || empty($port)) {
617  return false;
618  }
619 
620  return !isset($this->supportedSchemes[$scheme]) || $port !== $this->supportedSchemes[$scheme];
621  }
622 
631  protected function sanitizeScheme($scheme)
632  {
633  $scheme = strtolower($scheme);
634  $scheme = preg_replace('#:(//)?$#', '', $scheme);
635 
636  if (empty($scheme)) {
637  return '';
638  }
639 
640  if (!array_key_exists($scheme, $this->supportedSchemes)) {
641  throw new \InvalidArgumentException('Unsupported scheme "' . $scheme . '"; must be any empty string or in the set (' . implode(', ', array_keys($this->supportedSchemes)) . ')', 1436717338);
642  }
643 
644  return $scheme;
645  }
646 
653  protected function sanitizePath($path)
654  {
655  return preg_replace_callback(
656  '/(?:[^' . self::UNRESERVED_CHARLIST . ':@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/',
657  function ($matches) {
658  return rawurlencode($matches[0]);
659  },
660  $path
661  );
662  }
663 
672  protected function sanitizeQuery($query)
673  {
674  if (!empty($query) && strpos($query, '?') === 0) {
675  $query = substr($query, 1);
676  }
677 
678  $parts = explode('&', $query);
679  foreach ($parts as $index => $part) {
680  list($key, $value) = $this->splitQueryValue($part);
681  if ($value === null) {
682  $parts[$index] = $this->sanitizeQueryOrFragment($key);
683  continue;
684  }
685  $parts[$index] = $this->sanitizeQueryOrFragment($key) . '=' . $this->sanitizeQueryOrFragment($value);
686  }
687 
688  return implode('&', $parts);
689  }
690 
697  protected function splitQueryValue($value)
698  {
699  $data = explode('=', $value, 2);
700  if (count($data) === 1) {
701  $data[] = null;
702  }
703  return $data;
704  }
705 
712  protected function sanitizeFragment($fragment)
713  {
714  if ($fragment === null) {
715  $fragment = '';
716  }
717 
718  if (!empty($fragment) && strpos($fragment, '#') === 0) {
719  $fragment = substr($fragment, 1);
720  }
721 
722  return $this->sanitizeQueryOrFragment($fragment);
723  }
724 
731  protected function sanitizeQueryOrFragment($value)
732  {
733  return preg_replace_callback(
734  '/(?:[^' . self::UNRESERVED_CHARLIST . self::SUBDELIMITER_CHARLIST . '%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/',
735  function ($matches) {
736  return rawurlencode($matches[0]);
737  },
738  $value
739  );
740  }
741 }
withFragment($fragment)
Definition: Uri.php:544
splitQueryValue($value)
Definition: Uri.php:697
const UNRESERVED_CHARLIST
Definition: Uri.php:40
isNonStandardPort($scheme, $host, $port)
Definition: Uri.php:610
sanitizeFragment($fragment)
Definition: Uri.php:712
withUserInfo($user, $password=null)
Definition: Uri.php:385
sanitizeQuery($query)
Definition: Uri.php:672
withScheme($scheme)
Definition: Uri.php:361
sanitizeQueryOrFragment($value)
Definition: Uri.php:731
sanitizeScheme($scheme)
Definition: Uri.php:631
__construct($uri='')
Definition: Uri.php:102
const SUBDELIMITER_CHARLIST
Definition: Uri.php:33