8 * Copyright (c) 2009-2013 Nicholas J Humfrey.
9 * Copyright (c) 2005-2009 Zend Technologies USA Inc.
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 * 3. The name of the author 'Nicholas J Humfrey" may be used to endorse or
20 * promote products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
36 * @copyright Copyright (c) 2009-2013 Nicholas J Humfrey
37 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc.
38 * @license http://www.opensource.org/licenses/bsd-license.php
42 * Class that represents an HTTP 1.0 / 1.1 response message.
45 * @copyright Copyright (c) 2009-2013 Nicholas J Humfrey
46 * Copyright (c) 2005-2009 Zend Technologies USA Inc.
47 * @license http://www.opensource.org/licenses/bsd-license.php
49 class EasyRdf_Http_Response
53 * The HTTP response status code
60 * The HTTP response code as string
61 * (e.g. 'Not Found' for 404 or 'Internal Server Error' for 500)
68 * The HTTP response headers array
72 private $headers = array();
75 * The HTTP response body
84 * @param int $status HTTP Status code
85 * @param array $headers The HTTP response headers
86 * @param string $body The content of the response
87 * @param string $version The HTTP Version (1.0 or 1.1)
88 * @param string $message The HTTP response Message
89 * @return object EasyRdf_Http_Response
91 public function __construct(
98 $this->status = intval($status);
100 $this->version = $version;
101 $this->message = $message;
103 foreach ($headers as $k => $v) {
104 $k = ucwords(strtolower($k));
105 $this->headers[$k] = $v;
110 * Check whether the response in successful
114 public function isSuccessful()
116 return ($this->status >= 200 && $this->status < 300);
120 * Check whether the response is an error
124 public function isError()
126 return ($this->status >= 400 && $this->status < 600);
130 * Check whether the response is a redirection
134 public function isRedirect()
136 return ($this->status >= 300 && $this->status < 400);
140 * Get the HTTP response status code
144 public function getStatus()
146 return $this->status;
150 * Return a message describing the HTTP response code
151 * (Eg. "OK", "Not Found", "Moved Permanently")
155 public function getMessage()
157 return $this->message;
161 * Get the response body as string
165 public function getBody()
167 // Decode the body if it was transfer-encoded
168 switch (strtolower($this->getHeader('transfer-encoding'))) {
169 // Handle chunked body
171 return self::decodeChunkedBody($this->body);
174 // No transfer encoding, or unknown encoding extension:
183 * Get the raw response body (as transfered "on wire") as string
185 * If the body is encoded (with Transfer-Encoding, not content-encoding -
186 * IE "chunked" body), gzip compressed, etc. it will not be decoded.
190 public function getRawBody()
196 * Get the HTTP version of the response
200 public function getVersion()
202 return $this->version;
206 * Get the response headers
210 public function getHeaders()
212 return $this->headers;
216 * Get a specific header as string, or null if it is not set
218 * @param string$header
219 * @return string|array|null
221 public function getHeader($header)
223 $header = ucwords(strtolower($header));
224 if (array_key_exists($header, $this->headers)) {
225 return $this->headers[$header];
232 * Get all headers as string
234 * @param boolean $statusLine Whether to return the first status line (ie "HTTP 200 OK")
235 * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
238 public function getHeadersAsString($statusLine = true, $br = "\n")
243 $str = "HTTP/{$this->version} {$this->status} {$this->message}{$br}";
246 // Iterate over the headers and stringify them
247 foreach ($this->headers as $name => $value) {
248 if (is_string($value)) {
249 $str .= "{$name}: {$value}{$br}";
250 } elseif (is_array($value)) {
251 foreach ($value as $subval) {
252 $str .= "{$name}: {$subval}{$br}";
261 * Create an EasyRdf_Http_Response object from a HTTP response string
263 * @param string $responseStr
264 * @return EasyRdf_Http_Response
266 public static function fromString($responseStr)
268 // First, split body and headers
269 $matches = preg_split('|(?:\r?\n){2}|m', $responseStr, 2);
270 if ($matches and sizeof($matches) == 2) {
271 list ($headerLines, $body) = $matches;
273 throw new EasyRdf_Exception(
274 "Failed to parse HTTP response."
278 // Split headers part to lines
279 $headerLines = preg_split('|[\r\n]+|m', $headerLines);
280 $status = array_shift($headerLines);
281 if (preg_match("|^HTTP/([\d\.x]+) (\d+) ([^\r\n]+)|", $status, $m)) {
286 throw new EasyRdf_Exception(
287 "Failed to parse HTTP response status line."
291 // Process the rest of the header lines
293 foreach ($headerLines as $line) {
294 if (preg_match("|^([\w-]+):\s+(.+)$|", $line, $m)) {
295 $hName = ucwords(strtolower($m[1]));
298 if (isset($headers[$hName])) {
299 if (! is_array($headers[$hName])) {
300 $headers[$hName] = array($headers[$hName]);
302 $headers[$hName][] = $hValue;
304 $headers[$hName] = $hValue;
309 return new EasyRdf_Http_Response($status, $headers, $body, $version, $message);
314 * Decode a "chunked" transfer-encoded body and return the decoded text
316 * @param string $body
319 public static function decodeChunkedBody($body)
323 while (trim($body)) {
324 if (preg_match('/^([\da-fA-F]+)[^\r\n]*\r\n/sm', $body, $m)) {
325 $length = hexdec(trim($m[1]));
326 $cut = strlen($m[0]);
327 $decBody .= substr($body, $cut, $length);
328 $body = substr($body, $cut + $length + 2);
330 throw new EasyRdf_Exception(
331 "Failed to decode chunked body in HTTP response."
341 * Get the entire response as string
343 * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
346 public function asString($br = "\n")
348 return $this->getHeadersAsString(true, $br) . $br . $this->getRawBody();
352 * Implements magic __toString()
356 public function __toString()
358 return $this->asString();