21 $msg = trim($message->getMethod() .
' '
22 . $message->getRequestTarget())
25 $msg .=
"\r\nHost: " . $message->getUri()->getHost();
29 . $message->getStatusCode() .
' '
30 . $message->getReasonPhrase();
32 throw new \InvalidArgumentException(
'Unknown message type');
35 foreach ($message->
getHeaders() as $name => $values) {
36 $msg .=
"\r\n{$name}: " . implode(
', ', $values);
39 return "{$msg}\r\n\r\n" . $message->
getBody();
58 } elseif (is_string($uri)) {
62 throw new \InvalidArgumentException(
'URI must be a string or UriInterface');
80 if (is_scalar($resource)) {
81 $stream = fopen(
'php://temp',
'r+');
82 if ($resource !==
'') {
83 fwrite($stream, $resource);
86 return new Stream($stream, $options);
89 switch (gettype($resource)) {
91 return new Stream($resource, $options);
95 } elseif ($resource instanceof \Iterator) {
96 return new PumpStream(
function () use ($resource) {
97 if (!$resource->valid()) {
100 $result = $resource->current();
104 } elseif (method_exists($resource,
'__toString')) {
105 return stream_for((
string) $resource, $options);
109 return new Stream(fopen(
'php://temp',
'r+'), $options);
112 if (is_callable($resource)) {
116 throw new \InvalidArgumentException(
'Invalid resource type: ' . gettype($resource));
131 static $trimmed =
"\"' \n\t\r";
132 $params = $matches = [];
136 foreach (preg_split(
'/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
137 if (preg_match_all(
'/<[^>]+>|[^=]+/', $kvp, $matches)) {
140 $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
142 $part[] = trim($m[0], $trimmed);
164 if (!is_array($header)) {
165 return array_map(
'trim', explode(
',', $header));
169 foreach ($header as $value) {
170 foreach ((array) $value as $v) {
171 if (strpos($v,
',') ===
false) {
175 foreach (preg_split(
'/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
176 $result[] = trim($vv);
209 if (!isset($changes[
'uri'])) {
210 $uri = $request->
getUri();
213 if ($host = $changes[
'uri']->getHost()) {
214 $changes[
'set_headers'][
'Host'] = $host;
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;
224 $uri = $changes[
'uri'];
227 if (!empty($changes[
'remove_headers'])) {
231 if (!empty($changes[
'set_headers'])) {
233 $headers = $changes[
'set_headers'] + $headers;
236 if (isset($changes[
'query'])) {
237 $uri = $uri->withQuery($changes[
'query']);
242 isset($changes[
'method']) ? $changes[
'method'] : $request->
getMethod(),
245 isset($changes[
'body']) ? $changes[
'body'] : $request->
getBody(),
246 isset($changes[
'version'])
247 ? $changes[
'version']
249 $request->getServerParams()
251 ->withParsedBody($request->getParsedBody())
252 ->withQueryParams($request->getQueryParams())
253 ->withCookieParams($request->getCookieParams())
254 ->withUploadedFiles($request->getUploadedFiles());
258 isset($changes[
'method']) ? $changes[
'method'] : $request->
getMethod(),
261 isset($changes[
'body']) ? $changes[
'body'] : $request->
getBody(),
262 isset($changes[
'version'])
263 ? $changes[
'version']
302 set_error_handler(
function () use ($filename, $mode, &$ex) {
303 $ex = new \RuntimeException(sprintf(
304 'Unable to open %s using mode %s: %s',
311 $handle = fopen($filename, $mode);
312 restore_error_handler();
336 if ($maxLen === -1) {
337 while (!$stream->eof()) {
338 $buf = $stream->read(1048576);
349 while (!$stream->eof() && $len < $maxLen) {
350 $buf = $stream->read($maxLen - $len);
356 $len = strlen($buffer);
380 if ($maxLen === -1) {
381 while (!$source->eof()) {
382 if (!$dest->
write($source->read($bufferSize))) {
387 $remaining = $maxLen;
388 while ($remaining > 0 && !$source->eof()) {
389 $buf = $source->read(min($bufferSize, $remaining));
415 $pos = $stream->tell();
421 $ctx = hash_init($algo);
422 while (!$stream->eof()) {
423 hash_update($ctx, $stream->read(1048576));
426 $out = hash_final($ctx, (
bool) $rawOutput);
445 while (!$stream->eof()) {
447 if (
null == ($byte = $stream->read(1))) {
452 if ($byte ===
"\n" || ++$size === $maxLength - 1) {
471 if (!preg_match(
'/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data[
'start-line'], $matches)) {
472 throw new \InvalidArgumentException(
'Invalid request string');
474 $parts = explode(
' ', $data[
'start-line'], 3);
475 $version = isset($parts[2]) ? explode(
'/', $parts[2])[1] :
'1.1';
485 return $matches[1] ===
'/' ? $request : $request->withRequestTarget($parts[1]);
501 if (!preg_match(
'/^HTTP\/.* [0-9]{3}( .*|$)/', $data[
'start-line'])) {
502 throw new \InvalidArgumentException(
'Invalid response string: ' . $data[
'start-line']);
504 $parts = explode(
' ', $data[
'start-line'], 3);
510 explode(
'/', $parts[0])[1],
511 isset($parts[2]) ? $parts[2] :
null
536 if ($urlEncoding ===
true) {
537 $decoder =
function ($value) {
538 return rawurldecode(str_replace(
'+',
' ', $value));
540 } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
541 $decoder =
'rawurldecode';
542 } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
543 $decoder =
'urldecode';
545 $decoder =
function ($str) {
return $str; };
548 foreach (explode(
'&', $str) as $kvp) {
549 $parts = explode(
'=', $kvp, 2);
550 $key = $decoder($parts[0]);
551 $value = isset($parts[1]) ? $decoder($parts[1]) :
null;
552 if (!isset($result[$key])) {
553 $result[$key] = $value;
555 if (!is_array($result[$key])) {
556 $result[$key] = [$result[$key]];
558 $result[$key][] = $value;
578 function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
584 if ($encoding ===
false) {
585 $encoder =
function ($str) {
return $str; };
586 } elseif ($encoding === PHP_QUERY_RFC3986) {
587 $encoder =
'rawurlencode';
588 } elseif ($encoding === PHP_QUERY_RFC1738) {
589 $encoder =
'urlencode';
591 throw new \InvalidArgumentException(
'Invalid type');
595 foreach ($params as $k => $v) {
600 $qs .=
'=' . $encoder($v);
604 foreach ($v as $vv) {
607 $qs .=
'=' . $encoder($vv);
614 return $qs ? (string) substr($qs, 0, -1) :
'';
639 static $mimetypes = [
640 '3gp' =>
'video/3gpp',
641 '7z' =>
'application/x-7z-compressed',
642 'aac' =>
'audio/x-aac',
643 'ai' =>
'application/postscript',
644 'aif' =>
'audio/x-aiff',
645 'asc' =>
'text/plain',
646 'asf' =>
'video/x-ms-asf',
647 'atom' =>
'application/atom+xml',
648 'avi' =>
'video/x-msvideo',
649 'bmp' =>
'image/bmp',
650 'bz2' =>
'application/x-bzip2',
651 'cer' =>
'application/pkix-cert',
652 'crl' =>
'application/pkix-crl',
653 'crt' =>
'application/x-x509-ca-cert',
656 'cu' =>
'application/cu-seeme',
657 'deb' =>
'application/x-debian-package',
658 'doc' =>
'application/msword',
659 'docx' =>
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
660 'dvi' =>
'application/x-dvi',
661 'eot' =>
'application/vnd.ms-fontobject',
662 'eps' =>
'application/postscript',
663 'epub' =>
'application/epub+zip',
664 'etx' =>
'text/x-setext',
665 'flac' =>
'audio/flac',
666 'flv' =>
'video/x-flv',
667 'gif' =>
'image/gif',
668 'gz' =>
'application/gzip',
669 'htm' =>
'text/html',
670 'html' =>
'text/html',
671 'ico' =>
'image/x-icon',
672 'ics' =>
'text/calendar',
673 'ini' =>
'text/plain',
674 'iso' =>
'application/x-iso9660-image',
675 'jar' =>
'application/java-archive',
676 'jpe' =>
'image/jpeg',
677 'jpeg' =>
'image/jpeg',
678 'jpg' =>
'image/jpeg',
679 'js' =>
'text/javascript',
680 'json' =>
'application/json',
681 'latex' =>
'application/x-latex',
682 'log' =>
'text/plain',
683 'm4a' =>
'audio/mp4',
684 'm4v' =>
'video/mp4',
685 'mid' =>
'audio/midi',
686 'midi' =>
'audio/midi',
687 'mov' =>
'video/quicktime',
688 'mkv' =>
'video/x-matroska',
689 'mp3' =>
'audio/mpeg',
690 'mp4' =>
'video/mp4',
691 'mp4a' =>
'audio/mp4',
692 'mp4v' =>
'video/mp4',
693 'mpe' =>
'video/mpeg',
694 'mpeg' =>
'video/mpeg',
695 'mpg' =>
'video/mpeg',
696 'mpg4' =>
'video/mp4',
697 'oga' =>
'audio/ogg',
698 'ogg' =>
'audio/ogg',
699 'ogv' =>
'video/ogg',
700 'ogx' =>
'application/ogg',
701 'pbm' =>
'image/x-portable-bitmap',
702 'pdf' =>
'application/pdf',
703 'pgm' =>
'image/x-portable-graymap',
704 'png' =>
'image/png',
705 'pnm' =>
'image/x-portable-anymap',
706 'ppm' =>
'image/x-portable-pixmap',
707 'ppt' =>
'application/vnd.ms-powerpoint',
708 'pptx' =>
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
709 'ps' =>
'application/postscript',
710 'qt' =>
'video/quicktime',
711 'rar' =>
'application/x-rar-compressed',
712 'ras' =>
'image/x-cmu-raster',
713 'rss' =>
'application/rss+xml',
714 'rtf' =>
'application/rtf',
715 'sgm' =>
'text/sgml',
716 'sgml' =>
'text/sgml',
717 'svg' =>
'image/svg+xml',
718 'swf' =>
'application/x-shockwave-flash',
719 'tar' =>
'application/x-tar',
720 'tif' =>
'image/tiff',
721 'tiff' =>
'image/tiff',
722 'torrent' =>
'application/x-bittorrent',
723 'ttf' =>
'application/x-font-ttf',
724 'txt' =>
'text/plain',
725 'wav' =>
'audio/x-wav',
726 'webm' =>
'video/webm',
727 'webp' =>
'image/webp',
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',
743 $extension = strtolower($extension);
745 return isset($mimetypes[$extension])
746 ? $mimetypes[$extension]
765 throw new \InvalidArgumentException(
'Invalid message');
768 $message = ltrim($message,
"\r\n");
770 $messageParts = preg_split(
"/\r?\n\r?\n/", $message, 2);
772 if ($messageParts ===
false || count($messageParts) !== 2) {
773 throw new \InvalidArgumentException(
'Invalid message: Missing header delimiter');
776 list($rawHeaders, $body) = $messageParts;
777 $rawHeaders .=
"\r\n";
778 $headerParts = preg_split(
"/\r?\n/", $rawHeaders, 2);
780 if ($headerParts ===
false || count($headerParts) !== 2) {
781 throw new \InvalidArgumentException(
'Invalid message: Missing status line');
784 list($startLine, $rawHeaders) = $headerParts;
786 if (preg_match(
"/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] ===
'1.0') {
795 if ($count !== substr_count($rawHeaders,
"\n")) {
798 throw new \InvalidArgumentException(
'Invalid header syntax: Obsolete line folding');
801 throw new \InvalidArgumentException(
'Invalid header syntax');
806 foreach ($headerLines as $headerLine) {
807 $headers[$headerLine[1]][] = $headerLine[2];
811 'start-line' => $startLine,
812 'headers' => $headers,
828 $hostKey = array_filter(array_keys($headers),
function ($k) {
829 return strtolower($k) ===
'host';
837 $host = $headers[reset($hostKey)][0];
838 $scheme = substr($host, -4) ===
':443' ?
'https' :
'http';
840 return $scheme .
'://' . $host .
'/' . ltrim($path,
'/');
857 if (!$body->isSeekable() || !$body->isReadable()) {
861 $size = $body->getSize();
867 $summary = $body->read($truncateAt);
870 if ($size > $truncateAt) {
871 $summary .=
' (truncated...)';
876 if (preg_match(
'/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
888 foreach ($keys as &$key) {
889 $key = strtolower($key);
892 foreach ($data as $k => $v) {
893 if (!in_array(strtolower($k), $keys)) {