GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 5cefd1...492078 )
by Anton
04:08
created

build_query()   B

Complexity

Conditions 11
Paths 26

Size

Total Lines 37
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 25
c 0
b 0
f 0
nc 26
nop 2
dl 0
loc 37
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace RingCentral\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\StreamInterface;
8
use Psr\Http\Message\UriInterface;
9
10
// Manually define missing constants for PHP 5.3
11
defined('PHP_QUERY_RFC1738') or define('PHP_QUERY_RFC1738', 1);
12
defined('PHP_QUERY_RFC3986') or define('PHP_QUERY_RFC3986', 2);
13
14
/**
15
 * Returns the string representation of an HTTP message.
16
 *
17
 * @param MessageInterface $message Message to convert to a string.
18
 *
19
 * @return string
20
 */
21
function str(MessageInterface $message)
22
{
23
    if ($message instanceof RequestInterface) {
24
        $msg = trim($message->getMethod() . ' '
25
                . $message->getRequestTarget())
26
            . ' HTTP/' . $message->getProtocolVersion();
27
        if (!$message->hasHeader('host')) {
28
            $msg .= "\r\nHost: " . $message->getUri()->getHost();
29
        }
30
    } elseif ($message instanceof ResponseInterface) {
31
        $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
32
            . $message->getStatusCode() . ' '
33
            . $message->getReasonPhrase();
34
    } else {
35
        throw new \InvalidArgumentException('Unknown message type');
36
    }
37
38
    foreach ($message->getHeaders() as $name => $values) {
39
        $msg .= "\r\n{$name}: " . implode(', ', $values);
40
    }
41
42
    return "{$msg}\r\n\r\n" . $message->getBody();
43
}
44
45
/**
46
 * Returns a UriInterface for the given value.
47
 *
48
 * This function accepts a string or {@see Psr\Http\Message\UriInterface} and
49
 * returns a UriInterface for the given value. If the value is already a
50
 * `UriInterface`, it is returned as-is.
51
 *
52
 * @param string|UriInterface $uri
53
 *
54
 * @return UriInterface
55
 * @throws \InvalidArgumentException
56
 */
57
function uri_for($uri)
58
{
59
    if ($uri instanceof UriInterface) {
60
        return $uri;
61
    } elseif (is_string($uri)) {
0 ignored issues
show
introduced by
The condition is_string($uri) is always true.
Loading history...
62
        return new Uri($uri);
63
    }
64
65
    throw new \InvalidArgumentException('URI must be a string or UriInterface');
66
}
67
68
/**
69
 * Create a new stream based on the input type.
70
 *
71
 * Options is an associative array that can contain the following keys:
72
 * - metadata: Array of custom metadata.
73
 * - size: Size of the stream.
74
 *
75
 * @param resource|string|StreamInterface $resource Entity body data
76
 * @param array                           $options  Additional options
77
 *
78
 * @return Stream
79
 * @throws \InvalidArgumentException if the $resource arg is not valid.
80
 */
