<?php namespace Sabre\HTTP; /** * 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. * * @return Request */ static function getRequest() { $r = self::createFromServerArray($_SERVER); $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. * * @param ResponseInterface $response * @return void */ 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 ($k === 0) { header($key . ': ' . $v); } else { header($key . ': ' . $v, false); } } } $body = $response->getBody(); if (is_null($body)) return; $contentLength = $response->getHeader('Content-Length'); if ($contentLength !== null) { $output = fopen('php://output', 'wb'); if (is_resource($body) && get_resource_type($body) == 'stream') { stream_copy_to_stream($body, $output, $contentLength); } else { fwrite($output, $body, $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. * * @param array $serverArray * @return Request */ static function createFromServerArray(array $serverArray) { $headers = []; $method = null; $url = null; $httpVersion = '1.1'; $protocol = 'http'; $hostName = 'localhost'; foreach ($serverArray as $key => $value) { switch ($key) { case 'SERVER_PROTOCOL' : if ($value === 'HTTP/1.0') { $httpVersion = '1.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) && $value !== 'off') { $protocol = 'https'; } break; default : if (substr($key, 0, 5) === 'HTTP_') { // 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; } } $r = new Request($method, $url, $headers); $r->setHttpVersion($httpVersion); $r->setRawServerData($serverArray); $r->setAbsoluteUrl($protocol . '://' . $hostName . $url); return $r; } }