PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.0
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.0
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / guzzlehttp / psr7 / src / Uri.php

Uri.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.0, at vendor_prefixed/guzzlehttp/psr7/src/Uri.php

634 lines 24.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare (strict_types=1);
4 namespace YoastSEO_Vendor\GuzzleHttp\Psr7;
5
6 use YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException;
7 use YoastSEO_Vendor\Psr\Http\Message\UriInterface;
8 /**
9 * PSR-7 URI implementation.
10 *
11 * @author Michael Dowling
12 * @author Tobias Schultze
13 * @author Matthew Weier O'Phinney
14 */
15 class Uri implements \YoastSEO_Vendor\Psr\Http\Message\UriInterface, \JsonSerializable
16 {
17 /**
18 * Absolute http and https URIs require a host per RFC 7230 Section 2.7
19 * but in generic URIs the host can be empty. So for http(s) URIs
20 * we apply this default host when no host is given yet to form a
21 * valid URI.
22 */
23 private const HTTP_DEFAULT_HOST = 'localhost';
24 private const DEFAULT_PORTS = ['http' => 80, 'https' => 443, 'ftp' => 21, 'gopher' => 70, 'nntp' => 119, 'news' => 119, 'telnet' => 23, 'tn3270' => 23, 'imap' => 143, 'pop' => 110, 'ldap' => 389];
25 private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26', '+' => '%2B'];
26 /** @var string Uri scheme. */
27 private $scheme = '';
28 /** @var string Uri user info. */
29 private $userInfo = '';
30 /** @var string Uri host. */
31 private $host = '';
32 /** @var int|null Uri port. */
33 private $port;
34 /** @var string Uri path. */
35 private $path = '';
36 /** @var string Uri query string. */
37 private $query = '';
38 /** @var string Uri fragment. */
39 private $fragment = '';
40 public function __construct(string $uri = '')
41 {
42 if ($uri !== '') {
43 $parts = self::parse($uri);
44 if ($parts === \false) {
45 throw new \YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException("Unable to parse URI: {$uri}");
46 }
47 try {
48 $this->applyParts($parts);
49 } catch (\YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException $e) {
50 throw $e;
51 } catch (\InvalidArgumentException $e) {
52 throw new \YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException($e->getMessage(), 0, $e);
53 }
54 }
55 }
56 /**
57 * UTF-8 aware \parse_url() replacement.
58 *
59 * The internal function produces broken output for non ASCII domain names
60 * (IDN) when used with locales other than "C".
61 *
62 * On the other hand, cURL understands IDN correctly only when UTF-8 locale
63 * is configured ("C.UTF-8", "en_US.UTF-8", etc.).
64 *
65 * @see https://bugs.php.net/bug.php?id=52923
66 * @see https://www.php.net/manual/en/function.parse-url.php#114817
67 * @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING
68 *
69 * @return array|false
70 */
71 private static function parse(string $url)
72 {
73 if (self::isPathNoSchemeReference($url)) {
74 return self::parsePathNoSchemeReference($url);
75 }
76 // Preserve bracketed IPv6 literals before encoding, including dotted IPv4 tails.
77 $prefix = '';
78 if (\preg_match('%^([0-9A-Za-z+.-]+://\\[[0-9:.a-fA-F]+\\])(.*?)$%', $url, $matches)) {
79 /** @var array{0:string, 1:string, 2:string} $matches */
80 $prefix = $matches[1];
81 $url = $matches[2];
82 }
83 /** @var string|null */
84 $encodedUrl = \preg_replace_callback('%[^:/@?&=#]+%usD', static function ($matches) {
85 return \urlencode($matches[0]);
86 }, $url);
87 if ($encodedUrl === null) {
88 return \false;
89 }
90 $result = \parse_url($prefix . $encodedUrl);
91 if ($result === \false) {
92 return \false;
93 }
94 return \array_map('urldecode', $result);
95 }
96 private static function isPathNoSchemeReference(string $url) : bool
97 {
98 if ($url === '' || $url[0] === '/' || $url[0] === '?' || $url[0] === '#') {
99 return \false;
100 }
101 $firstSegment = \substr($url, 0, \strcspn($url, '/?#'));
102 return \strpos($firstSegment, ':') === \false;
103 }
104 /**
105 * @return array{path: string, query?: string, fragment?: string}
106 */
107 private static function parsePathNoSchemeReference(string $url) : array
108 {
109 $parts = [];
110 if (\false !== ($fragmentPosition = \strpos($url, '#'))) {
111 $parts['fragment'] = \substr($url, $fragmentPosition + 1);
112 $url = \substr($url, 0, $fragmentPosition);
113 }
114 if (\false !== ($queryPosition = \strpos($url, '?'))) {
115 $parts['query'] = \substr($url, $queryPosition + 1);
116 $url = \substr($url, 0, $queryPosition);
117 }
118 $parts['path'] = $url;
119 return $parts;
120 }
121 public function __toString() : string
122 {
123 return self::composeComponents($this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment);
124 }
125 /**
126 * Composes a URI reference string from its various components.
127 *
128 * Usually this method does not need to be called manually but instead is used indirectly via
129 * `Psr\Http\Message\UriInterface::__toString`.
130 *
131 * PSR-7 UriInterface treats an empty component the same as a missing component as
132 * getQuery(), getFragment() etc. always return a string. This explains the slight
133 * difference to RFC 3986 Section 5.3.
134 *
135 * Another adjustment is that the authority separator is added even when the authority is missing/empty
136 * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
137 * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
138 * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
139 * that format).
140 *
141 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.3
142 */
143 public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment) : string
144 {
145 $uri = '';
146 // weak type checks to also accept null until we can add scalar type hints
147 if ($scheme != '') {
148 $uri .= $scheme . ':';
149 }
150 if ($authority != '' || $scheme === 'file') {
151 $uri .= '//' . $authority;
152 }
153 if ($authority != '' && $path != '' && $path[0] != '/') {
154 $path = '/' . $path;
155 }
156 $uri .= $path;
157 if ($query != '') {
158 $uri .= '?' . $query;
159 }
160 if ($fragment != '') {
161 $uri .= '#' . $fragment;
162 }
163 return $uri;
164 }
165 /**
166 * Whether the URI has the default port of the current scheme.
167 *
168 * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
169 * independently of the implementation.
170 */
171 public static function isDefaultPort(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool
172 {
173 return $uri->getPort() === null || isset(self::DEFAULT_PORTS[$uri->getScheme()]) && $uri->getPort() === self::DEFAULT_PORTS[$uri->getScheme()];
174 }
175 /**
176 * Whether the URI is absolute, i.e. it has a scheme.
177 *
178 * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
179 * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
180 * to another URI, the base URI. Relative references can be divided into several forms:
181 * - network-path references, e.g. '//example.com/path'
182 * - absolute-path references, e.g. '/path'
183 * - relative-path references, e.g. 'subpath'
184 *
185 * @see Uri::isNetworkPathReference
186 * @see Uri::isAbsolutePathReference
187 * @see Uri::isRelativePathReference
188 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4
189 */
190 public static function isAbsolute(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool
191 {
192 return $uri->getScheme() !== '';
193 }
194 /**
195 * Whether the URI is a network-path reference.
196 *
197 * A relative reference that begins with two slash characters is termed an network-path reference.
198 *
199 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
200 */
201 public static function isNetworkPathReference(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool
202 {
203 return $uri->getScheme() === '' && $uri->getAuthority() !== '';
204 }
205 /**
206 * Whether the URI is a absolute-path reference.
207 *
208 * A relative reference that begins with a single slash character is termed an absolute-path reference.
209 *
210 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
211 */
212 public static function isAbsolutePathReference(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool
213 {
214 return $uri->getScheme() === '' && $uri->getAuthority() === '' && isset($uri->getPath()[0]) && $uri->getPath()[0] === '/';
215 }
216 /**
217 * Whether the URI is a relative-path reference.
218 *
219 * A relative reference that does not begin with a slash character is termed a relative-path reference.
220 *
221 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
222 */
223 public static function isRelativePathReference(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool
224 {
225 return $uri->getScheme() === '' && $uri->getAuthority() === '' && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
226 }
227 /**
228 * Whether the URI is a same-document reference.
229 *
230 * A same-document reference refers to a URI that is, aside from its fragment
231 * component, identical to the base URI. When no base URI is given, only an empty
232 * URI reference (apart from its fragment) is considered a same-document reference.
233 *
234 * @param UriInterface $uri The URI to check
235 * @param UriInterface|null $base An optional base URI to compare against
236 *
237 * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.4
238 */
239 public static function isSameDocumentReference(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, ?\YoastSEO_Vendor\Psr\Http\Message\UriInterface $base = null) : bool
240 {
241 if ($base !== null) {
242 $uri = \YoastSEO_Vendor\GuzzleHttp\Psr7\UriResolver::resolve($base, $uri);
243 return $uri->getScheme() === $base->getScheme() && $uri->getAuthority() === $base->getAuthority() && $uri->getPath() === $base->getPath() && $uri->getQuery() === $base->getQuery();
244 }
245 return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
246 }
247 /**
248 * Creates a new URI with a specific query string value removed.
249 *
250 * Any existing query string values that exactly match the provided key are
251 * removed.
252 *
253 * @param UriInterface $uri URI to use as a base.
254 * @param string $key Query string key to remove.
255 */
256 public static function withoutQueryValue(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, string $key) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
257 {
258 $result = self::getFilteredQueryString($uri, [$key]);
259 return $uri->withQuery(\implode('&', $result));
260 }
261 /**
262 * Creates a new URI with a specific query string value.
263 *
264 * Any existing query string values that exactly match the provided key are
265 * removed and replaced with the given key value pair.
266 *
267 * A value of null will set the query string key without a value, e.g. "key"
268 * instead of "key=value".
269 *
270 * @param UriInterface $uri URI to use as a base.
271 * @param string $key Key to set.
272 * @param string|null $value Value to set
273 */
274 public static function withQueryValue(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, string $key, ?string $value) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
275 {
276 $result = self::getFilteredQueryString($uri, [$key]);
277 $result[] = self::generateQueryString($key, $value);
278 return $uri->withQuery(\implode('&', $result));
279 }
280 /**
281 * Creates a new URI with multiple specific query string values.
282 *
283 * It has the same behavior as withQueryValue() but for an associative array of key => value.
284 *
285 * @param UriInterface $uri URI to use as a base.
286 * @param (string|null)[] $keyValueArray Associative array of key and values
287 */
288 public static function withQueryValues(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, array $keyValueArray) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
289 {
290 $result = self::getFilteredQueryString($uri, \array_keys($keyValueArray));
291 foreach ($keyValueArray as $key => $value) {
292 $result[] = self::generateQueryString((string) $key, $value !== null ? self::stringifyQueryValue($value) : null);
293 }
294 return $uri->withQuery(\implode('&', $result));
295 }
296 /**
297 * Stringifies a non-null query value, deprecating non-string values that
298 * guzzlehttp/psr7 3.0 will reject. Non-finite floats are normalized to the
299 * strings PHP coerces them to, as implicit coercion of NAN emits a warning
300 * on PHP 8.5.
301 *
302 * @param mixed $value
303 */
304 private static function stringifyQueryValue($value) : string
305 {
306 if (!\is_string($value)) {
307 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/psr7', '2.12', 'Passing %s to Uri::withQueryValues() is deprecated; cast it to a string. guzzlehttp/psr7 3.0 will only accept string or null query values.', \gettype($value));
308 if (\is_float($value) && !\is_finite($value)) {
309 return \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF');
310 }
311 }
312 return (string) $value;
313 }
314 /**
315 * Creates a URI from a hash of `parse_url` components.
316 *
317 * @see https://www.php.net/manual/en/function.parse-url.php
318 *
319 * @throws MalformedUriException If the components do not form a valid URI.
320 */
321 public static function fromParts(array $parts) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
322 {
323 $uri = new self();
324 try {
325 $uri->applyParts($parts);
326 $uri->validateState();
327 } catch (\YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException $e) {
328 throw $e;
329 } catch (\InvalidArgumentException $e) {
330 throw new \YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException($e->getMessage(), 0, $e);
331 }
332 return $uri;
333 }
334 /**
335 * @throws \InvalidArgumentException If the host is invalid.
336 *
337 * @internal
338 */
339 public static function assertValidHost(string $host) : void
340 {
341 if ($host === '') {
342 return;
343 }
344 if (\preg_match('/[\\x00-\\x20\\x7F]/', $host)) {
345 throw new \InvalidArgumentException(\sprintf('Invalid host: "%s"', $host));
346 }
347 }
348 public function getScheme() : string
349 {
350 return $this->scheme;
351 }
352 public function getAuthority() : string
353 {
354 $authority = $this->host;
355 if ($this->userInfo !== '') {
356 $authority = $this->userInfo . '@' . $authority;
357 }
358 if ($this->port !== null) {
359 $authority .= ':' . $this->port;
360 }
361 return $authority;
362 }
363 public function getUserInfo() : string
364 {
365 return $this->userInfo;
366 }
367 public function getHost() : string
368 {
369 return $this->host;
370 }
371 public function getPort() : ?int
372 {
373 return $this->port;
374 }
375 public function getPath() : string
376 {
377 return $this->path;
378 }
379 public function getQuery() : string
380 {
381 return $this->query;
382 }
383 public function getFragment() : string
384 {
385 return $this->fragment;
386 }
387 public function withScheme($scheme) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
388 {
389 $scheme = $this->filterScheme($scheme);
390 if ($this->scheme === $scheme) {
391 return $this;
392 }
393 $new = clone $this;
394 $new->scheme = $scheme;
395 $new->removeDefaultPort();
396 $new->validateState();
397 return $new;
398 }
399 public function withUserInfo($user, $password = null) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
400 {
401 $info = $this->filterUserInfoComponent($user);
402 if ($password !== null) {
403 $info .= ':' . $this->filterUserInfoComponent($password);
404 }
405 if ($this->userInfo === $info) {
406 return $this;
407 }
408 $new = clone $this;
409 $new->userInfo = $info;
410 $new->validateState();
411 return $new;
412 }
413 public function withHost($host) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
414 {
415 $host = $this->filterHost($host);
416 if ($this->host === $host) {
417 return $this;
418 }
419 $new = clone $this;
420 $new->host = $host;
421 $new->validateState();
422 return $new;
423 }
424 public function withPort($port) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
425 {
426 if ($port !== null && !\is_int($port)) {
427 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/psr7', '2.11', 'Passing %s to UriInterface::withPort() is deprecated; guzzlehttp/psr7 3.0 requires int|null.', \get_debug_type($port));
428 }
429 $port = $this->filterPort($port);
430 if ($this->port === $port) {
431 return $this;
432 }
433 $new = clone $this;
434 $new->port = $port;
435 $new->removeDefaultPort();
436 $new->validateState();
437 return $new;
438 }
439 public function withPath($path) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
440 {
441 $path = $this->filterPath($path);
442 if ($this->path === $path) {
443 return $this;
444 }
445 $new = clone $this;
446 $new->path = $path;
447 $new->validateState();
448 return $new;
449 }
450 public function withQuery($query) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
451 {
452 $query = $this->filterQueryAndFragment($query);
453 if ($this->query === $query) {
454 return $this;
455 }
456 $new = clone $this;
457 $new->query = $query;
458 return $new;
459 }
460 public function withFragment($fragment) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
461 {
462 $fragment = $this->filterQueryAndFragment($fragment);
463 if ($this->fragment === $fragment) {
464 return $this;
465 }
466 $new = clone $this;
467 $new->fragment = $fragment;
468 return $new;
469 }
470 public function jsonSerialize() : string
471 {
472 return $this->__toString();
473 }
474 /**
475 * Apply parse_url parts to a URI.
476 *
477 * @param array $parts Array of parse_url parts to apply.
478 */
479 private function applyParts(array $parts) : void
480 {
481 $this->scheme = isset($parts['scheme']) ? $this->filterScheme($parts['scheme']) : '';
482 $this->userInfo = isset($parts['user']) ? $this->filterUserInfoComponent($parts['user']) : '';
483 $this->host = isset($parts['host']) ? $this->filterHost($parts['host']) : '';
484 $this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null;
485 $this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
486 $this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : '';
487 $this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : '';
488 if (isset($parts['pass'])) {
489 $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']);
490 }
491 $this->removeDefaultPort();
492 }
493 /**
494 * @param mixed $scheme
495 *
496 * @throws \InvalidArgumentException If the scheme is invalid.
497 */
498 private function filterScheme($scheme) : string
499 {
500 if (!\is_string($scheme)) {
501 throw new \InvalidArgumentException('Scheme must be a string');
502 }
503 $scheme = \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
504 if ($scheme !== '' && !\preg_match('/^[a-z][a-z0-9.+-]*$/D', $scheme)) {
505 \YoastSEO_Vendor\trigger_deprecation('guzzlehttp/psr7', '2.11', 'Passing "%s" as a URI scheme is deprecated; guzzlehttp/psr7 3.0 requires URI schemes to match RFC 3986 syntax and begin with a letter.', $scheme);
506 }
507 return $scheme;
508 }
509 /**
510 * @param mixed $component
511 *
512 * @throws \InvalidArgumentException If the user info is invalid.
513 */
514 private function filterUserInfoComponent($component) : string
515 {
516 if (!\is_string($component)) {
517 throw new \InvalidArgumentException('User info must be a string');
518 }
519 return \preg_replace_callback('/(?:[^%' . \YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc3986::CHAR_UNRESERVED . \YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc3986::CHAR_SUB_DELIMS . ']+|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $component);
520 }
521 /**
522 * @param mixed $host
523 *
524 * @throws \InvalidArgumentException If the host is invalid.
525 */
526 private function filterHost($host) : string
527 {
528 if (!\is_string($host)) {
529 throw new \InvalidArgumentException('Host must be a string');
530 }
531 $host = \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
532 self::assertValidHost($host);
533 return $host;
534 }
535 /**
536 * @param mixed $port
537 *
538 * @throws \InvalidArgumentException If the port is invalid.
539 */
540 private function filterPort($port) : ?int
541 {
542 if ($port === null) {
543 return null;
544 }
545 $port = (int) $port;
546 if (0 > $port || 0xffff < $port) {
547 throw new \InvalidArgumentException(\sprintf('Invalid port: %d. Must be between 0 and 65535', $port));
548 }
549 return $port;
550 }
551 /**
552 * @param (string|int)[] $keys
553 *
554 * @return string[]
555 */
556 private static function getFilteredQueryString(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, array $keys) : array
557 {
558 $current = $uri->getQuery();
559 if ($current === '') {
560 return [];
561 }
562 $decodedKeys = \array_map(function ($k) : string {
563 return \rawurldecode((string) $k);
564 }, $keys);
565 return \array_filter(\explode('&', $current), function ($part) use($decodedKeys) {
566 return !\in_array(\rawurldecode(\explode('=', $part)[0]), $decodedKeys, \true);
567 });
568 }
569 private static function generateQueryString(string $key, ?string $value) : string
570 {
571 // Query string separators ("=", "&") and literal plus signs ("+") within the
572 // key or value need to be encoded
573 // (while preventing double-encoding) before setting the query string. All other
574 // chars that need percent-encoding will be encoded by withQuery().
575 $queryString = \strtr($key, self::QUERY_SEPARATORS_REPLACEMENT);
576 if ($value !== null) {
577 $queryString .= '=' . \strtr($value, self::QUERY_SEPARATORS_REPLACEMENT);
578 }
579 return $queryString;
580 }
581 private function removeDefaultPort() : void
582 {
583 if ($this->port !== null && self::isDefaultPort($this)) {
584 $this->port = null;
585 }
586 }
587 /**
588 * Filters the path of a URI
589 *
590 * @param mixed $path
591 *
592 * @throws \InvalidArgumentException If the path is invalid.
593 */
594 private function filterPath($path) : string
595 {
596 if (!\is_string($path)) {
597 throw new \InvalidArgumentException('Path must be a string');
598 }
599 return \preg_replace_callback('/(?:[^' . \YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc3986::CHAR_UNRESERVED . \YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc3986::CHAR_SUB_DELIMS . '%:@\\/]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $path);
600 }
601 /**
602 * Filters the query string or fragment of a URI.
603 *
604 * @param mixed $str
605 *
606 * @throws \InvalidArgumentException If the query or fragment is invalid.
607 */
608 private function filterQueryAndFragment($str) : string
609 {
610 if (!\is_string($str)) {
611 throw new \InvalidArgumentException('Query and fragment must be a string');
612 }
613 return \preg_replace_callback('/(?:[^' . \YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc3986::CHAR_UNRESERVED . \YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc3986::CHAR_SUB_DELIMS . '%:@\\/\\?]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $str);
614 }
615 private function rawurlencodeMatchZero(array $match) : string
616 {
617 return \rawurlencode($match[0]);
618 }
619 private function validateState() : void
620 {
621 if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
622 $this->host = self::HTTP_DEFAULT_HOST;
623 }
624 if ($this->getAuthority() === '') {
625 if (0 === \strpos($this->path, '//')) {
626 throw new \YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException('The path of a URI without an authority must not start with two slashes "//"');
627 }
628 if ($this->scheme === '' && \false !== \strpos(\explode('/', $this->path, 2)[0], ':')) {
629 throw new \YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException('A relative URI must not have a path beginning with a segment containing a colon');
630 }
631 }
632 }
633 }
634