commit vendor
This commit is contained in:
243
vendor/sabre/http/lib/Sapi.php
vendored
Normal file
243
vendor/sabre/http/lib/Sapi.php
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\HTTP;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* PHP SAPI.
|
||||
*
|
||||
* This object is responsible for:
|
||||
* 1. Constructing a Request object based on the current HTTP request sent to
|
||||
* the PHP process.
|
||||
* 2. Sending the Response object back to the client.
|
||||
*
|
||||
* It could be said that this class provides a mapping between the Request and
|
||||
* Response objects, and php's:
|
||||
*
|
||||
* * $_SERVER
|
||||
* * $_POST
|
||||
* * $_FILES
|
||||
* * php://input
|
||||
* * echo()
|
||||
* * header()
|
||||
* * php://output
|
||||
*
|
||||
* You can choose to either call all these methods statically, but you can also
|
||||
* instantiate this as an object to allow for polymorhpism.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Sapi
|
||||
{
|
||||
/**
|
||||
* This static method will create a new Request object, based on the
|
||||
* current PHP request.
|
||||
*/
|
||||
public static function getRequest(): Request
|
||||
{
|
||||
$serverArr = $_SERVER;
|
||||
|
||||
if ('cli' === PHP_SAPI) {
|
||||
// If we're running off the CLI, we're going to set some default
|
||||
// settings.
|
||||
$serverArr['REQUEST_URI'] = $_SERVER['REQUEST_URI'] ?? '/';
|
||||
$serverArr['REQUEST_METHOD'] = $_SERVER['REQUEST_METHOD'] ?? 'CLI';
|
||||
}
|
||||
|
||||
$r = self::createFromServerArray($serverArr);
|
||||
$r->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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user