TYPO3 CMS  TYPO3_7-6
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 (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($port) === false) {
438  $argumentType = is_object($port) ? get_class($port) : gettype($port);
439  throw new \InvalidArgumentException('Invalid port "' . $argumentType . '" specified, must be an integer.', 1436717324);
440  }
441 
442  $port = (int)$port;
443  if ($port < 1 || $port > 65535) {
444  throw new \InvalidArgumentException('Invalid port "' . $port . '" specified, must be a valid TCP/UDP port.', 1436717326);
445  }
446 
447  $clonedObject = clone $this;
448  $clonedObject->port = $port;
449  return $clonedObject;
450  }
451 
475  public function withPath($path)
476  {
477  if (!is_string($path)) {
478  throw new \InvalidArgumentException('Invalid path provided. Must be of type string.', 1436717328);
479  }
480 
481  if (strpos($path, '?') !== false) {
482  throw new \InvalidArgumentException('Invalid path provided. Must not contain a query string.', 1436717330);
483  }
484 
485  if (strpos($path, '#') !== false) {
486  throw new \InvalidArgumentException('Invalid path provided; must not contain a URI fragment', 1436717332);
487  }
488 
489  $path = $this->sanitizePath($path);
490  $clonedObject = clone $this;
491  $clonedObject->path = $path;
492  return $clonedObject;
493  }
494 
511  public function withQuery($query)
512  {
513  if (!is_string($query)) {
514  throw new \InvalidArgumentException('Query string must be a string.', 1436717334);
515  }
516 
517  if (strpos($query, '#') !== false) {
518  throw new \InvalidArgumentException('Query string must not include a URI fragment.', 1436717336);
519  }
520 
521  $query = $this->sanitizeQuery($query);
522  $clonedObject = clone $this;
523  $clonedObject->query = $query;
524  return $clonedObject;
525  }
526 
542  public function withFragment($fragment)
543  {
545  $clonedObject = clone $this;
546  $clonedObject->fragment = $fragment;
547  return $clonedObject;
548  }
549 
573  public function __toString()
574  {
575  $uri = '';
576 
577  if (!empty($this->scheme)) {
578  $uri .= $this->scheme . '://';
579  }
580 
581  $authority = $this->getAuthority();
582  if (!empty($authority)) {
583  $uri .= $authority;
584  }
585 
586  $path = $this->getPath();
587  if (!empty($path)) {
588  $uri .= '/' . ltrim($path, '/');
589  }
590 
591  if ($this->query) {
592  $uri .= '?' . $this->query;
593  }
594  if ($this->fragment) {
595  $uri .= '#' . $this->fragment;
596  }
597  return $uri;
598  }
599 
608  protected function isNonStandardPort($scheme, $host, $port)
609  {
610  if (empty($scheme)) {
611  return true;
612  }
613 
614  if (empty($host) || empty($port)) {
615  return false;
616  }
617 
618  return !isset($this->supportedSchemes[$scheme]) || $port !== $this->supportedSchemes[$scheme];
619  }
620 
629  protected function sanitizeScheme($scheme)
630  {
631  $scheme = strtolower($scheme);
632  $scheme = preg_replace('#:(//)?$#', '', $scheme);
633 
634  if (empty($scheme)) {
635  return '';
636  }
637 
638  if (!array_key_exists($scheme, $this->supportedSchemes)) {
639  throw new \InvalidArgumentException('Unsupported scheme "' . $scheme . '"; must be any empty string or in the set (' . implode(', ', array_keys($this->supportedSchemes)) . ')', 1436717338);
640  }
641 
642  return $scheme;
643  }
644 
651  protected function sanitizePath($path)
652  {
653  return preg_replace_callback(
654  '/(?:[^' . self::UNRESERVED_CHARLIST . ':@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/',
655  function ($matches) {
656  return rawurlencode($matches[0]);
657  },
658  $path
659  );
660  }
661 
670  protected function sanitizeQuery($query)
671  {
672  if (!empty($query) && strpos($query, '?') === 0) {
673  $query = substr($query, 1);
674  }
675 
676  $parts = explode('&', $query);
677  foreach ($parts as $index => $part) {
678  list($key, $value) = $this->splitQueryValue($part);
679  if ($value === null) {
680  $parts[$index] = $this->sanitizeQueryOrFragment($key);
681  continue;
682  }
683  $parts[$index] = $this->sanitizeQueryOrFragment($key) . '=' . $this->sanitizeQueryOrFragment($value);
684  }
685 
686  return implode('&', $parts);
687  }
688 
695  protected function splitQueryValue($value)
696  {
697  $data = explode('=', $value, 2);
698  if (count($data) === 1) {
699  $data[] = null;
700  }
701  return $data;
702  }
703 
710  protected function sanitizeFragment($fragment)
711  {
712  if ($fragment === null) {
713  $fragment = '';
714  }
715 
716  if (!empty($fragment) && strpos($fragment, '#') === 0) {
717  $fragment = substr($fragment, 1);
718  }
719 
720  return $this->sanitizeQueryOrFragment($fragment);
721  }
722 
729  protected function sanitizeQueryOrFragment($value)
730  {
731  return preg_replace_callback(
732  '/(?:[^' . self::UNRESERVED_CHARLIST . self::SUBDELIMITER_CHARLIST . '%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/',
733  function ($matches) {
734  return rawurlencode($matches[0]);
735  },
736  $value
737  );
738  }
739 }
withFragment($fragment)
Definition: Uri.php:542
splitQueryValue($value)
Definition: Uri.php:695
const UNRESERVED_CHARLIST
Definition: Uri.php:40
isNonStandardPort($scheme, $host, $port)
Definition: Uri.php:608
sanitizeFragment($fragment)
Definition: Uri.php:710
withUserInfo($user, $password=null)
Definition: Uri.php:385
sanitizeQuery($query)
Definition: Uri.php:670
withScheme($scheme)
Definition: Uri.php:361
sanitizeQueryOrFragment($value)
Definition: Uri.php:729
sanitizeScheme($scheme)
Definition: Uri.php:629
__construct($uri='')
Definition: Uri.php:102
const SUBDELIMITER_CHARLIST
Definition: Uri.php:33