Passed
Pull Request — master (#2)
by
unknown
15:51
created

copy_to_string()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 16
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 28
rs 8.8333
1
<?php
2
namespace GuzzleHttp\Psr7;
3
4
use Psr\Http\Message\MessageInterface;
5
use Psr\Http\Message\RequestInterface;
6
use Psr\Http\Message\ResponseInterface;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr\Http\Message\StreamInterface;
9
use Psr\Http\Message\UriInterface;
10
11
/**
12
 * Returns the string representation of an HTTP message.
13
 *
14
 * @param MessageInterface $message Message to convert to a string.
15
 *
16
 * @return string
17
 */
18
function str(MessageInterface $message)
19
{
20
    if ($message instanceof RequestInterface) {
21
        $msg = trim($message->getMethod() . ' '
22
                . $message->getRequestTarget())
23
            . ' HTTP/' . $message->getProtocolVersion();
24
        if (!$message->hasHeader('host')) {
25
            $msg .= "\r\nHost: " . $message->getUri()->getHost();
26
        }
27
    } elseif ($message instanceof ResponseInterface) {
28
        $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
29
            . $message->getStatusCode() . ' '
30
            . $message->getReasonPhrase();
31
    } else {
32
        throw new \InvalidArgumentException('Unknown message type');
33
    }
34
35
    foreach ($message->getHeaders() as $name => $values) {
36
        $msg .= "\r\n{$name}: " . implode(', ', $values);
37
    }
38
39
    return "{$msg}\r\n\r\n" . $message->getBody();
40
}
41
42
/**
43
 * Returns a UriInterface for the given value.
44
 *
45
 * This function accepts a string or {@see Psr\Http\Message\UriInterface} and
46
 * returns a UriInterface for the given value. If the value is already a
47
 * `UriInterface`, it is returned as-is.
48
 *
49
 * @param string|UriInterface $uri
50
 *
51
 * @return UriInterface
52
 * @throws \InvalidArgumentException
53
 */
54
function uri_for($uri)
55
{
56
    if ($uri instanceof UriInterface) {
57
        return $uri;
58
    } elseif (is_string($uri)) {
0 ignored issues
show
introduced by
The condition is_string($uri) is always true.
Loading history...
59
        return new Uri($uri);
60
    }
61
62
    throw new \InvalidArgumentException('URI must be a string or UriInterface');
63
}
64
65
/**
66
 * Create a new stream based on the input type.
67
 *
68
 * Options is an associative array that can contain the following keys:
69
 * - metadata: Array of custom metadata.
70
 * - size: Size of the stream.
71
 *
72
 * @param resource|string|null|int|float|bool|StreamInterface|callable $resource Entity body data
73
 * @param array                                                        $options  Additional options
74
 *
75
 * @return Stream
76
 * @throws \InvalidArgumentException if the $resource arg is not valid.
77
 */
78
function stream_for($resource = '', array $options = [])
79
{
80
    if (is_scalar($resource)) {
81
        $stream = fopen('php://temp', 'r+');
82
        if ($resource !== '') {
83
            fwrite($stream, $resource);
0 ignored issues
show
Bug introduced by
It seems like $resource can also be of type boolean; however, parameter $data of fwrite() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

83
            fwrite($stream, /** @scrutinizer ignore-type */ $resource);
Loading history...
84
            fseek($stream, 0);
85
        }
86
        return new Stream($stream, $options);
87
    }
88
89
    switch (gettype($resource)) {
90
        case 'resource':
91
            return new Stream($resource, $options);
92
        case 'object':
93
            if ($resource instanceof StreamInterface) {
0 ignored issues
show
introduced by
$resource is always a sub-type of Psr\Http\Message\StreamInterface.
Loading history...
94
                return $resource;
95
            } elseif ($resource instanceof \Iterator) {
96
                return new PumpStream(function () use ($resource) {
97
                    if (!$resource->valid()) {
98
                        return false;
99
                    }
100
                    $result = $resource->current();
101
                    $resource->next();
102
                    return $result;
103
                }, $options);
104
            } elseif (method_exists($resource, '__toString')) {
105
                return stream_for((string) $resource, $options);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Psr7\stream_for() has been deprecated: stream_for will be removed in guzzlehttp/psr7:2.0. Use Utils::streamFor instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

105
                return /** @scrutinizer ignore-deprecated */ stream_for((string) $resource, $options);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
106
            }
107
            break;
108
        case 'NULL':
109
            return new Stream(fopen('php://temp', 'r+'), $options);
110
    }
111
112
    if (is_callable($resource)) {
113
        return new PumpStream($resource, $options);
0 ignored issues
show
Bug introduced by
It seems like $resource can also be of type null; however, parameter $source of GuzzleHttp\Psr7\PumpStream::__construct() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
        return new PumpStream(/** @scrutinizer ignore-type */ $resource, $options);
Loading history...
Bug Best Practice introduced by
The expression return new GuzzleHttp\Ps...am($resource, $options) returns the type GuzzleHttp\Psr7\PumpStream which is incompatible with the documented return type GuzzleHttp\Psr7\Stream.
Loading history...
114
    }
115
116
    throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
117
}
118
119
/**
120
 * Parse an array of header values containing ";" separated data into an
121
 * array of associative arrays representing the header key value pair
122
 * data of the header. When a parameter does not contain a value, but just
123
 * contains a key, this function will inject a key with a '' string value.
124
 *
125
 * @param string|array $header Header to parse into components.
126
 *
127
 * @return array Returns the parsed header values.
128
 */
129
function parse_header($header)
130
{
131
    static $trimmed = "\"'  \n\t\r";
132
    $params = $matches = [];
133
134
    foreach (normalize_header($header) as $val) {
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Psr7\normalize_header() has been deprecated: normalize_header will be removed in guzzlehttp/psr7:2.0. Use Header::normalize instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

134
    foreach (/** @scrutinizer ignore-deprecated */ normalize_header($header) as $val) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
135
        $part = [];
136
        foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
137
            if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
138
                $m = $matches[0];
139
                if (isset($m[1])) {
140
                    $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
141
                } else {
142
                    $part[] = trim($m[0], $trimmed);
143
                }
144
            }
145
        }
146
        if ($part) {
147
            $params[] = $part;
148
        }
149
    }
