PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 27.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v27.5
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 / Utils.php

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

387 lines 15.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\Psr\Http\Message\RequestInterface;
7 use YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface;
8 use YoastSEO_Vendor\Psr\Http\Message\StreamInterface;
9 use YoastSEO_Vendor\Psr\Http\Message\UriInterface;
10 final class Utils
11 {
12 /**
13 * Remove the items given by the keys, case insensitively from the data.
14 *
15 * @param (string|int)[] $keys
16 */
17 public static function caselessRemove(array $keys, array $data) : array
18 {
19 $result = [];
20 foreach ($keys as &$key) {
21 $key = \strtolower((string) $key);
22 }
23 foreach ($data as $k => $v) {
24 if (!\in_array(\strtolower((string) $k), $keys)) {
25 $result[$k] = $v;
26 }
27 }
28 return $result;
29 }
30 /**
31 * Copy the contents of a stream into another stream until the given number
32 * of bytes have been read.
33 *
34 * @param StreamInterface $source Stream to read from
35 * @param StreamInterface $dest Stream to write to
36 * @param int $maxLen Maximum number of bytes to read. Pass -1
37 * to read the entire stream.
38 *
39 * @throws \RuntimeException on error.
40 */
41 public static function copyToStream(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $source, \YoastSEO_Vendor\Psr\Http\Message\StreamInterface $dest, int $maxLen = -1) : void
42 {
43 $bufferSize = 8192;
44 if ($maxLen === -1) {
45 while (!$source->eof()) {
46 if (!$dest->write($source->read($bufferSize))) {
47 break;
48 }
49 }
50 } else {
51 $remaining = $maxLen;
52 while ($remaining > 0 && !$source->eof()) {
53 $buf = $source->read(\min($bufferSize, $remaining));
54 $len = \strlen($buf);
55 if (!$len) {
56 break;
57 }
58 $remaining -= $len;
59 $dest->write($buf);
60 }
61 }
62 }
63 /**
64 * Copy the contents of a stream into a string until the given number of
65 * bytes have been read.
66 *
67 * @param StreamInterface $stream Stream to read
68 * @param int $maxLen Maximum number of bytes to read. Pass -1
69 * to read the entire stream.
70 *
71 * @throws \RuntimeException on error.
72 */
73 public static function copyToString(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, int $maxLen = -1) : string
74 {
75 $buffer = '';
76 if ($maxLen === -1) {
77 while (!$stream->eof()) {
78 $buf = $stream->read(1048576);
79 if ($buf === '') {
80 break;
81 }
82 $buffer .= $buf;
83 }
84 return $buffer;
85 }
86 $len = 0;
87 while (!$stream->eof() && $len < $maxLen) {
88 $buf = $stream->read($maxLen - $len);
89 if ($buf === '') {
90 break;
91 }
92 $buffer .= $buf;
93 $len = \strlen($buffer);
94 }
95 return $buffer;
96 }
97 /**
98 * Calculate a hash of a stream.
99 *
100 * This method reads the entire stream to calculate a rolling hash, based
101 * on PHP's `hash_init` functions.
102 *
103 * @param StreamInterface $stream Stream to calculate the hash for
104 * @param string $algo Hash algorithm (e.g. md5, crc32, etc)
105 * @param bool $rawOutput Whether or not to use raw output
106 *
107 * @throws \RuntimeException on error.
108 */
109 public static function hash(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, string $algo, bool $rawOutput = \false) : string
110 {
111 $pos = $stream->tell();
112 if ($pos > 0) {
113 $stream->rewind();
114 }
115 $ctx = \hash_init($algo);
116 while (!$stream->eof()) {
117 \hash_update($ctx, $stream->read(1048576));
118 }
119 $out = \hash_final($ctx, $rawOutput);
120 $stream->seek($pos);
121 return $out;
122 }
123 /**
124 * Clone and modify a request with the given changes.
125 *
126 * This method is useful for reducing the number of clones needed to mutate
127 * a message.
128 *
129 * The changes can be one of:
130 * - method: (string) Changes the HTTP method.
131 * - set_headers: (array) Sets the given headers.
132 * - remove_headers: (array) Remove the given headers.
133 * - body: (mixed) Sets the given body.
134 * - uri: (UriInterface) Set the URI.
135 * - query: (string) Set the query string value of the URI.
136 * - version: (string) Set the protocol version.
137 *
138 * @param RequestInterface $request Request to clone and modify.
139 * @param array $changes Changes to apply.
140 */
141 public static function modifyRequest(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $changes) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface
142 {
143 if (!$changes) {
144 return $request;
145 }
146 $headers = $request->getHeaders();
147 if (!isset($changes['uri'])) {
148 $uri = $request->getUri();
149 } else {
150 // Remove the host header if one is on the URI
151 if ($host = $changes['uri']->getHost()) {
152 $changes['set_headers']['Host'] = $host;
153 if ($port = $changes['uri']->getPort()) {
154 $standardPorts = ['http' => 80, 'https' => 443];
155 $scheme = $changes['uri']->getScheme();
156 if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
157 $changes['set_headers']['Host'] .= ':' . $port;
158 }
159 }
160 }
161 $uri = $changes['uri'];
162 }
163 if (!empty($changes['remove_headers'])) {
164 $headers = self::caselessRemove($changes['remove_headers'], $headers);
165 }
166 if (!empty($changes['set_headers'])) {
167 $headers = self::caselessRemove(\array_keys($changes['set_headers']), $headers);
168 $headers = $changes['set_headers'] + $headers;
169 }
170 if (isset($changes['query'])) {
171 $uri = $uri->withQuery($changes['query']);
172 }
173 if ($request instanceof \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface) {
174 $new = (new \YoastSEO_Vendor\GuzzleHttp\Psr7\ServerRequest($changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $changes['version'] ?? $request->getProtocolVersion(), $request->getServerParams()))->withParsedBody($request->getParsedBody())->withQueryParams($request->getQueryParams())->withCookieParams($request->getCookieParams())->withUploadedFiles($request->getUploadedFiles());
175 foreach ($request->getAttributes() as $key => $value) {
176 $new = $new->withAttribute($key, $value);
177 }
178 return $new;
179 }
180 return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request($changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $changes['version'] ?? $request->getProtocolVersion());
181 }
182 /**
183 * Read a line from the stream up to the maximum allowed buffer length.
184 *
185 * @param StreamInterface $stream Stream to read from
186 * @param int|null $maxLength Maximum buffer length
187 */
188 public static function readLine(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, ?int $maxLength = null) : string
189 {
190 $buffer = '';
191 $size = 0;
192 while (!$stream->eof()) {
193 if ('' === ($byte = $stream->read(1))) {
194 return $buffer;
195 }
196 $buffer .= $byte;
197 // Break when a new line is found or the max length - 1 is reached
198 if ($byte === "\n" || ++$size === $maxLength - 1) {
199 break;
200 }
201 }
202 return $buffer;
203 }
204 /**
205 * Redact the password in the user info part of a URI.
206 */
207 public static function redactUserInfo(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
208 {
209 $userInfo = $uri->getUserInfo();
210 if (\false !== ($pos = \strpos($userInfo, ':'))) {
211 return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***');
212 }
213 return $uri;
214 }
215 /**
216 * Create a new stream based on the input type.
217 *
218 * Options is an associative array that can contain the following keys:
219 * - metadata: Array of custom metadata.
220 * - size: Size of the stream.
221 *
222 * This method accepts the following `$resource` types:
223 * - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
224 * - `string`: Creates a stream object that uses the given string as the contents.
225 * - `resource`: Creates a stream object that wraps the given PHP stream resource.
226 * - `Iterator`: If the provided value implements `Iterator`, then a read-only
227 * stream object will be created that wraps the given iterable. Each time the
228 * stream is read from, data from the iterator will fill a buffer and will be
229 * continuously called until the buffer is equal to the requested read size.
230 * Subsequent read calls will first read from the buffer and then call `next`
231 * on the underlying iterator until it is exhausted.
232 * - `object` with `__toString()`: If the object has the `__toString()` method,
233 * the object will be cast to a string and then a stream will be returned that
234 * uses the string value.
235 * - `NULL`: When `null` is passed, an empty stream object is returned.
236 * - `callable` When a callable is passed, a read-only stream object will be
237 * created that invokes the given callable. The callable is invoked with the
238 * number of suggested bytes to read. The callable can return any number of
239 * bytes, but MUST return `false` when there is no more data to return. The
240 * stream object that wraps the callable will invoke the callable until the
241 * number of requested bytes are available. Any additional bytes will be
242 * buffered and used in subsequent reads.
243 *
244 * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
245 * @param array{size?: int, metadata?: array} $options Additional options
246 *
247 * @throws \InvalidArgumentException if the $resource arg is not valid.
248 */
249 public static function streamFor($resource = '', array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface
250 {
251 if (\is_scalar($resource)) {
252 $stream = self::tryFopen('php://temp', 'r+');
253 if ($resource !== '') {
254 \fwrite($stream, (string) $resource);
255 \fseek($stream, 0);
256 }
257 return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($stream, $options);
258 }
259 switch (\gettype($resource)) {
260 case 'resource':
261 /*
262 * The 'php://input' is a special stream with quirks and inconsistencies.
263 * We avoid using that stream by reading it into php://temp
264 */
265 /** @var resource $resource */
266 if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') {
267 $stream = self::tryFopen('php://temp', 'w+');
268 \stream_copy_to_stream($resource, $stream);
269 \fseek($stream, 0);
270 $resource = $stream;
271 }
272 return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($resource, $options);
273 case 'object':
274 /** @var object $resource */
275 if ($resource instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) {
276 return $resource;
277 } elseif ($resource instanceof \Iterator) {
278 return new \YoastSEO_Vendor\GuzzleHttp\Psr7\PumpStream(function () use($resource) {
279 if (!$resource->valid()) {
280 return \false;
281 }
282 $result = $resource->current();
283 $resource->next();
284 return $result;
285 }, $options);
286 } elseif (\method_exists($resource, '__toString')) {
287 return self::streamFor((string) $resource, $options);
288 }
289 break;
290 case 'NULL':
291 return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream(self::tryFopen('php://temp', 'r+'), $options);
292 }
293 if (\is_callable($resource)) {
294 return new \YoastSEO_Vendor\GuzzleHttp\Psr7\PumpStream($resource, $options);
295 }
296 throw new \InvalidArgumentException('Invalid resource type: ' . \gettype($resource));
297 }
298 /**
299 * Safely opens a PHP stream resource using a filename.
300 *
301 * When fopen fails, PHP normally raises a warning. This function adds an
302 * error handler that checks for errors and throws an exception instead.
303 *
304 * @param string $filename File to open
305 * @param string $mode Mode used to open the file
306 *
307 * @return resource
308 *
309 * @throws \RuntimeException if the file cannot be opened
310 */
311 public static function tryFopen(string $filename, string $mode)
312 {
313 $ex = null;
314 \set_error_handler(static function (int $errno, string $errstr) use($filename, $mode, &$ex) : bool {
315 $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $errstr));
316 return \true;
317 });
318 try {
319 /** @var resource $handle */
320 $handle = \fopen($filename, $mode);
321 } catch (\Throwable $e) {
322 $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $e->getMessage()), 0, $e);
323 }
324 \restore_error_handler();
325 if ($ex) {
326 /** @var \RuntimeException $ex */
327 throw $ex;
328 }
329 return $handle;
330 }
331 /**
332 * Safely gets the contents of a given stream.
333 *
334 * When stream_get_contents fails, PHP normally raises a warning. This
335 * function adds an error handler that checks for errors and throws an
336 * exception instead.
337 *
338 * @param resource $stream
339 *
340 * @throws \RuntimeException if the stream cannot be read
341 */
342 public static function tryGetContents($stream) : string
343 {
344 $ex = null;
345 \set_error_handler(static function (int $errno, string $errstr) use(&$ex) : bool {
346 $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $errstr));
347 return \true;
348 });
349 try {
350 /** @var string|false $contents */
351 $contents = \stream_get_contents($stream);
352 if ($contents === \false) {
353 $ex = new \RuntimeException('Unable to read stream contents');
354 }
355 } catch (\Throwable $e) {
356 $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $e->getMessage()), 0, $e);
357 }
358 \restore_error_handler();
359 if ($ex) {
360 /** @var \RuntimeException $ex */
361 throw $ex;
362 }
363 return $contents;
364 }
365 /**
366 * Returns a UriInterface for the given value.
367 *
368 * This function accepts a string or UriInterface and returns a
369 * UriInterface for the given value. If the value is already a
370 * UriInterface, it is returned as-is.
371 *
372 * @param string|UriInterface $uri
373 *
374 * @throws \InvalidArgumentException
375 */
376 public static function uriFor($uri) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
377 {
378 if ($uri instanceof \YoastSEO_Vendor\Psr\Http\Message\UriInterface) {
379 return $uri;
380 }
381 if (\is_string($uri)) {
382 return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri($uri);
383 }
384 throw new \InvalidArgumentException('URI must be a string or UriInterface');
385 }
386 }
387