1<?php
2
3namespace Sabre\HTTP;
4
5/**
6 * This is the abstract base class for both the Request and Response objects.
7 *
8 * This object contains a few simple methods that are shared by both.
9 *
10 * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
11 * @author Evert Pot (http://evertpot.com/)
12 * @license http://sabre.io/license/ Modified BSD License
13 */
14abstract class Message implements MessageInterface {
15
16    /**
17     * Request body
18     *
19     * This should be a stream resource
20     *
21     * @var resource
22     */
23    protected $body;
24
25    /**
26     * Contains the list of HTTP headers
27     *
28     * @var array
29     */
30    protected $headers = [];
31
32    /**
33     * HTTP message version (1.0 or 1.1)
34     *
35     * @var string
36     */
37    protected $httpVersion = '1.1';
38
39    /**
40     * Returns the body as a readable stream resource.
41     *
42     * Note that the stream may not be rewindable, and therefore may only be
43     * read once.
44     *
45     * @return resource
46     */
47    function getBodyAsStream() {
48
49        $body = $this->getBody();
50        if (is_string($body) || is_null($body)) {
51            $stream = fopen('php://temp', 'r+');
52            fwrite($stream, $body);
53            rewind($stream);
54            return $stream;
55        }
56        return $body;
57
58    }
59
60    /**
61     * Returns the body as a string.
62     *
63     * Note that because the underlying data may be based on a stream, this
64     * method could only work correctly the first time.
65     *
66     * @return string
67     */
68    function getBodyAsString() {
69
70        $body = $this->getBody();
71        if (is_string($body)) {
72            return $body;
73        }
74        if (is_null($body)) {
75            return '';
76        }
77        return stream_get_contents($body);
78
79    }
80
81    /**
82     * Returns the message body, as it's internal representation.
83     *
84     * This could be either a string or a stream.
85     *
86     * @return resource|string
87     */
88    function getBody() {
89
90        return $this->body;
91
92    }
93
94    /**
95     * Replaces the body resource with a new stream or string.
96     *
97     * @param resource|string $body
98     */
99    function setBody($body) {
100
101        $this->body = $body;
102
103    }
104
105    /**
106     * Returns all the HTTP headers as an array.
107     *
108     * Every header is returned as an array, with one or more values.
109     *
110     * @return array
111     */
112    function getHeaders() {
113
114        $result = [];
115        foreach ($this->headers as $headerInfo) {
116            $result[$headerInfo[0]] = $headerInfo[1];
117        }
118        return $result;
119
120    }
121
122    /**
123     * Will return true or false, depending on if a HTTP header exists.
124     *
125     * @param string $name
126     * @return bool
127     */
128    function hasHeader($name) {
129
130        return isset($this->headers[strtolower($name)]);
131
132    }
133
134    /**
135     * Returns a specific HTTP header, based on it's name.
136     *
137     * The name must be treated as case-insensitive.
138     * If the header does not exist, this method must return null.
139     *
140     * If a header appeared more than once in a HTTP request, this method will
141     * concatenate all the values with a comma.
142     *
143     * Note that this not make sense for all headers. Some, such as
144     * `Set-Cookie` cannot be logically combined with a comma. In those cases
145     * you *should* use getHeaderAsArray().
146     *
147     * @param string $name
148     * @return string|null
149     */
150    function getHeader($name) {
151
152        $name = strtolower($name);
153
154        if (isset($this->headers[$name])) {
155            return implode(',', $this->headers[$name][1]);
156        }
157        return null;
158
159    }
160
161    /**
162     * Returns a HTTP header as an array.
163     *
164     * For every time the HTTP header appeared in the request or response, an
165     * item will appear in the array.
166     *
167     * If the header did not exists, this method will return an empty array.
168     *
169     * @param string $name
170     * @return string[]
171     */
172    function getHeaderAsArray($name) {
173
174        $name = strtolower($name);
175
176        if (isset($this->headers[$name])) {
177            return $this->headers[$name][1];
178        }
179
180        return [];
181
182    }
183
184    /**
185     * Updates a HTTP header.
186     *
187     * The case-sensitity of the name value must be retained as-is.
188     *
189     * If the header already existed, it will be overwritten.
190     *
191     * @param string $name
192     * @param string|string[] $value
193     * @return void
194     */
195    function setHeader($name, $value) {
196
197        $this->headers[strtolower($name)] = [$name, (array)$value];
198
199    }
200
201    /**
202     * Sets a new set of HTTP headers.
203     *
204     * The headers array should contain headernames for keys, and their value
205     * should be specified as either a string or an array.
206     *
207     * Any header that already existed will be overwritten.
208     *
209     * @param array $headers
210     * @return void
211     */
212    function setHeaders(array $headers) {
213
214        foreach ($headers as $name => $value) {
215            $this->setHeader($name, $value);
216        }
217
218    }
219
220    /**
221     * Adds a HTTP header.
222     *
223     * This method will not overwrite any existing HTTP header, but instead add
224     * another value. Individual values can be retrieved with
225     * getHeadersAsArray.
226     *
227     * @param string $name
228     * @param string $value
229     * @return void
230     */
231    function addHeader($name, $value) {
232
233        $lName = strtolower($name);
234        if (isset($this->headers[$lName])) {
235            $this->headers[$lName][1] = array_merge(
236                $this->headers[$lName][1],
237                (array)$value
238            );
239        } else {
240            $this->headers[$lName] = [
241                $name,
242                (array)$value
243            ];
244        }
245
246    }
247
248    /**
249     * Adds a new set of HTTP headers.
250     *
251     * Any existing headers will not be overwritten.
252     *
253     * @param array $headers
254     * @return void
255     */
256    function addHeaders(array $headers) {
257
258        foreach ($headers as $name => $value) {
259            $this->addHeader($name, $value);
260        }
261
262    }
263
264
265    /**
266     * Removes a HTTP header.
267     *
268     * The specified header name must be treated as case-insenstive.
269     * This method should return true if the header was successfully deleted,
270     * and false if the header did not exist.
271     *
272     * @return bool
273     */
274    function removeHeader($name) {
275
276        $name = strtolower($name);
277        if (!isset($this->headers[$name])) {
278            return false;
279        }
280        unset($this->headers[$name]);
281        return true;
282
283    }
284
285    /**
286     * Sets the HTTP version.
287     *
288     * Should be 1.0 or 1.1.
289     *
290     * @param string $version
291     * @return void
292     */
293    function setHttpVersion($version) {
294
295        $this->httpVersion = $version;
296
297    }
298
299    /**
300     * Returns the HTTP version.
301     *
302     * @return string
303     */
304    function getHttpVersion() {
305
306        return $this->httpVersion;
307
308    }
309}
310