150
151
    return $params;
152
}
153
154
/**
155
 * Converts an array of header values that may contain comma separated
156
 * headers into an array of headers with no comma separated values.
157
 *
158
 * @param string|array $header Header to normalize.
159
 *
160
 * @return array Returns the normalized header field values.
161
 */
162
function normalize_header($header)
163
{
164
    if (!is_array($header)) {
165
        return array_map('trim', explode(',', $header));
166
    }
167
168
    $result = [];
169
    foreach ($header as $value) {
170
        foreach ((array) $value as $v) {
171
            if (strpos($v, ',') === false) {
172
                $result[] = $v;
173
                continue;
174
            }
175
            foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
176
                $result[] = trim($vv);
177
            }
178
        }
179
    }
180
181
    return $result;
182
}
183
184
/**
185
 * Clone and modify a request with the given changes.
186
 *
187
 * The changes can be one of:
188
 * - method: (string) Changes the HTTP method.
189
 * - set_headers: (array) Sets the given headers.
190
 * - remove_headers: (array) Remove the given headers.
191
 * - body: (mixed) Sets the given body.
192
 * - uri: (UriInterface) Set the URI.
193
 * - query: (string) Set the query string value of the URI.
194
 * - version: (string) Set the protocol version.
195
 *
196
 * @param RequestInterface $request Request to clone and modify.
197
 * @param array            $changes Changes to apply.
198
 *
199
 * @return RequestInterface
200
 */
201
function modify_request(RequestInterface $request, array $changes)
202
{
203
    if (!$changes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $changes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
204
        return $request;
205
    }
206
207
    $headers = $request->getHeaders();
208
209
    if (!isset($changes['uri'])) {
210
        $uri = $request->getUri();
211
    } else {
212
        // Remove the host header if one is on the URI
213
        if ($host = $changes['uri']->getHost()) {
214
            $changes['set_headers']['Host'] = $host;
215
216
            if ($port = $changes['uri']->getPort()) {
217
                $standardPorts = ['http' => 80, 'https' => 443];
218
                $scheme = $changes['uri']->getScheme();
219
                if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
220
                    $changes['set_headers']['Host'] .= ':'.$port;
221
                }
222
            }
223
        }
224
        $uri = $changes['uri'];
225
    }
