xref: /plugin/dw2pdf/vendor/mpdf/psr-http-message-shim/src/Uri.php (revision 92200750cd9cfc4a9489fe7c04c6e41b5a3de6ee)
1<?php
2
3namespace Mpdf\PsrHttpMessageShim;
4
5use Psr\Http\Message\UriInterface;
6
7/**
8 * PSR-7 URI implementation ported from nyholm/psr7 and adapted for PHP 5.6
9 *
10 * @link https://github.com/Nyholm/psr7/blob/master/src/Uri.php
11 */
12final class Uri implements UriInterface
13{
14	private static $schemes = ['http' => 80, 'https' => 443];
15
16	const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
17
18	const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
19
20	/** @var string Uri scheme. */
21	private $scheme = '';
22
23	/** @var string Uri user info. */
24	private $userInfo = '';
25
26	/** @var string Uri host. */
27	private $host = '';
28
29	/** @var int|null Uri port. */
30	private $port;
31
32	/** @var string Uri path. */
33	private $path = '';
34
35	/** @var string Uri query string. */
36	private $query = '';
37
38	/** @var string Uri fragment. */
39	private $fragment = '';
40
41	public function __construct($uri = '')
42	{
43		if ('' !== $uri) {
44			if (false === $parts = \parse_url($uri)) {
45				throw new \InvalidArgumentException(\sprintf('Unable to parse URI: "%s"', $uri));
46			}
47
48			// Apply parse_url parts to a URI.
49			$this->scheme = isset($parts['scheme']) ? \strtr($parts['scheme'], 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') : '';
50			$this->userInfo = isset($parts['user']) ? $parts['user'] : '';
51			$this->host = isset($parts['host']) ? \strtr($parts['host'], 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') : '';
52			$this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null;
53			$this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
54			$this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : '';
55			$this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : '';
56			if (isset($parts['pass'])) {
57				$this->userInfo .= ':' . $parts['pass'];
58			}
59		}
60	}
61
62	public function __toString(): string
63	{
64		return self::createUriString($this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment);
65	}
66
67	public function getScheme(): string
68	{
69		return $this->scheme;
70	}
71
72	public function getAuthority(): string
73	{
74		if ('' === $this->host) {
75			return '';
76		}
77
78		$authority = $this->host;
79		if ('' !== $this->userInfo) {
80			$authority = $this->userInfo . '@' . $authority;
81		}
82
83		if (null !== $this->port) {
84			$authority .= ':' . $this->port;
85		}
86
87		return $authority;
88	}
89
90	public function getUserInfo(): string
91	{
92		return $this->userInfo;
93	}
94
95	public function getHost(): string
96	{
97		return $this->host;
98	}
99
100	public function getPort(): ?int
101	{
102		return $this->port;
103	}
104
105	public function getPath(): string
106	{
107		return $this->path;
108	}
109
110	public function getQuery(): string
111	{
112		return $this->query;
113	}
114
115	public function getFragment(): string
116	{
117		return $this->fragment;
118	}
119
120	public function withScheme(string $scheme): UriInterface
121	{
122		if (!\is_string($scheme)) {
123			throw new \InvalidArgumentException('Scheme must be a string');
124		}
125
126		if ($this->scheme === $scheme = \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')) {
127			return $this;
128		}
129
130		$new = clone $this;
131		$new->scheme = $scheme;
132		$new->port = $new->filterPort($new->port);
133
134		return $new;
135	}
136
137	public function withUserInfo(string $user, ?string $password = null): UriInterface
138	{
139		$info = $user;
140		if (null !== $password && '' !== $password) {
141			$info .= ':' . $password;
142		}
143
144		if ($this->userInfo === $info) {
145			return $this;
146		}
147
148		$new = clone $this;
149		$new->userInfo = $info;
150
151		return $new;
152	}
153
154	public function withHost(string $host): UriInterface
155	{
156		if (!\is_string($host)) {
157			throw new \InvalidArgumentException('Host must be a string');
158		}
159
160		if ($this->host === $host = \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')) {
161			return $this;
162		}
163
164		$new = clone $this;
165		$new->host = $host;
166
167		return $new;
168	}
169
170	public function withPort(?int $port): UriInterface
171	{
172		if ($this->port === $port = $this->filterPort($port)) {
173			return $this;
174		}
175
176		$new = clone $this;
177		$new->port = $port;
178
179		return $new;
180	}
181
182	public function withPath(string $path): UriInterface
183	{
184		if ($this->path === $path = $this->filterPath($path)) {
185			return $this;
186		}
187
188		$new = clone $this;
189		$new->path = $path;
190
191		return $new;
192	}
193
194	public function withQuery(string $query): UriInterface
195	{
196		if ($this->query === $query = $this->filterQueryAndFragment($query)) {
197			return $this;
198		}
199
200		$new = clone $this;
201		$new->query = $query;
202
203		return $new;
204	}
205
206	public function withFragment(string $fragment): UriInterface
207	{
208		if ($this->fragment === $fragment = $this->filterQueryAndFragment($fragment)) {
209			return $this;
210		}
211
212		$new = clone $this;
213		$new->fragment = $fragment;
214
215		return $new;
216	}
217
218	/**
219	 * Create a URI string from its various parts.
220	 */
221	private static function createUriString(string $scheme, string $authority, string $path, string $query, string $fragment): string
222	{
223		$uri = '';
224		if ('' !== $scheme) {
225			$uri .= $scheme . ':';
226		}
227
228		if ('' !== $authority) {
229			$uri .= '//' . $authority;
230		}
231
232		if ('' !== $path) {
233			if ('/' !== $path[0]) {
234				if ('' !== $authority) {
235					// If the path is rootless and an authority is present, the path MUST be prefixed by "/"
236					$path = '/' . $path;
237				}
238			} elseif (isset($path[1]) && '/' === $path[1]) {
239				if ('' === $authority) {
240					// If the path is starting with more than one "/" and no authority is present, the
241					// starting slashes MUST be reduced to one.
242					$path = '/' . \ltrim($path, '/');
243				}
244			}
245
246			$uri .= $path;
247		}
248
249		if ('' !== $query) {
250			$uri .= '?' . $query;
251		}
252
253		if ('' !== $fragment) {
254			$uri .= '#' . $fragment;
255		}
256
257		return $uri;
258	}
259
260	/**
261	 * Is a given port non-standard for the current scheme?
262	 */
263	private static function isNonStandardPort(string $scheme, int $port): bool
264	{
265		return !isset(self::$schemes[$scheme]) || $port !== self::$schemes[$scheme];
266	}
267
268	private function filterPort(int $port): ?int
269	{
270		if (null === $port) {
271			return null;
272		}
273
274		$port = (int) $port;
275		if (0 > $port || 0xffff < $port) {
276			throw new \InvalidArgumentException(\sprintf('Invalid port: %d. Must be between 0 and 65535', $port));
277		}
278
279		return self::isNonStandardPort($this->scheme, $port) ? $port : null;
280	}
281
282	private function filterPath($path)
283	{
284		if (!\is_string($path)) {
285			throw new \InvalidArgumentException('Path must be a string');
286		}
287
288		return \preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $path);
289	}
290
291	private function filterQueryAndFragment($str)
292	{
293		if (!\is_string($str)) {
294			throw new \InvalidArgumentException('Query and fragment must be a string');
295		}
296
297		return \preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $str);
298	}
299
300	private static function rawurlencodeMatchZero(array $match)
301	{
302		return \rawurlencode($match[0]);
303	}
304
305}
306