81
function stream_for($resource = '', array $options = array())
82
{
83
    switch (gettype($resource)) {
84
        case 'string':
85
            $stream = fopen('php://temp', 'r+');
86
            if ($resource !== '') {
87
                fwrite($stream, $resource);
88
                fseek($stream, 0);
89
            }
90
            return new Stream($stream, $options);
91
        case 'resource':
92
            return new Stream($resource, $options);
93
        case 'object':
94
            if ($resource instanceof StreamInterface) {
0 ignored issues
show
introduced by
$resource is always a sub-type of Psr\Http\Message\StreamInterface.
Loading history...
95
                return $resource;
96
            } elseif ($resource instanceof \Iterator) {
97
                return new PumpStream(function () use ($resource) {
98
                    if (!$resource->valid()) {
99
                        return false;
100
                    }
101
                    $result = $resource->current();
102
                    $resource->next();
103
                    return $result;
104
                }, $options);
105
            } elseif (method_exists($resource, '__toString')) {
106
                return stream_for((string) $resource, $options);
107
            }
108
            break;
109
        case 'NULL':
110
            return new Stream(fopen('php://temp', 'r+'), $options);
111
    }
112
113
    if (is_callable($resource)) {
114
        return new PumpStream($resource, $options);
0 ignored issues
show
Bug Best Practice introduced by
The expression return new RingCentral\P...am($resource, $options) returns the type RingCentral\Psr7\PumpStream which is incompatible with the documented return type RingCentral\Psr7\Stream.
Loading history...
115
    }
116
117
    throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
118
}
119
120
/**
121
 * Parse an array of header values containing ";" separated data into an
122
 * array of associative arrays representing the header key value pair
123
 * data of the header. When a parameter does not contain a value, but just
124
 * contains a key, this function will inject a key with a '' string value.
125
 *
126
 * @param string|array $header Header to parse into components.
127
 *
128
 * @return array Returns the parsed header values.
129
 */
130
function parse_header($header)
131
{
132
    static $trimmed = "\"'  \n\t\r";
133
    $params = $matches = array();
134
135
    foreach (normalize_header($header) as $val) {
136
        $part = array();
137
        foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
138
            if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
139
                $m = $matches[0];
140
                if (isset($m[1])) {
141
                    $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
142
                } else {
143
                    $part[] = trim($m[0], $trimmed);
144
                }
145
            }
146
        }
147
        if ($part) {
148
            $params[] = $part;
149
        }
150
    }
151
152
    return $params;
153
}
154
155
/**
156
 * Converts an array of header values that may contain comma separated
157
 * headers into an array of headers with no comma separated values.
158
 *
159
 * @param string|array $header Header to normalize.
160
 *
161
 * @return array Returns the normalized header field values.
162
 */
163
function normalize_header($header)
164
{
165
    if (!is_array($header)) {
166
        return array_map('trim', explode(',', $header));
167
    }
168
169
    $result = array();
170
    foreach ($header as $value) {
171
        foreach ((array) $value as $v) {
172
            if (strpos($v, ',') === false) {
173
                $result[] = $v;
174
                continue;
175
            }
176
            foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
177
                $result[] = trim($vv);
178
            }
179
        }
180
    }
181
182
    return $result;
183
}
184
185
/**
186
 * Clone and modify a request with the given changes.
187
 *
188
 * The changes can be one of:
189
 * - method: (string) Changes the HTTP method.
190
 * - set_headers: (array) Sets the given headers.
191
 * - remove_headers: (array) Remove the given headers.
192
 * - body: (mixed) Sets the given body.
193
 * - uri: (UriInterface) Set the URI.
194
 * - query: (string) Set the query string value of the URI.
195
 * - version: (string) Set the protocol version.
196
 *
197
 * @param RequestInterface $request Request to clone and modify.
198
 * @param array            $changes Changes to apply.
199
 *
200
 * @return RequestInterface
201
 */
202
function modify_request(RequestInterface $request, array $changes)
203
{
204
    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...
205
        return $request;
206
    }
207
208
    $headers = $request->getHeaders();
209
210
    if (!isset($changes['uri'])) {
211
        $uri = $request->getUri();
212
    } else {
213
        // Remove the host header if one is on the URI
214
        if ($host = $changes['uri']->getHost()) {
215
            $changes['set_headers']['Host'] = $host;
216
        }
217
        $uri = $changes['uri'];
218
    }
219
220
    if (!empty($changes['remove_headers'])) {
221
        $headers = _caseless_remove($changes['remove_headers'], $headers);
222
    }
223
224
    if (!empty($changes['set_headers'])) {
225
        $headers = _caseless_remove(array_keys($changes['set_headers']), $headers);
226
        $headers = $changes['set_headers'] + $headers;
227
    }