226
227
    if (!empty($changes['remove_headers'])) {
228
        $headers = _caseless_remove($changes['remove_headers'], $headers);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Psr7\_caseless_remove() has been deprecated: _caseless_remove will be removed in guzzlehttp/psr7:2.0. Use Utils::caselessRemove instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

228
        $headers = /** @scrutinizer ignore-deprecated */ _caseless_remove($changes['remove_headers'], $headers);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
229
    }
230
231
    if (!empty($changes['set_headers'])) {
232
        $headers = _caseless_remove(array_keys($changes['set_headers']), $headers);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Psr7\_caseless_remove() has been deprecated: _caseless_remove will be removed in guzzlehttp/psr7:2.0. Use Utils::caselessRemove instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

232
        $headers = /** @scrutinizer ignore-deprecated */ _caseless_remove(array_keys($changes['set_headers']), $headers);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
233
        $headers = $changes['set_headers'] + $headers;
234
    }
235
236
    if (isset($changes['query'])) {
237
        $uri = $uri->withQuery($changes['query']);
238
    }
239
240
    if ($request instanceof ServerRequestInterface) {
241
        return new ServerRequest(
242
            isset($changes['method']) ? $changes['method'] : $request->getMethod(),
243
            $uri,
244
            $headers,
245
            isset($changes['body']) ? $changes['body'] : $request->getBody(),
246
            isset($changes['version'])
247
                ? $changes['version']
248
                : $request->getProtocolVersion(),
249
            $request->getServerParams()
250
        );
251
    }
252
253
    return new Request(
254
        isset($changes['method']) ? $changes['method'] : $request->getMethod(),
255
        $uri,
256
        $headers,
257
        isset($changes['body']) ? $changes['body'] : $request->getBody(),
258
        isset($changes['version'])
259
            ? $changes['version']
260
            : $request->getProtocolVersion()
261
    );
262
}
263
264
/**
265
 * Attempts to rewind a message body and throws an exception on failure.
266
 *
267
 * The body of the message will only be rewound if a call to `tell()` returns a
268
 * value other than `0`.
269
 *
270
 * @param MessageInterface $message Message to rewind
271
 *
272
 * @throws \RuntimeException
273
 */
274
function rewind_body(MessageInterface $message)
275
{
276
    $body = $message->getBody();
277
278
    if ($body->tell()) {
279
        $body->rewind();
280
    }
281
}
282
283
/**
284
 * Safely opens a PHP stream resource using a filename.
285
 *
286
 * When fopen fails, PHP normally raises a warning. This function adds an
287
 * error handler that checks for errors and throws an exception instead.
288
 *
289
 * @param string $filename File to open
290
 * @param string $mode     Mode used to open the file
291
 *
292
 * @return resource
293
 * @throws \RuntimeException if the file cannot be opened
294
 */
295
function try_fopen($filename, $mode)
296
{
297
    $ex = null;
298
    set_error_handler(function () use ($filename, $mode, &$ex) {
299
        $ex = new \RuntimeException(sprintf(
300
            'Unable to open %s using mode %s: %s',
301
            $filename,
302
            $mode,
303
            func_get_args()[1]
304
        ));
305
    });
306
307
    $handle = fopen($filename, $mode);
308
    restore_error_handler();
309
310
    if ($ex) {
311
        /** @var $ex \RuntimeException */
312
        throw $ex;
313
    }
314
315
    return $handle;
316
}
317
318
/**
319
 * Copy the contents of a stream into a string until the given number of
320
 * bytes have been read.
321
 *
322
 * @param StreamInterface $stream Stream to read
323
 * @param int             $maxLen Maximum number of bytes to read. Pass -1
324
 *                                to read the entire stream.
325
 * @return string
326
 * @throws \RuntimeException on error.
327
 */
