Source for file Response.php
Documentation is available at Response.php
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
* @package Microsoft_Http
* @version $Id: Response.php 61036 2011-04-19 06:09:54Z unknown $
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* Microsoft_Http_Response represents an HTTP 1.0 / 1.1 response message. It
* includes easy access to all the response's different elemts, as well as some
* convenience methods for parsing and validating HTTP responses.
* @package Microsoft_Http
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* List of all known HTTP response codes - used by responseCodeAsText() to
* translate numeric codes to messages.
protected static $messages = array(
101 => 'Switching Protocols',
203 => 'Non-Authoritative Information',
206 => 'Partial Content',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
// 306 is deprecated but reserved
307 => 'Temporary Redirect',
402 => 'Payment Required',
405 => 'Method Not Allowed',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
500 => 'Internal Server Error',
501 => 'Not Implemented',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
509 => 'Bandwidth Limit Exceeded'
* The HTTP version (1.0, 1.1)
* The HTTP response code as string
* (e.g. 'Not Found' for 404 or 'Internal Server Error' for 500)
* The HTTP response headers array
* HTTP response constructor
* In most cases, you would use Microsoft_Http_Response::fromString to parse an HTTP
* response string and create a new Microsoft_Http_Response object.
* NOTE: The constructor no longer accepts nulls or empty values for the code and
* headers and will throw an exception if the passed values do not form a valid HTTP
* If no message is passed, the message will be guessed according to the response code.
* @param int $code Response code (200, 404, ...)
* @param array $headers Headers array
* @param string $body Response body
* @param string $version HTTP version
* @param string $message Response code as text
* @throws Microsoft_Http_Exception
public function __construct($code, $headers, $body = null, $version = '1.1', $message = null)
// Make sure the response code is valid and set it
if (self::responseCodeAsText($code) === null) {
require_once 'Microsoft/Http/Exception.php';
// Make sure we got valid headers and set them
require_once 'Microsoft/Http/Exception.php';
foreach ($headers as $name => $value) {
list ($name, $value) = explode(": ", $value, 1);
require_once 'Microsoft/Http/Exception.php';
// If we got the response message, set it. Else, set it according to
$this->message = self::responseCodeAsText($code);
* Check whether the response is an error
if ($restype == 4 || $restype == 5) {
* Check whether the response in successful
if ($restype == 2 || $restype == 1) { // Shouldn't 3xx count as success as well ???
* Check whether the response is a redirection
* Get the response body as string
* This method returns the body of the HTTP response (the content), as it
* should be in it's readable version - that is, after decoding it (if it
* was decoded), deflating it (if it was gzip compressed), etc.
* If you want to get the raw body (as transfered on wire) use
* $this->getRawBody() instead.
// Decode the body if it was transfer-encoded
$body = self::decodeChunkedBody($this->body);
// No transfer encoding, or unknown encoding extension:
// Decode any content-encoding (gzip or deflate) if needed
$body = self::decodeGzip($body);
// Handle deflate encoding
$body = self::decodeDeflate($body);
* Get the raw response body (as transfered "on wire") as string
* If the body is encoded (with Transfer-Encoding, not content-encoding -
* IE "chunked" body), gzip compressed, etc. it will not be decoded.
* Get the HTTP version of the response
* Get the HTTP response status code
* Return a message describing the HTTP response code
* (Eg. "OK", "Not Found", "Moved Permanently")
* Get the response headers
* Get a specific header as string, or null if it is not set
* @return string|array|null
* Get all headers as string
* @param boolean $status_line Whether to return the first status line (IE "HTTP 200 OK")
* @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
// Iterate over the headers and stringify them
foreach ($this->headers as $name => $value)
$str .= "{ $name}: { $value}{ $br}";
elseif (is_array($value)) {
foreach ($value as $subval) {
$str .= "{ $name}: { $subval}{ $br}";
* Get the entire response as string
* @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
public function asString($br = "\n")
* Implements magic __toString()
public function __toString()
* A convenience function that returns a text representation of
* HTTP response codes. Returns 'Unknown' for unknown codes.
* Returns array of all codes, if $code is not specified.
* Conforms to HTTP/1.1 as defined in RFC 2616 (except for 'Unknown')
* See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 for reference
* @param int $code HTTP response code
* @param boolean $http11 Use HTTP version 1.1
public static function responseCodeAsText($code = null, $http11 = true)
$messages = self::$messages;
if (! $http11) $messages[302] = 'Moved Temporarily';
} elseif (isset($messages[$code])) {
* Extract the response code from a response string
* @param string $response_str
public static function extractCode($response_str)
preg_match("|^HTTP/[\d\.x]+ (\d+)|", $response_str, $m);
* Extract the HTTP message from a response
* @param string $response_str
public static function extractMessage($response_str)
preg_match("|^HTTP/[\d\.x]+ \d+ ([^\r\n]+)|", $response_str, $m);
* Extract the HTTP version from a response
* @param string $response_str
public static function extractVersion($response_str)
preg_match("|^HTTP/([\d\.x]+) \d+|", $response_str, $m);
* Extract the headers from a response string
* @param string $response_str
public static function extractHeaders($response_str)
// First, split body and headers
$parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2);
if (! $parts[0]) return $headers;
// Split headers part to lines
$lines = explode("\n", $parts[0]);
foreach($lines as $line) {
$line = trim($line, "\r\n");
if (preg_match("|^([\w-]+):\s+(.+)|", $line, $m)) {
$h_name = strtolower($m[1]);
if (isset($headers[$h_name])) {
if (! is_array($headers[$h_name])) {
$headers[$h_name] = array($headers[$h_name]);
$headers[$h_name][] = $h_value;
$headers[$h_name] = $h_value;
} elseif (preg_match("|^\s+(.+)$|", $line, $m) && $last_header !== null) {
if (is_array($headers[$last_header])) {
end($headers[$last_header]);
$last_header_key = key($headers[$last_header]);
$headers[$last_header][$last_header_key] .= $m[1];
$headers[$last_header] .= $m[1];
* Extract the body from a response string
* @param string $response_str
public static function extractBody($response_str)
$parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2);
* Decode a "chunked" transfer-encoded body and return the decoded text
public static function decodeChunkedBody($body)
// If mbstring overloads substr and strlen functions, we have to
// override it's internal encoding
if (function_exists('mb_internal_encoding') &&
((int) ini_get('mbstring.func_overload')) & 2) {
$mbIntEnc = mb_internal_encoding();
mb_internal_encoding('ASCII');
if (! preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm", $body, $m)) {
require_once 'Microsoft/Http/Exception.php';
throw new Microsoft_Http_Exception("Error parsing body - doesn't seem to be a chunked message");
$length = hexdec(trim($m[1]));
$decBody .= substr($body, $cut, $length);
$body = substr($body, $cut + $length + 2);
mb_internal_encoding($mbIntEnc);
* Decode a gzip encoded message (when Content-encoding = gzip)
* Currently requires PHP with zlib support
public static function decodeGzip($body)
if (! function_exists('gzinflate')) {
require_once 'Microsoft/Http/Exception.php';
throw new Microsoft_Http_Exception(
'zlib extension is required in order to decode "gzip" encoding'
return gzinflate(substr($body, 10));
* Decode a zlib deflated message (when Content-encoding = deflate)
* Currently requires PHP with zlib support
public static function decodeDeflate($body)
if (! function_exists('gzuncompress')) {
require_once 'Microsoft/Http/Exception.php';
throw new Microsoft_Http_Exception(
'zlib extension is required in order to decode "deflate" encoding'
* Some servers (IIS ?) send a broken deflate response, without the
* RFC-required zlib header.
* We try to detect the zlib header, and if it does not exsit we
* teat the body is plain DEFLATE content.
* This method was adapted from PEAR HTTP_Request2 by (c) Alexey Borzov
* @link http://framework.zend.com/issues/browse/ZF-6040
$zlibHeader = unpack('n', substr($body, 0, 2));
if ($zlibHeader[1] % 31 == 0) {
return gzuncompress($body);
* Create a new Microsoft_Http_Response object from a string
* @param string $response_str
* @return Microsoft_Http_Response
public static function fromString($response_str)
$code = self::extractCode($response_str);
$headers = self::extractHeaders($response_str);
$body = self::extractBody($response_str);
$version = self::extractVersion($response_str);
$message = self::extractMessage($response_str);
return new Microsoft_Http_Response($code, $headers, $body, $version, $message);
|