228
229
    if (isset($changes['query'])) {
230
        $uri = $uri->withQuery($changes['query']);
231
    }
232
233
    return new Request(
234
        isset($changes['method']) ? $changes['method'] : $request->getMethod(),
235
        $uri,
236
        $headers,
237
        isset($changes['body']) ? $changes['body'] : $request->getBody(),
238
        isset($changes['version'])
239
            ? $changes['version']
240
            : $request->getProtocolVersion()
241
    );
242
}
243
244
/**
245
 * Attempts to rewind a message body and throws an exception on failure.
246
 *
247
 * The body of the message will only be rewound if a call to `tell()` returns a
248
 * value other than `0`.
249
 *
250
 * @param MessageInterface $message Message to rewind
251
 *
252
 * @throws \RuntimeException
253
 */
254
function rewind_body(MessageInterface $message)
255
{
256
    $body = $message->getBody();
257
258
    if ($body->tell()) {
259
        $body->rewind();
260
    }
261
}
262
263
/**
264
 * Safely opens a PHP stream resource using a filename.
265
 *
266
 * When fopen fails, PHP normally raises a warning. This function adds an
267
 * error handler that checks for errors and throws an exception instead.
268
 *
269
 * @param string $filename File to open
270
 * @param string $mode     Mode used to open the file
271
 *
272
 * @return resource
273
 * @throws \RuntimeException if the file cannot be opened
274
 */
275
function try_fopen($filename, $mode)
276
{
277
    $ex = null;
278
    $fargs = func_get_args();
279
    set_error_handler(function () use ($filename, $mode, &$ex, $fargs) {
280
        $ex = new \RuntimeException(sprintf(
281
            'Unable to open %s using mode %s: %s',
282
            $filename,
283
            $mode,
284
            $fargs[1]
285
        ));
286
    });
287
288
    $handle = fopen($filename, $mode);
289
    restore_error_handler();
290
291
    if ($ex) {
292
        /** @var $ex \RuntimeException */
293
        throw $ex;
294
    }
295
296
    return $handle;
297
}
298
299
/**
300
 * Copy the contents of a stream into a string until the given number of
301
 * bytes have been read.
302
 *
303
 * @param StreamInterface $stream Stream to read
304
 * @param int             $maxLen Maximum number of bytes to read. Pass -1
305
 *                                to read the entire stream.
306
 * @return string
307
 * @throws \RuntimeException on error.
308
 */
309
function copy_to_string(StreamInterface $stream, $maxLen = -1)
310
{
311
    $buffer = '';
312
313
    if ($maxLen === -1) {
314
        while (!$stream->eof()) {
315
            $buf = $stream->read(1048576);
316
            // Using a loose equality here to match on '' and false.
317
            if ($buf == null) {
318
                break;
319
            }
320
            $buffer .= $buf;
321
        }
322
        return $buffer;
323
    }
324
325
    $len = 0;
326
    while (!$stream->eof() && $len < $maxLen) {
327
        $buf = $stream->read($maxLen - $len);
328
        // Using a loose equality here to match on '' and false.
329
        if ($buf == null) {
330
            break;
331
        }
332
        $buffer .= $buf;
333
        $len = strlen($buffer);
334
    }
335
336
    return $buffer;
337
}
338
339
/**
340
 * Copy the contents of a stream into another stream until the given number
341
 * of bytes have been read.
342
 *
343
 * @param StreamInterface $source Stream to read from
344
 * @param StreamInterface $dest   Stream to write to
345
 * @param int             $maxLen Maximum number of bytes to read. Pass -1
346
 *                                to read the entire stream.
347
 *
348
 * @throws \RuntimeException on error.
349
 */