328
function copy_to_string(StreamInterface $stream, $maxLen = -1)
329
{
330
    $buffer = '';
331
332
    if ($maxLen === -1) {
333
        while (!$stream->eof()) {
334
            $buf = $stream->read(1048576);
335
            // Using a loose equality here to match on '' and false.
336
            if ($buf == null) {
337
                break;
338
            }
339
            $buffer .= $buf;
340
        }
341
        return $buffer;
342
    }
343
344
    $len = 0;
345
    while (!$stream->eof() && $len < $maxLen) {
346
        $buf = $stream->read($maxLen - $len);
347
        // Using a loose equality here to match on '' and false.
348
        if ($buf == null) {
349
            break;
350
        }
351
        $buffer .= $buf;
352
        $len = strlen($buffer);
353
    }
354
355
    return $buffer;
356
}
357
358
/**
359
 * Copy the contents of a stream into another stream until the given number
360
 * of bytes have been read.
361
 *
362
 * @param StreamInterface $source Stream to read from
363
 * @param StreamInterface $dest   Stream to write to
364
 * @param int             $maxLen Maximum number of bytes to read. Pass -1
365
 *                                to read the entire stream.
366
 *
367
 * @throws \RuntimeException on error.
368
 */
369
function copy_to_stream(
370
    StreamInterface $source,
371
    StreamInterface $dest,
372
    $maxLen = -1
373
) {
374
    $bufferSize = 8192;
375
376
    if ($maxLen === -1) {
377
        while (!$source->eof()) {
378
            if (!$dest->write($source->read($bufferSize))) {
379
                break;
380
            }
381
        }
382
    } else {
383
        $remaining = $maxLen;
384
        while ($remaining > 0 && !$source->eof()) {
385
            $buf = $source->read(min($bufferSize, $remaining));
386
            $len = strlen($buf);
387
            if (!$len) {
388
                break;
389
            }
390
            $remaining -= $len;
391
            $dest->write($buf);
392
        }
393
    }
394
}
395
396
/**
397
 * Calculate a hash of a Stream
398
 *
399
 * @param StreamInterface $stream    Stream to calculate the hash for
400
 * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
401
 * @param bool            $rawOutput Whether or not to use raw output
402
 *
403
 * @return string Returns the hash of the stream
404
 * @throws \RuntimeException on error.
405
 */
406
function hash(
407
    StreamInterface $stream,
408
    $algo,
409
    $rawOutput = false
410
) {
411
    $pos = $stream->tell();
412
413
    if ($pos > 0) {
414
        $stream->rewind();
415
    }
416
417
    $ctx = hash_init($algo);
418
    while (!$stream->eof()) {
419
        hash_update($ctx, $stream->read(1048576));
420
    }
421
422
    $out = hash_final($ctx, (bool) $rawOutput);
423
    $stream->seek($pos);
424
425
    return $out;
426
}
427
428
/**
429
 * Read a line from the stream up to the maximum allowed buffer length
430
 *
431
 * @param StreamInterface $stream    Stream to read from
432
 * @param int             $maxLength Maximum buffer length
433
 *
434
 * @return string
435
 */
436
function readline(StreamInterface $stream, $maxLength = null)
437
{
438
    $buffer = '';
439
    $size = 0;
440
441
    while (!$stream->eof()) {
442
        // Using a loose equality here to match on '' and false.
443
        if (null == ($byte = $stream->read(1))) {
444
            return $buffer;
445
        }
446
        $buffer .= $byte;
447
        // Break when a new line is found or the max length - 1 is reached
448
        if ($byte === "\n" || ++$size === $maxLength - 1) {
449
            break;
450
        }
451
    }
452
453
    return $buffer;
454
}
455
456
/**
457
 * Parses a request message string into a request object.
458
 *
459
 * @param string $message Request message string.
460
 *
461
 * @return Request
462
 */
463
function parse_request($message)
464
{
465
    $data = _parse_message($message);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Psr7\_parse_message() has been deprecated: _parse_message will be removed in guzzlehttp/psr7:2.0. Use Message::parseMessage instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

465
    $data = /** @scrutinizer ignore-deprecated */ _parse_message($message);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
466
    $matches = [];
467
    if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
468
        throw new \InvalidArgumentException('Invalid request string');
469
    }
470
    $parts = explode(' ', $data['start-line'], 3);
471
    $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
472
473
    $request = new Request(
474
        $parts[0],
475
        $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1],
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Psr7\_parse_request_uri() has been deprecated: _parse_request_uri will be removed in guzzlehttp/psr7:2.0. Use Message::parseRequestUri instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

