setBody(fopen('php://input', 'r')); $r->setPostData($_POST); return $r; } /** * Sends the HTTP response back to a HTTP client. * * This calls php's header() function and streams the body to php://output. */ public static function sendResponse(ResponseInterface $response) { header('HTTP/'.$response->getHttpVersion().' '.$response->getStatus().' '.$response->getStatusText()); foreach ($response->getHeaders() as $key => $value) { foreach ($value as $k => $v) { if (0 === $k) { header($key.': '.$v); } else { header($key.': '.$v, false); } } } $body = $response->getBody(); if (null === $body) { return; } if (is_callable($body)) { $body(); return; } $contentLength = $response->getHeader('Content-Length'); if (null !== $contentLength) { $output = fopen('php://output', 'wb'); if (is_resource($body) && 'stream' == get_resource_type($body)) { if (PHP_INT_SIZE > 4) { // use the dedicated function on 64 Bit systems // a workaround to make PHP more possible to use mmap based copy, see https://github.com/sabre-io/http/pull/119 $left = (int) $contentLength; // copy with 4MiB chunks $chunk_size = 4 * 1024 * 1024; stream_set_chunk_size($output, $chunk_size); // If this is a partial response, flush the beginning bytes until the first position that is a multiple of the page size. $contentRange = $response->getHeader('Content-Range'); // Matching "Content-Range: bytes 1234-5678/7890" if (null !== $contentRange && preg_match('/^bytes\s([0-9]+)-([0-9]+)\//i', $contentRange, $matches)) { // 4kB should be the default page size on most architectures $pageSize = 4096; $offset = (int) $matches[1]; $delta = ($offset % $pageSize) > 0 ? ($pageSize - $offset % $pageSize) : 0; if ($delta > 0) { $left -= stream_copy_to_stream($body, $output, min($delta, $left)); } } while ($left > 0) { $copied = stream_copy_to_stream($body, $output, min($left, $chunk_size)); // stream_copy_to_stream($src, $dest, $maxLength) must return the number of bytes copied or false in case of failure // But when the $maxLength is greater than the total number of bytes remaining in the stream, // It returns the negative number of bytes copied // So break the loop in such cases. if ($copied <= 0) { break; } $left -= $copied; } } else { // workaround for 32 Bit systems to avoid stream_copy_to_stream while (!feof($body)) { fwrite($output, fread($body, 8192)); } } } else { fwrite($output, $body, (int) $contentLength); } } else { file_put_contents('php://output', $body); } if (is_resource($body)) { fclose($body); } } /** * This static method will create a new Request object, based on a PHP * $_SERVER array. * * REQUEST_URI and REQUEST_METHOD are required. */ public static function createFromServerArray(array $serverArray): Request { $headers = []; $method = null; $url = null; $httpVersion = '1.1'; $protocol = 'http'; $hostName = 'localhost'; foreach ($serverArray as $key => $value) { $key = (string) $key; switch ($key) { case 'SERVER_PROTOCOL': if ('HTTP/1.0' === $value) { $httpVersion = '1.0'; } elseif ('HTTP/2.0' === $value) { $httpVersion = '2.0'; } break; case 'REQUEST_METHOD': $method = $value; break; case 'REQUEST_URI': $url = $value; break; // These sometimes show up without a HTTP_ prefix case 'CONTENT_TYPE': $headers['Content-Type'] = $value; break; case 'CONTENT_LENGTH': $headers['Content-Length'] = $value; break; // mod_php on apache will put credentials in these variables. // (fast)cgi does not usually do this, however. case 'PHP_AUTH_USER': if (isset($serverArray['PHP_AUTH_PW'])) { $headers['Authorization'] = 'Basic '.base64_encode($value.':'.$serverArray['PHP_AUTH_PW']); } break; // Similarly, mod_php may also screw around with digest auth. case 'PHP_AUTH_DIGEST': $headers['Authorization'] = 'Digest '.$value; break; // Apache may prefix the HTTP_AUTHORIZATION header with // REDIRECT_, if mod_rewrite was used. case 'REDIRECT_HTTP_AUTHORIZATION': $headers['Authorization'] = $value; break; case 'HTTP_HOST': $hostName = $value; $headers['Host'] = $value; break; case 'HTTPS': if (!empty($value) && 'off' !== $value) { $protocol = 'https'; } break; default: if ('HTTP_' === substr($key, 0, 5)) { // It's a HTTP header // Normalizing it to be prettier $header = strtolower(substr($key, 5)); // Transforming dashes into spaces, and uppercasing // every first letter. $header = ucwords(str_replace('_', ' ', $header)); // Turning spaces into dashes. $header = str_replace(' ', '-', $header); $headers[$header] = $value; } break; } } if (null === $url) { throw new InvalidArgumentException('The _SERVER array must have a REQUEST_URI key'); } if (null === $method) { throw new InvalidArgumentException('The _SERVER array must have a REQUEST_METHOD key'); } $r = new Request($method, $url, $headers); $r->setHttpVersion($httpVersion); $r->setRawServerData($serverArray); $r->setAbsoluteUrl($protocol.'://'.$hostName.$url); return $r; } }