350
function copy_to_stream(
351
    StreamInterface $source,
352
    StreamInterface $dest,
353
    $maxLen = -1
354
) {
355
    if ($maxLen === -1) {
356
        while (!$source->eof()) {
357
            if (!$dest->write($source->read(1048576))) {
358
                break;
359
            }
360
        }
361
        return;
362
    }
363
364
    $bytes = 0;
365
    while (!$source->eof()) {
366
        $buf = $source->read($maxLen - $bytes);
367
        if (!($len = strlen($buf))) {
368
            break;
369
        }
370
        $bytes += $len;
371
        $dest->write($buf);
372
        if ($bytes == $maxLen) {
373
            break;
374
        }
375
    }
376
}
377
378
/**
379
 * Calculate a hash of a Stream
380
 *
381
 * @param StreamInterface $stream    Stream to calculate the hash for
382
 * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
383
 * @param bool            $rawOutput Whether or not to use raw output
384
 *
385
 * @return string Returns the hash of the stream
386
 * @throws \RuntimeException on error.
387
 */
388
function hash(
389
    StreamInterface $stream,
390
    $algo,
391
    $rawOutput = false
392
) {
393
    $pos = $stream->tell();
394
395
    if ($pos > 0) {
396
        $stream->rewind();
397
    }
398
399
    $ctx = hash_init($algo);
400
    while (!$stream->eof()) {
401
        hash_update($ctx, $stream->read(1048576));
402
    }
403
404
    $out = hash_final($ctx, (bool) $rawOutput);
405
    $stream->seek($pos);
406
407
    return $out;
408
}
409
410
/**
411
 * Read a line from the stream up to the maximum allowed buffer length
412
 *
413
 * @param StreamInterface $stream    Stream to read from
414
 * @param int             $maxLength Maximum buffer length
415
 *
416
 * @return string|bool
417
 */
418
function readline(StreamInterface $stream, $maxLength = null)
419
{
420
    $buffer = '';
421
    $size = 0;
422
423
    while (!$stream->eof()) {
424
        // Using a loose equality here to match on '' and false.
425
        if (null == ($byte = $stream->read(1))) {
426
            return $buffer;
427
        }
428
        $buffer .= $byte;
429
        // Break when a new line is found or the max length - 1 is reached
430
        if ($byte == PHP_EOL || ++$size == $maxLength - 1) {
431
            break;
432
        }
433
    }
434
435
    return $buffer;
436
}
437
438
/**
439
 * Parses a request message string into a request object.
440
 *
441
 * @param string $message Request message string.
442
 *
443
 * @return Request
444
 */
445
function parse_request($message)
446
{
447
    $data = _parse_message($message);
448
    $matches = array();
449
    if (!preg_match('/^[a-zA-Z]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
450
        throw new \InvalidArgumentException('Invalid request string');
451
    }
452
    $parts = explode(' ', $data['start-line'], 3);
453
    $subParts = isset($parts[2]) ?  explode('/', $parts[2]) : array();
454
    $version = isset($parts[2]) ? $subParts[1] : '1.1';
455
456
    $request = new Request(
457
        $parts[0],
458
        $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1],
459
        $data['headers'],
460
        $data['body'],
461
        $version
462
    );
463
464
    return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
465
}
466
467
/**
468
 * Parses a request message string into a server request object.
469
 *
470
 * @param string $message Request message string.
471
 * @param array $serverParams Server params that will be added to the
472
 *                            ServerRequest object
473
 *
474
 * @return ServerRequest
475
 */
476
function parse_server_request($message, array $serverParams = array())
477
{
478
    $request = parse_request($message);
479
480
    return new ServerRequest(
481
        $request->getMethod(),
482
        $request->getUri(),
483
        $request->getHeaders(),
484
        $request->getBody(),
485
        $request->getProtocolVersion(),
486
        $serverParams
487
    );
488
}
489
490
/**
491
 * Parses a response message string into a response object.
492
 *
493
 * @param string $message Response message string.
494
 *
495
 * @return Response
496
 */