475
        $matches[1] === '/' ? /** @scrutinizer ignore-deprecated */ _parse_request_uri($parts[1], $data['headers']) : $parts[1],

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
476
        $data['headers'],
477
        $data['body'],
478
        $version
479
    );
480
481
    return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
482
}
483
484
/**
485
 * Parses a response message string into a response object.
486
 *
487
 * @param string $message Response message string.
488
 *
489
 * @return Response
490
 */
491
function parse_response($message)
492
{
493
    $data = _parse_message($message);
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Psr7\_parse_message() has been deprecated: _parse_message will be removed in guzzlehttp/psr7:2.0. Use Message::parseMessage instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

493
    $data = /** @scrutinizer ignore-deprecated */ _parse_message($message);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
494
    // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
495
    // between status-code and reason-phrase is required. But browsers accept
496
    // responses without space and reason as well.
497
    if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
498
        throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
499
    }
500
    $parts = explode(' ', $data['start-line'], 3);
501
502
    return new Response(
503
        $parts[1],
0 ignored issues
show
Bug introduced by
$parts[1] of type string is incompatible with the type integer expected by parameter $status of GuzzleHttp\Psr7\Response::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

503
        /** @scrutinizer ignore-type */ $parts[1],
Loading history...
504
        $data['headers'],
505
        $data['body'],
506
        explode('/', $parts[0])[1],
507
        isset($parts[2]) ? $parts[2] : null
508
    );
509
}
510
511
/**
512
 * Parse a query string into an associative array.
513
 *
514
 * If multiple values are found for the same key, the value of that key
515
 * value pair will become an array. This function does not parse nested
516
 * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
517
 * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
518
 *
519
 * @param string      $str         Query string to parse
520
 * @param bool|string $urlEncoding How the query string is encoded
521
 *
522
 * @return array
523
 */
524
function parse_query($str, $urlEncoding = true)
525
{
526
    $result = [];
527
528
    if ($str === '') {
529
        return $result;
530
    }
531
532
    if ($urlEncoding === true) {
533
        $decoder = function ($value) {
534
            return rawurldecode(str_replace('+', ' ', $value));
535
        };
536
    } elseif ($urlEncoding == PHP_QUERY_RFC3986) {
537
        $decoder = 'rawurldecode';
538
    } elseif ($urlEncoding == PHP_QUERY_RFC1738) {
539
        $decoder = 'urldecode';
540
    } else {
541
        $decoder = function ($str) { return $str; };
542
    }
543
544
    foreach (explode('&', $str) as $kvp) {
545
        $parts = explode('=', $kvp, 2);
546
        $key = $decoder($parts[0]);
547
        $value = isset($parts[1]) ? $decoder($parts[1]) : null;
548
        if (!isset($result[$key])) {
549
            $result[$key] = $value;
550
        } else {
551
            if (!is_array($result[$key])) {
552
                $result[$key] = [$result[$key]];
553
            }
554
            $result[$key][] = $value;
555
        }
556
    }
557
558
    return $result;
559
}
560
561
/**
562
 * Build a query string from an array of key value pairs.
563
 *
564
 * This function can use the return value of parse_query() to build a query
565
 * string. This function does not modify the provided keys when an array is
566
 * encountered (like http_build_query would).
567
 *
568
 * @param array     $params   Query string parameters.
569
 * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
570
 *                            to encode using RFC3986, or PHP_QUERY_RFC1738
571
 *                            to encode using RFC1738.
572
 * @return string
573
 */
574
function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
575
{
576
    if (!$params) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $params of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
577
        return '';
578
    }
579
580
    if ($encoding === false) {
581
        $encoder = function ($str) { return $str; };
582
    } elseif ($encoding === PHP_QUERY_RFC3986) {
583
        $encoder = 'rawurlencode';
584
    } elseif ($encoding === PHP_QUERY_RFC1738) {
585
        $encoder = 'urlencode';
586
    } else {
587
        throw new \InvalidArgumentException('Invalid type');
588
    }
589
590
    $qs = '';
