Open Journal Systems  3.3.0
Uri.php
1 <?php
2 namespace GuzzleHttp\Psr7;
3 
5 
13 class Uri implements UriInterface
14 {
21  const HTTP_DEFAULT_HOST = 'localhost';
22 
23  private static $defaultPorts = [
24  'http' => 80,
25  'https' => 443,
26  'ftp' => 21,
27  'gopher' => 70,
28  'nntp' => 119,
29  'news' => 119,
30  'telnet' => 23,
31  'tn3270' => 23,
32  'imap' => 143,
33  'pop' => 110,
34  'ldap' => 389,
35  ];
36 
37  private static $charUnreserved = 'a-zA-Z0-9_\-\.~';
38  private static $charSubDelims = '!\$&\'\(\)\*\+,;=';
39  private static $replaceQuery = ['=' => '%3D', '&' => '%26'];
40 
42  private $scheme = '';
43 
45  private $userInfo = '';
46 
48  private $host = '';
49 
51  private $port;
52 
54  private $path = '';
55 
57  private $query = '';
58 
60  private $fragment = '';
61 
65  public function __construct($uri = '')
66  {
67  // weak type check to also accept null until we can add scalar type hints
68  if ($uri != '') {
69  $parts = parse_url($uri);
70  if ($parts === false) {
71  throw new \InvalidArgumentException("Unable to parse URI: $uri");
72  }
73  $this->applyParts($parts);
74  }
75  }
76 
77  public function __toString()
78  {
80  $this->scheme,
81  $this->getAuthority(),
82  $this->path,
83  $this->query,
84  $this->fragment
85  );
86  }
87 
114  public static function composeComponents($scheme, $authority, $path, $query, $fragment)
115  {
116  $uri = '';
117 
118  // weak type checks to also accept null until we can add scalar type hints
119  if ($scheme != '') {
120  $uri .= $scheme . ':';
121  }
122 
123  if ($authority != ''|| $scheme === 'file') {
124  $uri .= '//' . $authority;
125  }
126 
127  $uri .= $path;
128 
129  if ($query != '') {
130  $uri .= '?' . $query;
131  }
132 
133  if ($fragment != '') {
134  $uri .= '#' . $fragment;
135  }
136 
137  return $uri;
138  }
139 
150  public static function isDefaultPort(UriInterface $uri)
151  {
152  return $uri->getPort() === null
153  || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]);
154  }
155 
174  public static function isAbsolute(UriInterface $uri)
175  {
176  return $uri->getScheme() !== '';
177  }
178 
189  public static function isNetworkPathReference(UriInterface $uri)
190  {
191  return $uri->getScheme() === '' && $uri->getAuthority() !== '';
192  }
193 
204  public static function isAbsolutePathReference(UriInterface $uri)
205  {
206  return $uri->getScheme() === ''
207  && $uri->getAuthority() === ''
208  && isset($uri->getPath()[0])
209  && $uri->getPath()[0] === '/';
210  }
211 
222  public static function isRelativePathReference(UriInterface $uri)
223  {
224  return $uri->getScheme() === ''
225  && $uri->getAuthority() === ''
226  && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
227  }
228 
242  public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null)
243  {
244  if ($base !== null) {
245  $uri = UriResolver::resolve($base, $uri);
246 
247  return ($uri->getScheme() === $base->getScheme())
248  && ($uri->getAuthority() === $base->getAuthority())
249  && ($uri->getPath() === $base->getPath())
250  && ($uri->getQuery() === $base->getQuery());
251  }
252 
253  return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
254  }
255 
266  public static function removeDotSegments($path)
267  {
268  return UriResolver::removeDotSegments($path);
269  }
270 
282  public static function resolve(UriInterface $base, $rel)
283  {
284  if (!($rel instanceof UriInterface)) {
285  $rel = new self($rel);
286  }
287 
288  return UriResolver::resolve($base, $rel);
289  }
290 
302  public static function withoutQueryValue(UriInterface $uri, $key)
303  {
304  $result = self::getFilteredQueryString($uri, [$key]);
305 
306  return $uri->withQuery(implode('&', $result));
307  }
308 
324  public static function withQueryValue(UriInterface $uri, $key, $value)
325  {
326  $result = self::getFilteredQueryString($uri, [$key]);
327 
328  $result[] = self::generateQueryString($key, $value);
329 
330  return $uri->withQuery(implode('&', $result));
331  }
332 
343  public static function withQueryValues(UriInterface $uri, array $keyValueArray)
344  {
345  $result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
346 
347  foreach ($keyValueArray as $key => $value) {
348  $result[] = self::generateQueryString($key, $value);
349  }
350 
351  return $uri->withQuery(implode('&', $result));
352  }
353 
364  public static function fromParts(array $parts)
365  {
366  $uri = new self();
367  $uri->applyParts($parts);
368  $uri->validateState();
369 
370  return $uri;
371  }
372 
373  public function getScheme()
374  {
375  return $this->scheme;
376  }
377 
378  public function getAuthority()
379  {
380  $authority = $this->host;
381  if ($this->userInfo !== '') {
382  $authority = $this->userInfo . '@' . $authority;
383  }
384 
385  if ($this->port !== null) {
386  $authority .= ':' . $this->port;
387  }
388 
389  return $authority;
390  }
391 
392  public function getUserInfo()
393  {
394  return $this->userInfo;
395  }
396 
397  public function getHost()
398  {
399  return $this->host;
400  }
401 
402  public function getPort()
403  {
404  return $this->port;
405  }
406 
407  public function getPath()
408  {
409  return $this->path;
410  }
411 
412  public function getQuery()
413  {
414  return $this->query;
415  }
416 
417  public function getFragment()
418  {
419  return $this->fragment;
420  }
421 
422  public function withScheme($scheme)
423  {
424  $scheme = $this->filterScheme($scheme);
425 
426  if ($this->scheme === $scheme) {
427  return $this;
428  }
429 
430  $new = clone $this;
431  $new->scheme = $scheme;
432  $new->removeDefaultPort();
433  $new->validateState();
434 
435  return $new;
436  }
437 
438  public function withUserInfo($user, $password = null)
439  {
440  $info = $this->filterUserInfoComponent($user);
441  if ($password !== null) {
442  $info .= ':' . $this->filterUserInfoComponent($password);
443  }
444 
445  if ($this->userInfo === $info) {
446  return $this;
447  }
448 
449  $new = clone $this;
450  $new->userInfo = $info;
451  $new->validateState();
452 
453  return $new;
454  }
455 
456  public function withHost($host)
457  {
458  $host = $this->filterHost($host);
459 
460  if ($this->host === $host) {
461  return $this;
462  }
463 
464  $new = clone $this;
465  $new->host = $host;
466  $new->validateState();
467 
468  return $new;
469  }
470 
471  public function withPort($port)
472  {
473  $port = $this->filterPort($port);
474 
475  if ($this->port === $port) {
476  return $this;
477  }
478 
479  $new = clone $this;
480  $new->port = $port;
481  $new->removeDefaultPort();
482  $new->validateState();
483 
484  return $new;
485  }
486 
487  public function withPath($path)
488  {
489  $path = $this->filterPath($path);
490 
491  if ($this->path === $path) {
492  return $this;
493  }
494 
495  $new = clone $this;
496  $new->path = $path;
497  $new->validateState();
498 
499  return $new;
500  }
501 
502  public function withQuery($query)
503  {
504  $query = $this->filterQueryAndFragment($query);
505 
506  if ($this->query === $query) {
507  return $this;
508  }
509 
510  $new = clone $this;
511  $new->query = $query;
512 
513  return $new;
514  }
515 
516  public function withFragment($fragment)
517  {
518  $fragment = $this->filterQueryAndFragment($fragment);
519 
520  if ($this->fragment === $fragment) {
521  return $this;
522  }
523 
524  $new = clone $this;
525  $new->fragment = $fragment;
526 
527  return $new;
528  }
529 
535  private function applyParts(array $parts)
536  {
537  $this->scheme = isset($parts['scheme'])
538  ? $this->filterScheme($parts['scheme'])
539  : '';
540  $this->userInfo = isset($parts['user'])
541  ? $this->filterUserInfoComponent($parts['user'])
542  : '';
543  $this->host = isset($parts['host'])
544  ? $this->filterHost($parts['host'])
545  : '';
546  $this->port = isset($parts['port'])
547  ? $this->filterPort($parts['port'])
548  : null;
549  $this->path = isset($parts['path'])
550  ? $this->filterPath($parts['path'])
551  : '';
552  $this->query = isset($parts['query'])
553  ? $this->filterQueryAndFragment($parts['query'])
554  : '';
555  $this->fragment = isset($parts['fragment'])
556  ? $this->filterQueryAndFragment($parts['fragment'])
557  : '';
558  if (isset($parts['pass'])) {
559  $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']);
560  }
561 
562  $this->removeDefaultPort();
563  }
564 
572  private function filterScheme($scheme)
573  {
574  if (!is_string($scheme)) {
575  throw new \InvalidArgumentException('Scheme must be a string');
576  }
577 
578  return strtolower($scheme);
579  }
580 
588  private function filterUserInfoComponent($component)
589  {
590  if (!is_string($component)) {
591  throw new \InvalidArgumentException('User info must be a string');
592  }
593 
594  return preg_replace_callback(
595  '/(?:[^%' . self::$charUnreserved . self::$charSubDelims . ']+|%(?![A-Fa-f0-9]{2}))/',
596  [$this, 'rawurlencodeMatchZero'],
597  $component
598  );
599  }
600 
608  private function filterHost($host)
609  {
610  if (!is_string($host)) {
611  throw new \InvalidArgumentException('Host must be a string');
612  }
613 
614  return strtolower($host);
615  }
616 
624  private function filterPort($port)
625  {
626  if ($port === null) {
627  return null;
628  }
629 
630  $port = (int) $port;
631  if (0 > $port || 0xffff < $port) {
632  throw new \InvalidArgumentException(
633  sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
634  );
635  }
636 
637  return $port;
638  }
639 
646  private static function getFilteredQueryString(UriInterface $uri, array $keys)
647  {
648  $current = $uri->getQuery();
649 
650  if ($current === '') {
651  return [];
652  }
653 
654  $decodedKeys = array_map('rawurldecode', $keys);
655 
656  return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
657  return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
658  });
659  }
660 
667  private static function generateQueryString($key, $value)
668  {
669  // Query string separators ("=", "&") within the key or value need to be encoded
670  // (while preventing double-encoding) before setting the query string. All other
671  // chars that need percent-encoding will be encoded by withQuery().
672  $queryString = strtr($key, self::$replaceQuery);
673 
674  if ($value !== null) {
675  $queryString .= '=' . strtr($value, self::$replaceQuery);
676  }
677 
678  return $queryString;
679  }
680 
681  private function removeDefaultPort()
682  {
683  if ($this->port !== null && self::isDefaultPort($this)) {
684  $this->port = null;
685  }
686  }
687 
697  private function filterPath($path)
698  {
699  if (!is_string($path)) {
700  throw new \InvalidArgumentException('Path must be a string');
701  }
702 
703  return preg_replace_callback(
704  '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
705  [$this, 'rawurlencodeMatchZero'],
706  $path
707  );
708  }
709 
719  private function filterQueryAndFragment($str)
720  {
721  if (!is_string($str)) {
722  throw new \InvalidArgumentException('Query and fragment must be a string');
723  }
724 
725  return preg_replace_callback(
726  '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
727  [$this, 'rawurlencodeMatchZero'],
728  $str
729  );
730  }
731 
732  private function rawurlencodeMatchZero(array $match)
733  {
734  return rawurlencode($match[0]);
735  }
736 
737  private function validateState()
738  {
739  if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
740  $this->host = self::HTTP_DEFAULT_HOST;
741  }
742 
743  if ($this->getAuthority() === '') {
744  if (0 === strpos($this->path, '//')) {
745  throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"');
746  }
747  if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
748  throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon');
749  }
750  } elseif (isset($this->path[0]) && $this->path[0] !== '/') {
751  @trigger_error(
752  'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' .
753  'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.',
754  E_USER_DEPRECATED
755  );
756  $this->path = '/'. $this->path;
757  //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty');
758  }
759  }
760 }
GuzzleHttp\Psr7\Uri\withQuery
withQuery($query)
Definition: Uri.php:523
GuzzleHttp\Psr7\Uri\__construct
__construct($uri='')
Definition: Uri.php:86
Psr\Http\Message\UriInterface\withQuery
withQuery($query)
Psr\Http\Message\UriInterface\getPath
getPath()
GuzzleHttp\Psr7\Uri\getPort
getPort()
Definition: Uri.php:423
GuzzleHttp\Psr7\Uri\withPath
withPath($path)
Definition: Uri.php:508
GuzzleHttp\Psr7\Uri\withHost
withHost($host)
Definition: Uri.php:477
GuzzleHttp\Psr7\Uri\getAuthority
getAuthority()
Definition: Uri.php:399
GuzzleHttp\Psr7\UriResolver\resolve
static resolve(UriInterface $base, UriInterface $rel)
Definition: UriResolver.php:62
GuzzleHttp\Psr7\UriResolver\removeDotSegments
static removeDotSegments($path)
Definition: UriResolver.php:23
GuzzleHttp\Psr7\Uri\composeComponents
static composeComponents($scheme, $authority, $path, $query, $fragment)
Definition: Uri.php:135
GuzzleHttp\Psr7\Uri\getHost
getHost()
Definition: Uri.php:418
GuzzleHttp\Psr7\Uri\withFragment
withFragment($fragment)
Definition: Uri.php:537
GuzzleHttp\Psr7\Uri\resolve
static resolve(UriInterface $base, $rel)
Definition: Uri.php:303
GuzzleHttp\Psr7\Uri
Definition: Uri.php:13
GuzzleHttp\Psr7\Uri\getPath
getPath()
Definition: Uri.php:428
GuzzleHttp\Psr7\Uri\withScheme
withScheme($scheme)
Definition: Uri.php:443
GuzzleHttp\Psr7\Uri\isNetworkPathReference
static isNetworkPathReference(UriInterface $uri)
Definition: Uri.php:210
GuzzleHttp\Psr7
Definition: AppendStream.php:2
Psr\Http\Message\UriInterface
Definition: UriInterface.php:24
GuzzleHttp\Psr7\Uri\withUserInfo
withUserInfo($user, $password=null)
Definition: Uri.php:459
GuzzleHttp\Psr7\Uri\isSameDocumentReference
static isSameDocumentReference(UriInterface $uri, UriInterface $base=null)
Definition: Uri.php:263
GuzzleHttp\Psr7\Uri\__toString
__toString()
Definition: Uri.php:98
GuzzleHttp\Psr7\Uri\isDefaultPort
static isDefaultPort(UriInterface $uri)
Definition: Uri.php:171
GuzzleHttp\Psr7\Uri\isRelativePathReference
static isRelativePathReference(UriInterface $uri)
Definition: Uri.php:243
GuzzleHttp\Psr7\Uri\isAbsolutePathReference
static isAbsolutePathReference(UriInterface $uri)
Definition: Uri.php:225
Psr\Http\Message\UriInterface\getScheme
getScheme()
GuzzleHttp\Psr7\Uri\withPort
withPort($port)
Definition: Uri.php:492
GuzzleHttp\Psr7\Uri\withQueryValues
static withQueryValues(UriInterface $uri, array $keyValueArray)
Definition: Uri.php:364
GuzzleHttp\Psr7\Uri\getQuery
getQuery()
Definition: Uri.php:433
Psr\Http\Message\UriInterface\getQuery
getQuery()
GuzzleHttp\Psr7\Uri\getUserInfo
getUserInfo()
Definition: Uri.php:413
GuzzleHttp\Psr7\Uri\fromParts
static fromParts(array $parts)
Definition: Uri.php:385
GuzzleHttp\Psr7\Uri\withoutQueryValue
static withoutQueryValue(UriInterface $uri, $key)
Definition: Uri.php:323
GuzzleHttp\Psr7\Uri\isAbsolute
static isAbsolute(UriInterface $uri)
Definition: Uri.php:195
GuzzleHttp\Psr7\Uri\getFragment
getFragment()
Definition: Uri.php:438
GuzzleHttp\Psr7\Uri\withQueryValue
static withQueryValue(UriInterface $uri, $key, $value)
Definition: Uri.php:345
Psr\Http\Message\UriInterface\getPort
getPort()
Psr\Http\Message\UriInterface\getAuthority
getAuthority()
GuzzleHttp\Psr7\Uri\HTTP_DEFAULT_HOST
const HTTP_DEFAULT_HOST
Definition: Uri.php:21
GuzzleHttp\Psr7\Uri\removeDotSegments
static removeDotSegments($path)
Definition: Uri.php:287
GuzzleHttp\Psr7\Uri\getScheme
getScheme()
Definition: Uri.php:394