497
function parse_response($message)
498
{
499
    $data = _parse_message($message);
500
    // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
501
    // between status-code and reason-phrase is required. But browsers accept
502
    // responses without space and reason as well.
503
    if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
504
        throw new \InvalidArgumentException('Invalid response string');
505
    }
506
    $parts = explode(' ', $data['start-line'], 3);
507
    $subParts = explode('/', $parts[0]);
508
509
    return new Response(
510
        $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 RingCentral\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

510
        /** @scrutinizer ignore-type */ $parts[1],
Loading history...
511
        $data['headers'],
512
        $data['body'],
513
        $subParts[1],
514
        isset($parts[2]) ? $parts[2] : null
515
    );
516
}
517
518
/**
519
 * Parse a query string into an associative array.
520
 *
521
 * If multiple values are found for the same key, the value of that key
522
 * value pair will become an array. This function does not parse nested
523
 * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
524
 * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
525
 *
526
 * @param string      $str         Query string to parse
527
 * @param bool|string $urlEncoding How the query string is encoded
528
 *
529
 * @return array
530
 */
531
function parse_query($str, $urlEncoding = true)
532
{
533
    $result = array();
534
535
    if ($str === '') {
536
        return $result;
537
    }
538
539
    if ($urlEncoding === true) {
540
        $decoder = function ($value) {
541
            return rawurldecode(str_replace('+', ' ', $value));
542
        };
543
    } elseif ($urlEncoding == PHP_QUERY_RFC3986) {
544
        $decoder = 'rawurldecode';
545
    } elseif ($urlEncoding == PHP_QUERY_RFC1738) {
546
        $decoder = 'urldecode';
547
    } else {
548
        $decoder = function ($str) { return $str; };
549
    }
550
551
    foreach (explode('&', $str) as $kvp) {
552
        $parts = explode('=', $kvp, 2);
553
        $key = $decoder($parts[0]);
554
        $value = isset($parts[1]) ? $decoder($parts[1]) : null;
555
        if (!isset($result[$key])) {
556
            $result[$key] = $value;
557
        } else {
558
            if (!is_array($result[$key])) {
559
                $result[$key] = array($result[$key]);
560
            }
561
            $result[$key][] = $value;
562
        }
563
    }
564
565
    return $result;
566
}
567
568
/**
569
 * Build a query string from an array of key value pairs.
570
 *
571
 * This function can use the return value of parseQuery() to build a query
572
 * string. This function does not modify the provided keys when an array is
573
 * encountered (like http_build_query would).
574
 *
575
 * @param array     $params   Query string parameters.
576
 * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
577
 *                            to encode using RFC3986, or PHP_QUERY_RFC1738
578
 *                            to encode using RFC1738.
579
 * @return string
580
 */
581
function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
582
{
583
    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...
584
        return '';
585
    }
586
587
    if ($encoding === false) {
588
        $encoder = function ($str) { return $str; };
589
    } elseif ($encoding == PHP_QUERY_RFC3986) {
590
        $encoder = 'rawurlencode';
591
    } elseif ($encoding == PHP_QUERY_RFC1738) {
592
        $encoder = 'urlencode';
593
    } else {
594
        throw new \InvalidArgumentException('Invalid type');
595
    }
596
597
    $qs = '';
598
    foreach ($params as $k => $v) {
599
        $k = $encoder($k);
600
        if (!is_array($v)) {
601
            $qs .= $k;
602
            if ($v !== null) {
603
                $qs .= '=' . $encoder($v);
604
            }
605
            $qs .= '&';
606
        } else {
607
            foreach ($v as $vv) {
608
                $qs .= $k;
609
                if ($vv !== null) {
610
                    $qs .= '=' . $encoder($vv);
611
                }
612
                $qs .= '&';
613
            }
614
        }
615
    }
616
617
    return $qs ? (string) substr($qs, 0, -1) : '';