591
    foreach ($params as $k => $v) {
592
        $k = $encoder($k);
593
        if (!is_array($v)) {
594
            $qs .= $k;
595
            if ($v !== null) {
596
                $qs .= '=' . $encoder($v);
597
            }
598
            $qs .= '&';
599
        } else {
600
            foreach ($v as $vv) {
601
                $qs .= $k;
602
                if ($vv !== null) {
603
                    $qs .= '=' . $encoder($vv);
604
                }
605
                $qs .= '&';
606
            }
607
        }
608
    }
609
610
    return $qs ? (string) substr($qs, 0, -1) : '';
611
}
612
613
/**
614
 * Determines the mimetype of a file by looking at its extension.
615
 *
616
 * @param $filename
617
 *
618
 * @return null|string
619
 */
620
function mimetype_from_filename($filename)
621
{
622
    return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION));
0 ignored issues
show
Deprecated Code introduced by
The function GuzzleHttp\Psr7\mimetype_from_extension() has been deprecated: mimetype_from_extension will be removed in guzzlehttp/psr7:2.0. Use MimeType::fromExtension instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

622
    return /** @scrutinizer ignore-deprecated */ mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
623
}
624
625
/**
626
 * Maps a file extensions to a mimetype.
627
 *
628
 * @param $extension string The file extension.
629
 *
630
 * @return string|null
631
 * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
632
 */
633
function mimetype_from_extension($extension)
634
{
635
    static $mimetypes = [
636
        '7z' => 'application/x-7z-compressed',
637
        'aac' => 'audio/x-aac',
638
        'ai' => 'application/postscript',
639
        'aif' => 'audio/x-aiff',
640
        'asc' => 'text/plain',
641
        'asf' => 'video/x-ms-asf',
642
        'atom' => 'application/atom+xml',
643
        'avi' => 'video/x-msvideo',
644
        'bmp' => 'image/bmp',
645
        'bz2' => 'application/x-bzip2',
646
        'cer' => 'application/pkix-cert',
647
        'crl' => 'application/pkix-crl',
648
        'crt' => 'application/x-x509-ca-cert',
649
        'css' => 'text/css',
650
        'csv' => 'text/csv',
651
        'cu' => 'application/cu-seeme',
652
        'deb' => 'application/x-debian-package',
653
        'doc' => 'application/msword',
654
        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
655
        'dvi' => 'application/x-dvi',
656
        'eot' => 'application/vnd.ms-fontobject',
657
        'eps' => 'application/postscript',
658
        'epub' => 'application/epub+zip',
659
        'etx' => 'text/x-setext',
660
        'flac' => 'audio/flac',
661
        'flv' => 'video/x-flv',
662
        'gif' => 'image/gif',
663
        'gz' => 'application/gzip',
664
        'htm' => 'text/html',
665
        'html' => 'text/html',
666
        'ico' => 'image/x-icon',
667
        'ics' => 'text/calendar',
668
        'ini' => 'text/plain',
669
        'iso' => 'application/x-iso9660-image',
670
        'jar' => 'application/java-archive',
671
        'jpe' => 'image/jpeg',
672
        'jpeg' => 'image/jpeg',
673
        'jpg' => 'image/jpeg',
674
        'js' => 'text/javascript',
675
        'json' => 'application/json',
676
        'latex' => 'application/x-latex',
677
        'log' => 'text/plain',
678
        'm4a' => 'audio/mp4',
679
        'm4v' => 'video/mp4',
680
        'mid' => 'audio/midi',
681
        'midi' => 'audio/midi',
682
        'mov' => 'video/quicktime',
683
        'mp3' => 'audio/mpeg',
684
        'mp4' => 'video/mp4',
685
        'mp4a' => 'audio/mp4',
686
        'mp4v' => 'video/mp4',
687
        'mpe' => 'video/mpeg',
688
        'mpeg' => 'video/mpeg',
689
        'mpg' => 'video/mpeg',
690
        'mpg4' => 'video/mp4',
691
        'oga' => 'audio/ogg',
692
        'ogg' => 'audio/ogg',
693
        'ogv' => 'video/ogg',
694
        'ogx' => 'application/ogg',
695
        'pbm' => 'image/x-portable-bitmap',
696
        'pdf' => 'application/pdf',
697
        'pgm' => 'image/x-portable-graymap',
698
        'png' => 'image/png',
699
        'pnm' => 'image/x-portable-anymap',
700
        'ppm' => 'image/x-portable-pixmap',
701
        'ppt' => 'application/vnd.ms-powerpoint',
702
        'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
703
        'ps' => 'application/postscript',
704
        'qt' => 'video/quicktime',
705
        'rar' => 'application/x-rar-compressed',
706
        'ras' => 'image/x-cmu-raster',
707
        'rss' => 'application/rss+xml',
708
        'rtf' => 'application/rtf',
709
        'sgm' => 'text/sgml',
710
        'sgml' => 'text/sgml',
711
        'svg' => 'image/svg+xml',
712
        'swf' => 'application/x-shockwave-flash',
713
        'tar' => 'application/x-tar',
714
        'tif' => 'image/tiff',
715
        'tiff' => 'image/tiff',
716
        'torrent' => 'application/x-bittorrent',
717
        'ttf' => 'application/x-font-ttf',
718
        'txt' => 'text/plain',
719
        'wav' => 'audio/x-wav',
720
        'webm' => 'video/webm',
721
        'wma' => 'audio/x-ms-wma',
722
        'wmv' => 'video/x-ms-wmv',
723
        'woff' => 'application/x-font-woff',
724
        'wsdl' => 'application/wsdl+xml',
725
        'xbm' => 'image/x-xbitmap',
726
        'xls' => 'application/vnd.ms-excel',
727
        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
728
        'xml' => 'application/xml',
729
        'xpm' => 'image/x-xpixmap',
730
        'xwd' => 'image/x-xwindowdump',
731
        'yaml' => 'text/yaml',
732
        'yml' => 'text/yaml',
733
        'zip' => 'application/zip',
734
    ];
735
736
    $extension = strtolower($extension);
737
738
    return isset($mimetypes[$extension])
739
        ? $mimetypes[$extension]
740
        : null;
741
}
742
743
/**
744
 * Parses an HTTP message into an associative array.
745
 *
746
 * The array contains the "start-line" key containing the start line of
747
 * the message, "headers" key containing an associative array of header
748
 * array values, and a "body" key containing the body of the message.
749
 *
750
 * @param string $message HTTP request or response to parse.
751
 *
752
 * @return array
753
 * @internal
754
 */
