PluginProbe
Packeta / 2.0.9
Packeta v2.0.9
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / http / src / Http / Url.php

Url.php in Packeta 2.0.9, at deps/nette/http/src/Http/Url.php

334 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\Http;
9
10 use Packetery\Nette;
11 /**
12 * Mutable representation of a URL.
13 *
14 * <pre>
15 * scheme user password host port path query fragment
16 * | | | | | | | |
17 * /--\ /--\ /------\ /-------\ /--\/------------\ /--------\ /------\
18 * http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl
19 * \______\__________________________/
20 * | |
21 * hostUrl authority
22 * </pre>
23 *
24 * @property string $scheme
25 * @property string $user
26 * @property string $password
27 * @property string $host
28 * @property int $port
29 * @property string $path
30 * @property string $query
31 * @property string $fragment
32 * @property-read string $absoluteUrl
33 * @property-read string $authority
34 * @property-read string $hostUrl
35 * @property-read string $basePath
36 * @property-read string $baseUrl
37 * @property-read string $relativeUrl
38 * @property-read array $queryParameters
39 */
40 class Url implements \JsonSerializable
41 {
42 use \Packetery\Nette\SmartObject;
43 /** @var array */
44 public static $defaultPorts = ['http' => 80, 'https' => 443, 'ftp' => 21];
45 /** @var string */
46 private $scheme = '';
47 /** @var string */
48 private $user = '';
49 /** @var string */
50 private $password = '';
51 /** @var string */
52 private $host = '';
53 /** @var int|null */
54 private $port;
55 /** @var string */
56 private $path = '';
57 /** @var array */
58 private $query = [];
59 /** @var string */
60 private $fragment = '';
61 /**
62 * @param string|self|UrlImmutable $url
63 * @throws \Packetery\Nette\InvalidArgumentException if URL is malformed
64 */
65 public function __construct($url = null)
66 {
67 if (\is_string($url)) {
68 $p = @\parse_url($url);
69 // @ - is escalated to exception
70 if ($p === \false) {
71 throw new \Packetery\Nette\InvalidArgumentException("Malformed or unsupported URI '{$url}'.");
72 }
73 $this->scheme = $p['scheme'] ?? '';
74 $this->port = $p['port'] ?? null;
75 $this->host = \rawurldecode($p['host'] ?? '');
76 $this->user = \rawurldecode($p['user'] ?? '');
77 $this->password = \rawurldecode($p['pass'] ?? '');
78 $this->setPath($p['path'] ?? '');
79 $this->setQuery($p['query'] ?? []);
80 $this->fragment = \rawurldecode($p['fragment'] ?? '');
81 } elseif ($url instanceof UrlImmutable || $url instanceof self) {
82 [$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment] = $url->export();
83 } elseif ($url !== null) {
84 throw new \Packetery\Nette\InvalidArgumentException();
85 }
86 }
87 /** @return static */
88 public function setScheme(string $scheme)
89 {
90 $this->scheme = $scheme;
91 return $this;
92 }
93 public function getScheme() : string
94 {
95 return $this->scheme;
96 }
97 /** @return static */
98 public function setUser(string $user)
99 {
100 $this->user = $user;
101 return $this;
102 }
103 public function getUser() : string
104 {
105 return $this->user;
106 }
107 /** @return static */
108 public function setPassword(string $password)
109 {
110 $this->password = $password;
111 return $this;
112 }
113 public function getPassword() : string
114 {
115 return $this->password;
116 }
117 /** @return static */
118 public function setHost(string $host)
119 {
120 $this->host = $host;
121 $this->setPath($this->path);
122 return $this;
123 }
124 public function getHost() : string
125 {
126 return $this->host;
127 }
128 /**
129 * Returns the part of domain.
130 */
131 public function getDomain(int $level = 2) : string
132 {
133 $parts = \ip2long($this->host) ? [$this->host] : \explode('.', $this->host);
134 $parts = $level >= 0 ? \array_slice($parts, -$level) : \array_slice($parts, 0, $level);
135 return \implode('.', $parts);
136 }
137 /** @return static */
138 public function setPort(int $port)
139 {
140 $this->port = $port;
141 return $this;
142 }
143 public function getPort() : ?int
144 {
145 return $this->port ?: $this->getDefaultPort();
146 }
147 public function getDefaultPort() : ?int
148 {
149 return self::$defaultPorts[$this->scheme] ?? null;
150 }
151 /** @return static */
152 public function setPath(string $path)
153 {
154 $this->path = $path;
155 if ($this->host && \substr($this->path, 0, 1) !== '/') {
156 $this->path = '/' . $this->path;
157 }
158 return $this;
159 }
160 public function getPath() : string
161 {
162 return $this->path;
163 }
164 /**
165 * @param string|array $value
166 * @return static
167 */
168 public function setQuery($query)
169 {
170 $this->query = \is_array($query) ? $query : self::parseQuery($query);
171 return $this;
172 }
173 /**
174 * @param string|array $value
175 * @return static
176 */
177 public function appendQuery($query)
178 {
179 $this->query = \is_array($query) ? $query + $this->query : self::parseQuery($this->getQuery() . '&' . $query);
180 return $this;
181 }
182 public function getQuery() : string
183 {
184 return \http_build_query($this->query, '', '&', \PHP_QUERY_RFC3986);
185 }
186 public function getQueryParameters() : array
187 {
188 return $this->query;
189 }
190 /** @return mixed */
191 public function getQueryParameter(string $name)
192 {
193 if (\func_num_args() > 1) {
194 \trigger_error(__METHOD__ . '() parameter $default is deprecated, use operator ??', \E_USER_DEPRECATED);
195 }
196 return $this->query[$name] ?? null;
197 }
198 /**
199 * @param mixed $value null unsets the parameter
200 * @return static
201 */
202 public function setQueryParameter(string $name, $value)
203 {
204 $this->query[$name] = $value;
205 return $this;
206 }
207 /** @return static */
208 public function setFragment(string $fragment)
209 {
210 $this->fragment = $fragment;
211 return $this;
212 }
213 public function getFragment() : string
214 {
215 return $this->fragment;
216 }
217 public function getAbsoluteUrl() : string
218 {
219 return $this->getHostUrl() . $this->path . (($tmp = $this->getQuery()) ? '?' . $tmp : '') . ($this->fragment === '' ? '' : '#' . $this->fragment);
220 }
221 /**
222 * Returns the [user[:pass]@]host[:port] part of URI.
223 */
224 public function getAuthority() : string
225 {
226 return $this->host === '' ? '' : ($this->user !== '' ? \rawurlencode($this->user) . ($this->password === '' ? '' : ':' . \rawurlencode($this->password)) . '@' : '') . $this->host . ($this->port && $this->port !== $this->getDefaultPort() ? ':' . $this->port : '');
227 }
228 /**
229 * Returns the scheme and authority part of URI.
230 */
231 public function getHostUrl() : string
232 {
233 return ($this->scheme ? $this->scheme . ':' : '') . (($authority = $this->getAuthority()) !== '' ? '//' . $authority : '');
234 }
235 /** @deprecated use UrlScript::getBasePath() instead */
236 public function getBasePath() : string
237 {
238 $pos = \strrpos($this->path, '/');
239 return $pos === \false ? '' : \substr($this->path, 0, $pos + 1);
240 }
241 /** @deprecated use UrlScript::getBaseUrl() instead */
242 public function getBaseUrl() : string
243 {
244 return $this->getHostUrl() . $this->getBasePath();
245 }
246 /** @deprecated use UrlScript::getRelativeUrl() instead */
247 public function getRelativeUrl() : string
248 {
249 return \substr($this->getAbsoluteUrl(), \strlen($this->getBaseUrl()));
250 }
251 /**
252 * URL comparison.
253 * @param string|self $url
254 */
255 public function isEqual($url) : bool
256 {
257 $url = new self($url);
258 $query = $url->query;
259 \ksort($query);
260 $query2 = $this->query;
261 \ksort($query2);
262 $host = \rtrim($url->host, '.');
263 $host2 = \rtrim($this->host, '.');
264 return $url->scheme === $this->scheme && (!\strcasecmp($host, $host2) || self::idnHostToUnicode($host) === self::idnHostToUnicode($host2)) && $url->getPort() === $this->getPort() && $url->user === $this->user && $url->password === $this->password && self::unescape($url->path, '%/') === self::unescape($this->path, '%/') && $query === $query2 && $url->fragment === $this->fragment;
265 }
266 /**
267 * Transforms URL to canonical form.
268 * @return static
269 * @deprecated
270 */
271 public function canonicalize()
272 {
273 $this->path = \preg_replace_callback('#[^!$&\'()*+,/:;=@%]+#', function (array $m) : string {
274 return \rawurlencode($m[0]);
275 }, self::unescape($this->path, '%/'));
276 $this->host = \rtrim($this->host, '.');
277 $this->host = self::idnHostToUnicode(\strtolower($this->host));
278 return $this;
279 }
280 public function __toString() : string
281 {
282 return $this->getAbsoluteUrl();
283 }
284 public function jsonSerialize() : string
285 {
286 return $this->getAbsoluteUrl();
287 }
288 /** @internal */
289 public final function export() : array
290 {
291 return [$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment];
292 }
293 /**
294 * Converts IDN ASCII host to UTF-8.
295 */
296 private static function idnHostToUnicode(string $host) : string
297 {
298 if (\strpos($host, '--') === \false) {
299 // host does not contain IDN
300 return $host;
301 }
302 if (\function_exists('idn_to_utf8') && \defined('INTL_IDNA_VARIANT_UTS46')) {
303 return \idn_to_utf8($host, \IDNA_DEFAULT, \INTL_IDNA_VARIANT_UTS46) ?: $host;
304 }
305 \trigger_error('PHP extension intl is not loaded or is too old', \E_USER_WARNING);
306 }
307 /**
308 * Similar to rawurldecode, but preserves reserved chars encoded.
309 */
310 public static function unescape(string $s, string $reserved = '%;/?:@&=+$,') : string
311 {
312 // reserved (@see RFC 2396) = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
313 // within a path segment, the characters "/", ";", "=", "?" are reserved
314 // within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", "$" are reserved.
315 if ($reserved !== '') {
316 $s = \preg_replace_callback('#%(' . \substr(\chunk_split(\bin2hex($reserved), 2, '|'), 0, -1) . ')#i', function (array $m) : string {
317 return '%25' . \strtoupper($m[1]);
318 }, $s);
319 }
320 return \rawurldecode($s);
321 }
322 /**
323 * Parses query string. Is affected by directive arg_separator.input.
324 */
325 public static function parseQuery(string $s) : array
326 {
327 $s = \str_replace(['%5B', '%5b'], '[', $s);
328 $sep = \preg_quote(\ini_get('arg_separator.input'));
329 $s = \preg_replace("#([{$sep}])([^[{$sep}=]+)([^{$sep}]*)#", '&0[$2]$3', '&' . $s);
330 \parse_str($s, $res);
331 return $res[0] ?? [];
332 }
333 }
334