618
}
619
620
/**
621
 * Determines the mimetype of a file by looking at its extension.
622
 *
623
 * @param $filename
624
 *
625
 * @return null|string
626
 */
627
function mimetype_from_filename($filename)
628
{
629
    return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION));
630
}
631
632
/**
633
 * Maps a file extensions to a mimetype.
634
 *
635
 * @param $extension string The file extension.
636
 *
637
 * @return string|null
638
 * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
639
 */
640
function mimetype_from_extension($extension)
641
{
642
    static $mimetypes = array(
643
        '7z' => 'application/x-7z-compressed',
644
        'aac' => 'audio/x-aac',
645
        'ai' => 'application/postscript',
646
        'aif' => 'audio/x-aiff',
647
        'asc' => 'text/plain',
648
        'asf' => 'video/x-ms-asf',
649
        'atom' => 'application/atom+xml',
650
        'avi' => 'video/x-msvideo',
651
        'bmp' => 'image/bmp',
652
        'bz2' => 'application/x-bzip2',
653
        'cer' => 'application/pkix-cert',
654
        'crl' => 'application/pkix-crl',
655
        'crt' => 'application/x-x509-ca-cert',
656
        'css' => 'text/css',
657
        'csv' => 'text/csv',
658
        'cu' => 'application/cu-seeme',
659
        'deb' => 'application/x-debian-package',
660
        'doc' => 'application/msword',
661
        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
662
        'dvi' => 'application/x-dvi',
663
        'eot' => 'application/vnd.ms-fontobject',
664
        'eps' => 'application/postscript',
665
        'epub' => 'application/epub+zip',
666
        'etx' => 'text/x-setext',
667
        'flac' => 'audio/flac',
668
        'flv' => 'video/x-flv',
669
        'gif' => 'image/gif',
670
        'gz' => 'application/gzip',
671
        'htm' => 'text/html',
672
        'html' => 'text/html',
673
        'ico' => 'image/x-icon',
674
        'ics' => 'text/calendar',
675
        'ini' => 'text/plain',
676
        'iso' => 'application/x-iso9660-image',
677
        'jar' => 'application/java-archive',
678
        'jpe' => 'image/jpeg',
679
        'jpeg' => 'image/jpeg',
680
        'jpg' => 'image/jpeg',
681
        'js' => 'text/javascript',
682
        'json' => 'application/json',
683
        'latex' => 'application/x-latex',
684
        'log' => 'text/plain',
685
        'm4a' => 'audio/mp4',
686
        'm4v' => 'video/mp4',
687
        'mid' => 'audio/midi',
688
        'midi' => 'audio/midi',
689
        'mov' => 'video/quicktime',
690
        'mp3' => 'audio/mpeg',
691
        'mp4' => 'video/mp4',
692
        'mp4a' => 'audio/mp4',
693
        'mp4v' => 'video/mp4',
694
        'mpe' => 'video/mpeg',
695
        'mpeg' => 'video/mpeg',
696
        'mpg' => 'video/mpeg',
697
        'mpg4' => 'video/mp4',
698
        'oga' => 'audio/ogg',
699
        'ogg' => 'audio/ogg',
700
        'ogv' => 'video/ogg',
701
        'ogx' => 'application/ogg',
702
        'pbm' => 'image/x-portable-bitmap',
703
        'pdf' => 'application/pdf',
704
        'pgm' => 'image/x-portable-graymap',
705
        'png' => 'image/png',
706
        'pnm' => 'image/x-portable-anymap',
707
        'ppm' => 'image/x-portable-pixmap',
708
        'ppt' => 'application/vnd.ms-powerpoint',
709
        'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
710
        'ps' => 'application/postscript',
711
        'qt' => 'video/quicktime',
712
        'rar' => 'application/x-rar-compressed',
713
        'ras' => 'image/x-cmu-raster',
714
        'rss' => 'application/rss+xml',
715
        'rtf' => 'application/rtf',
716
        'sgm' => 'text/sgml',
717
        'sgml' => 'text/sgml',
718
        'svg' => 'image/svg+xml',
719
        'swf' => 'application/x-shockwave-flash',
720
        'tar' => 'application/x-tar',
721
        'tif' => 'image/tiff',
722
        'tiff' => 'image/tiff',
723
        'torrent' => 'application/x-bittorrent',
724
        'ttf' => 'application/x-font-ttf',
725
        'txt' => 'text/plain',
726
        'wav' => 'audio/x-wav',
727
        'webm' => 'video/webm',
728
        'wma' => 'audio/x-ms-wma',
729
        'wmv' => 'video/x-ms-wmv',
730
        'woff' => 'application/x-font-woff',
731
        'wsdl' => 'application/wsdl+xml',
732
        'xbm' => 'image/x-xbitmap',
733
        'xls' => 'application/vnd.ms-excel',
734
        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
735
        'xml' => 'application/xml',
736
        'xpm' => 'image/x-xpixmap',
737
        'xwd' => 'image/x-xwindowdump',
738
        'yaml' => 'text/yaml',
739
        'yml' => 'text/yaml',
740
        'zip' => 'application/zip',
741
    );
742
743
    $extension = strtolower($extension);
744
745
    return isset($mimetypes[$extension])
746
        ? $mimetypes[$extension]
747
        : null;
748
}
749
750
/**
751
 * Parses an HTTP message into an associative array.
752
 *
753
 * The array contains the "start-line" key containing the start line of
754
 * the message, "headers" key containing an associative array of header
755
 * array values, and a "body" key containing the body of the message.
756
 *
757
 * @param string $message HTTP request or response to parse.
758
 *
759
 * @return array
760
 * @internal
761
 */