755
function _parse_message($message)
756
{
757
    if (!$message) {
758
        throw new \InvalidArgumentException('Invalid message');
759
    }
760
761
    // Iterate over each line in the message, accounting for line endings
762
    $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE);
763
    $result = ['start-line' => array_shift($lines), 'headers' => [], 'body' => ''];
764
    array_shift($lines);
765
766
    for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) {
767
        $line = $lines[$i];
768
        // If two line breaks were encountered, then this is the end of body
769
        if (empty($line)) {
770
            if ($i < $totalLines - 1) {
771
                $result['body'] = implode('', array_slice($lines, $i + 2));
772
            }
773
            break;
774
        }
775
        if (strpos($line, ':')) {
776
            $parts = explode(':', $line, 2);
777
            $key = trim($parts[0]);
778
            $value = isset($parts[1]) ? trim($parts[1]) : '';
779
            $result['headers'][$key][] = $value;
780
        }
781
    }
782
783
    return $result;
784
}
785
786
/**
787
 * Constructs a URI for an HTTP request message.
788
 *
789
 * @param string $path    Path from the start-line
790
 * @param array  $headers Array of headers (each value an array).
791
 *
792
 * @return string
793
 * @internal
794
 */
795
function _parse_request_uri($path, array $headers)
796
{
797
    $hostKey = array_filter(array_keys($headers), function ($k) {
798
        return strtolower($k) === 'host';
799
    });
800
801
    // If no host is found, then a full URI cannot be constructed.
802
    if (!$hostKey) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $hostKey of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
803
        return $path;
804
    }
805
806
    $host = $headers[reset($hostKey)][0];
807
    $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
808
809
    return $scheme . '://' . $host . '/' . ltrim($path, '/');
810
}
811
812
/** @internal */
813
function _caseless_remove($keys, array $data)
814
{
815
    $result = [];
816
817
    foreach ($keys as &$key) {
818
        $key = strtolower($key);
819
    }
820
821
    foreach ($data as $k => $v) {
822
        if (!in_array(strtolower($k), $keys)) {
823
            $result[$k] = $v;
824
        }
825
    }
826
827
    return $result;
828
}
829