1<?php
2
3namespace Sabre\HTTP;
4
5/**
6 * PHP SAPI
7 *
8 * This object is responsible for:
9 * 1. Constructing a Request object based on the current HTTP request sent to
10 *    the PHP process.
11 * 2. Sending the Response object back to the client.
12 *
13 * It could be said that this class provides a mapping between the Request and
14 * Response objects, and php's:
15 *
16 * * $_SERVER
17 * * $_POST
18 * * $_FILES
19 * * php://input
20 * * echo()
21 * * header()
22 * * php://output
23 *
24 * You can choose to either call all these methods statically, but you can also
25 * instantiate this as an object to allow for polymorhpism.
26 *
27 * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
28 * @author Evert Pot (http://evertpot.com/)
29 * @license http://sabre.io/license/ Modified BSD License
30 */
31class Sapi {
32
33    /**
34     * This static method will create a new Request object, based on the
35     * current PHP request.
36     *
37     * @return Request
38     */
39    static function getRequest() {
40
41        $r = self::createFromServerArray($_SERVER);
42        $r->setBody(fopen('php://input', 'r'));
43        $r->setPostData($_POST);
44        return $r;
45
46    }
47
48    /**
49     * Sends the HTTP response back to a HTTP client.
50     *
51     * This calls php's header() function and streams the body to php://output.
52     *
53     * @param ResponseInterface $response
54     * @return void
55     */
56    static function sendResponse(ResponseInterface $response) {
57
58        header('HTTP/' . $response->getHttpVersion() . ' ' . $response->getStatus() . ' ' . $response->getStatusText());
59        foreach ($response->getHeaders() as $key => $value) {
60
61            foreach ($value as $k => $v) {
62                if ($k === 0) {
63                    header($key . ': ' . $v);
64                } else {
65                    header($key . ': ' . $v, false);
66                }
67            }
68
69        }
70
71        $body = $response->getBody();
72        if (is_null($body)) return;
73
74        $contentLength = $response->getHeader('Content-Length');
75        if ($contentLength !== null) {
76            $output = fopen('php://output', 'wb');
77            if (is_resource($body) && get_resource_type($body) == 'stream') {
78                stream_copy_to_stream($body, $output, $contentLength);
79            } else {
80                fwrite($output, $body, $contentLength);
81            }
82        } else {
83            file_put_contents('php://output', $body);
84        }
85
86        if (is_resource($body)) {
87            fclose($body);
88        }
89
90    }
91
92    /**
93     * This static method will create a new Request object, based on a PHP
94     * $_SERVER array.
95     *
96     * @param array $serverArray
97     * @return Request
98     */
99    static function createFromServerArray(array $serverArray) {
100
101        $headers = [];
102        $method = null;
103        $url = null;
104        $httpVersion = '1.1';
105
106        $protocol = 'http';
107        $hostName = 'localhost';
108
109        foreach ($serverArray as $key => $value) {
110
111            switch ($key) {
112
113                case 'SERVER_PROTOCOL' :
114                    if ($value === 'HTTP/1.0') {
115                        $httpVersion = '1.0';
116                    }
117                    break;
118                case 'REQUEST_METHOD' :
119                    $method = $value;
120                    break;
121                case 'REQUEST_URI' :
122                    $url = $value;
123                    break;
124
125                // These sometimes should up without a HTTP_ prefix
126                case 'CONTENT_TYPE' :
127                    $headers['Content-Type'] = $value;
128                    break;
129                case 'CONTENT_LENGTH' :
130                    $headers['Content-Length'] = $value;
131                    break;
132
133                // mod_php on apache will put credentials in these variables.
134                // (fast)cgi does not usually do this, however.
135                case 'PHP_AUTH_USER' :
136                    if (isset($serverArray['PHP_AUTH_PW'])) {
137                        $headers['Authorization'] = 'Basic ' . base64_encode($value . ':' . $serverArray['PHP_AUTH_PW']);
138                    }
139                    break;
140
141                // Similarly, mod_php may also screw around with digest auth.
142                case 'PHP_AUTH_DIGEST' :
143                    $headers['Authorization'] = 'Digest ' . $value;
144                    break;
145
146                // Apache may prefix the HTTP_AUTHORIZATION header with
147                // REDIRECT_, if mod_rewrite was used.
148                case 'REDIRECT_HTTP_AUTHORIZATION' :
149                    $headers['Authorization'] = $value;
150                    break;
151
152                case 'HTTP_HOST' :
153                    $hostName = $value;
154                    $headers['Host'] = $value;
155                    break;
156
157                case 'HTTPS' :
158                    if (!empty($value) && $value !== 'off') {
159                        $protocol = 'https';
160                    }
161                    break;
162
163                default :
164                    if (substr($key, 0, 5) === 'HTTP_') {
165                        // It's a HTTP header
166
167                        // Normalizing it to be prettier
168                        $header = strtolower(substr($key, 5));
169
170                        // Transforming dashes into spaces, and uppercasing
171                        // every first letter.
172                        $header = ucwords(str_replace('_', ' ', $header));
173
174                        // Turning spaces into dashes.
175                        $header = str_replace(' ', '-', $header);
176                        $headers[$header] = $value;
177
178                    }
179                    break;
180
181
182            }
183
184        }
185
186        $r = new Request($method, $url, $headers);
187        $r->setHttpVersion($httpVersion);
188        $r->setRawServerData($serverArray);
189        $r->setAbsoluteUrl($protocol . '://' . $hostName . $url);
190        return $r;
191
192    }
193
194}
195