762
function _parse_message($message)
763
{
764
    if (!$message) {
765
        throw new \InvalidArgumentException('Invalid message');
766
    }
767
768
    // Iterate over each line in the message, accounting for line endings
769
    $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE);
770
    $result = array('start-line' => array_shift($lines), 'headers' => array(), 'body' => '');
771
    array_shift($lines);
772
773
    for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) {
774
        $line = $lines[$i];
775
        // If two line breaks were encountered, then this is the end of body
776
        if (empty($line)) {
777
            if ($i < $totalLines - 1) {
778
                $result['body'] = implode('', array_slice($lines, $i + 2));
779
            }
780
            break;
781
        }
782
        if (strpos($line, ':')) {
783
            $parts = explode(':', $line, 2);
784
            $key = trim($parts[0]);
785
            $value = isset($parts[1]) ? trim($parts[1]) : '';
786
            $result['headers'][$key][] = $value;
787
        }
788
    }
789
790
    return $result;
791
}
792
793
/**
794
 * Constructs a URI for an HTTP request message.
795
 *
796
 * @param string $path    Path from the start-line
797
 * @param array  $headers Array of headers (each value an array).
798
 *
799
 * @return string
800
 * @internal
801
 */
802
function _parse_request_uri($path, array $headers)
803
{
804
    $hostKey = array_filter(array_keys($headers), function ($k) {
805
        return strtolower($k) === 'host';
806
    });
807
808
    // If no host is found, then a full URI cannot be constructed.
809
    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...
810
        return $path;
811
    }
812
813
    $host = $headers[reset($hostKey)][0];
814
    $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
815
816
    return $scheme . '://' . $host . '/' . ltrim($path, '/');
817
}
818
819
/** @internal */
820
function _caseless_remove($keys, array $data)
821
{
822
    $result = array();
823
824
    foreach ($keys as &$key) {
825
        $key = strtolower($key);
826
    }
827
828
    foreach ($data as $k => $v) {
829
        if (!in_array(strtolower($k), $keys)) {
830
            $result[$k] = $v;
831
        }
832
    }
833
834
    return $result;
835
}
836