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