*\r
* LICENSE:\r
*\r
- * Copyright (c) 2008-2011, Alexey Borzov <avb@php.net>\r
+ * Copyright (c) 2008-2012, Alexey Borzov <avb@php.net>\r
* All rights reserved.\r
*\r
* Redistribution and use in source and binary forms, with or without\r
* modification, are permitted provided that the following conditions\r
* are met:\r
*\r
- * * Redistributions of source code must retain the above copyright\r
+ * * Redistributions of source code must retain the above copyright\r
* notice, this list of conditions and the following disclaimer.\r
- * * Redistributions in binary form must reproduce the above copyright\r
+ * * Redistributions in binary form must reproduce the above copyright\r
* notice, this list of conditions and the following disclaimer in the\r
* documentation and/or other materials provided with the distribution.\r
- * * The names of the authors may not be used to endorse or promote products\r
+ * * The names of the authors may not be used to endorse or promote products\r
* derived from this software without specific prior written permission.\r
*\r
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS\r
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
*\r
- * @category HTTP\r
- * @package HTTP_Request2\r
- * @author Alexey Borzov <avb@php.net>\r
- * @license http://opensource.org/licenses/bsd-license.php New BSD License\r
- * @version SVN: $Id: Response.php 309921 2011-04-03 16:43:02Z avb $\r
- * @link http://pear.php.net/package/HTTP_Request2\r
+ * @category HTTP\r
+ * @package HTTP_Request2\r
+ * @author Alexey Borzov <avb@php.net>\r
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License\r
+ * @version SVN: $Id: Response.php 324936 2012-04-07 07:49:03Z avb $\r
+ * @link http://pear.php.net/package/HTTP_Request2\r
*/\r
\r
/**\r
* var_dump($response->getHeader(), $response->getCookies(), $response->getBody());\r
* </code>\r
*\r
- *\r
- * @category HTTP\r
- * @package HTTP_Request2\r
- * @author Alexey Borzov <avb@php.net>\r
- * @version Release: 2.0.0RC1\r
- * @link http://tools.ietf.org/html/rfc2616#section-6\r
+ * @category HTTP\r
+ * @package HTTP_Request2\r
+ * @author Alexey Borzov <avb@php.net>\r
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License\r
+ * @version Release: 2.1.1\r
+ * @link http://pear.php.net/package/HTTP_Request2\r
+ * @link http://tools.ietf.org/html/rfc2616#section-6\r
*/\r
class HTTP_Request2_Response\r
{\r
- /**\r
- * HTTP protocol version (e.g. 1.0, 1.1)\r
- * @var string\r
- */\r
+ /**\r
+ * HTTP protocol version (e.g. 1.0, 1.1)\r
+ * @var string\r
+ */\r
protected $version;\r
\r
- /**\r
- * Status code\r
- * @var integer\r
- * @link http://tools.ietf.org/html/rfc2616#section-6.1.1\r
- */\r
+ /**\r
+ * Status code\r
+ * @var integer\r
+ * @link http://tools.ietf.org/html/rfc2616#section-6.1.1\r
+ */\r
protected $code;\r
\r
- /**\r
- * Reason phrase\r
- * @var string\r
- * @link http://tools.ietf.org/html/rfc2616#section-6.1.1\r
- */\r
+ /**\r
+ * Reason phrase\r
+ * @var string\r
+ * @link http://tools.ietf.org/html/rfc2616#section-6.1.1\r
+ */\r
protected $reasonPhrase;\r
\r
- /**\r
- * Effective URL (may be different from original request URL in case of redirects)\r
- * @var string\r
- */\r
+ /**\r
+ * Effective URL (may be different from original request URL in case of redirects)\r
+ * @var string\r
+ */\r
protected $effectiveUrl;\r
\r
- /**\r
- * Associative array of response headers\r
- * @var array\r
- */\r
+ /**\r
+ * Associative array of response headers\r
+ * @var array\r
+ */\r
protected $headers = array();\r
\r
- /**\r
- * Cookies set in the response\r
- * @var array\r
- */\r
+ /**\r
+ * Cookies set in the response\r
+ * @var array\r
+ */\r
protected $cookies = array();\r
\r
- /**\r
- * Name of last header processed by parseHederLine()\r
- *\r
- * Used to handle the headers that span multiple lines\r
- *\r
- * @var string\r
- */\r
+ /**\r
+ * Name of last header processed by parseHederLine()\r
+ *\r
+ * Used to handle the headers that span multiple lines\r
+ *\r
+ * @var string\r
+ */\r
protected $lastHeader = null;\r
\r
- /**\r
- * Response body\r
- * @var string\r
- */\r
+ /**\r
+ * Response body\r
+ * @var string\r
+ */\r
protected $body = '';\r
\r
- /**\r
- * Whether the body is still encoded by Content-Encoding\r
- *\r
- * cURL provides the decoded body to the callback; if we are reading from\r
- * socket the body is still gzipped / deflated\r
- *\r
- * @var bool\r
- */\r
+ /**\r
+ * Whether the body is still encoded by Content-Encoding\r
+ *\r
+ * cURL provides the decoded body to the callback; if we are reading from\r
+ * socket the body is still gzipped / deflated\r
+ *\r
+ * @var bool\r
+ */\r
protected $bodyEncoded;\r
\r
- /**\r
- * Associative array of HTTP status code / reason phrase.\r
- *\r
- * @var array\r
- * @link http://tools.ietf.org/html/rfc2616#section-10\r
- */\r
+ /**\r
+ * Associative array of HTTP status code / reason phrase.\r
+ *\r
+ * @var array\r
+ * @link http://tools.ietf.org/html/rfc2616#section-10\r
+ */\r
protected static $phrases = array(\r
\r
// 1xx: Informational - Request received, continuing process\r
\r
);\r
\r
- /**\r
- * Constructor, parses the response status line\r
- *\r
- * @param string Response status line (e.g. "HTTP/1.1 200 OK")\r
- * @param bool Whether body is still encoded by Content-Encoding\r
- * @param string Effective URL of the response\r
- * @throws HTTP_Request2_MessageException if status line is invalid according to spec\r
- */\r
+ /**\r
+ * Returns the default reason phrase for the given code or all reason phrases\r
+ *\r
+ * @param int $code Response code\r
+ *\r
+ * @return string|array|null Default reason phrase for $code if $code is given\r
+ * (null if no phrase is available), array of all\r
+ * reason phrases if $code is null\r
+ * @link http://pear.php.net/bugs/18716\r
+ */\r
+ public static function getDefaultReasonPhrase($code = null)\r
+ {\r
+ if (null === $code) {\r
+ return self::$phrases;\r
+ } else {\r
+ return isset(self::$phrases[$code]) ? self::$phrases[$code] : null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Constructor, parses the response status line\r
+ *\r
+ * @param string $statusLine Response status line (e.g. "HTTP/1.1 200 OK")\r
+ * @param bool $bodyEncoded Whether body is still encoded by Content-Encoding\r
+ * @param string $effectiveUrl Effective URL of the response\r
+ *\r
+ * @throws HTTP_Request2_MessageException if status line is invalid according to spec\r
+ */\r
public function __construct($statusLine, $bodyEncoded = true, $effectiveUrl = null)\r
{\r
if (!preg_match('!^HTTP/(\d\.\d) (\d{3})(?: (.+))?!', $statusLine, $m)) {\r
HTTP_Request2_Exception::MALFORMED_RESPONSE\r
);\r
}\r
- $this->version = $m[1];\r
- $this->code = intval($m[2]);\r
- if (!empty($m[3])) {\r
- $this->reasonPhrase = trim($m[3]);\r
- } elseif (!empty(self::$phrases[$this->code])) {\r
- $this->reasonPhrase = self::$phrases[$this->code];\r
- }\r
+ $this->version = $m[1];\r
+ $this->code = intval($m[2]);\r
+ $this->reasonPhrase = !empty($m[3]) ? trim($m[3]) : self::getDefaultReasonPhrase($this->code);\r
$this->bodyEncoded = (bool)$bodyEncoded;\r
$this->effectiveUrl = (string)$effectiveUrl;\r
}\r
\r
- /**\r
- * Parses the line from HTTP response filling $headers array\r
- *\r
- * The method should be called after reading the line from socket or receiving\r
- * it into cURL callback. Passing an empty string here indicates the end of\r
- * response headers and triggers additional processing, so be sure to pass an\r
- * empty string in the end.\r
- *\r
- * @param string Line from HTTP response\r
- */\r
+ /**\r
+ * Parses the line from HTTP response filling $headers array\r
+ *\r
+ * The method should be called after reading the line from socket or receiving\r
+ * it into cURL callback. Passing an empty string here indicates the end of\r
+ * response headers and triggers additional processing, so be sure to pass an\r
+ * empty string in the end.\r
+ *\r
+ * @param string $headerLine Line from HTTP response\r
+ */\r
public function parseHeaderLine($headerLine)\r
{\r
$headerLine = trim($headerLine, "\r\n");\r
\r
- // empty string signals the end of headers, process the received ones\r
if ('' == $headerLine) {\r
+ // empty string signals the end of headers, process the received ones\r
if (!empty($this->headers['set-cookie'])) {\r
$cookies = is_array($this->headers['set-cookie'])?\r
$this->headers['set-cookie']:\r
}\r
}\r
\r
- // string of the form header-name: header value\r
} elseif (preg_match('!^([^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+):(.+)$!', $headerLine, $m)) {\r
+ // string of the form header-name: header value\r
$name = strtolower($m[1]);\r
$value = trim($m[2]);\r
if (empty($this->headers[$name])) {\r
}\r
$this->lastHeader = $name;\r
\r
- // continuation of a previous header\r
} elseif (preg_match('!^\s+(.+)$!', $headerLine, $m) && $this->lastHeader) {\r
+ // continuation of a previous header\r
if (!is_array($this->headers[$this->lastHeader])) {\r
$this->headers[$this->lastHeader] .= ' ' . trim($m[1]);\r
} else {\r
}\r
}\r
\r
- /**\r
- * Parses a Set-Cookie header to fill $cookies array\r
- *\r
- * @param string value of Set-Cookie header\r
- * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html\r
- */\r
+ /**\r
+ * Parses a Set-Cookie header to fill $cookies array\r
+ *\r
+ * @param string $cookieString value of Set-Cookie header\r
+ *\r
+ * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html\r
+ */\r
protected function parseCookie($cookieString)\r
{\r
$cookie = array(\r
'secure' => false\r
);\r
\r
- // Only a name=value pair\r
if (!strpos($cookieString, ';')) {\r
+ // Only a name=value pair\r
$pos = strpos($cookieString, '=');\r
$cookie['name'] = trim(substr($cookieString, 0, $pos));\r
$cookie['value'] = trim(substr($cookieString, $pos + 1));\r
\r
- // Some optional parameters are supplied\r
} else {\r
+ // Some optional parameters are supplied\r
$elements = explode(';', $cookieString);\r
$pos = strpos($elements[0], '=');\r
$cookie['name'] = trim(substr($elements[0], 0, $pos));\r
$this->cookies[] = $cookie;\r
}\r
\r
- /**\r
- * Appends a string to the response body\r
- * @param string\r
- */\r
+ /**\r
+ * Appends a string to the response body\r
+ *\r
+ * @param string $bodyChunk part of response body\r
+ */\r
public function appendBody($bodyChunk)\r
{\r
$this->body .= $bodyChunk;\r
}\r
\r
- /**\r
- * Returns the effective URL of the response\r
- *\r
- * This may be different from the request URL if redirects were followed.\r
- *\r
- * @return string\r
- * @link http://pear.php.net/bugs/bug.php?id=18412\r
- */\r
+ /**\r
+ * Returns the effective URL of the response\r
+ *\r
+ * This may be different from the request URL if redirects were followed.\r
+ *\r
+ * @return string\r
+ * @link http://pear.php.net/bugs/bug.php?id=18412\r
+ */\r
public function getEffectiveUrl()\r
{\r
return $this->effectiveUrl;\r
}\r
\r
- /**\r
- * Returns the status code\r
- * @return integer\r
- */\r
+ /**\r
+ * Returns the status code\r
+ *\r
+ * @return integer\r
+ */\r
public function getStatus()\r
{\r
return $this->code;\r
}\r
\r
- /**\r
- * Returns the reason phrase\r
- * @return string\r
- */\r
+ /**\r
+ * Returns the reason phrase\r
+ *\r
+ * @return string\r
+ */\r
public function getReasonPhrase()\r
{\r
return $this->reasonPhrase;\r
}\r
\r
- /**\r
- * Whether response is a redirect that can be automatically handled by HTTP_Request2\r
- * @return bool\r
- */\r
+ /**\r
+ * Whether response is a redirect that can be automatically handled by HTTP_Request2\r
+ *\r
+ * @return bool\r
+ */\r
public function isRedirect()\r
{\r
return in_array($this->code, array(300, 301, 302, 303, 307))\r
&& isset($this->headers['location']);\r
}\r
\r
- /**\r
- * Returns either the named header or all response headers\r
- *\r
- * @param string Name of header to return\r
- * @return string|array Value of $headerName header (null if header is\r
- * not present), array of all response headers if\r
- * $headerName is null\r
- */\r
+ /**\r
+ * Returns either the named header or all response headers\r
+ *\r
+ * @param string $headerName Name of header to return\r
+ *\r
+ * @return string|array Value of $headerName header (null if header is\r
+ * not present), array of all response headers if\r
+ * $headerName is null\r
+ */\r
public function getHeader($headerName = null)\r
{\r
if (null === $headerName) {\r
}\r
}\r
\r
- /**\r
- * Returns cookies set in response\r
- *\r
- * @return array\r
- */\r
+ /**\r
+ * Returns cookies set in response\r
+ *\r
+ * @return array\r
+ */\r
public function getCookies()\r
{\r
return $this->cookies;\r
}\r
\r
- /**\r
- * Returns the body of the response\r
- *\r
- * @return string\r
- * @throws HTTP_Request2_Exception if body cannot be decoded\r
- */\r
+ /**\r
+ * Returns the body of the response\r
+ *\r
+ * @return string\r
+ * @throws HTTP_Request2_Exception if body cannot be decoded\r
+ */\r
public function getBody()\r
{\r
- if (0 == strlen($this->body) || !$this->bodyEncoded ||\r
- !in_array(strtolower($this->getHeader('content-encoding')), array('gzip', 'deflate'))\r
+ if (0 == strlen($this->body) || !$this->bodyEncoded\r
+ || !in_array(strtolower($this->getHeader('content-encoding')), array('gzip', 'deflate'))\r
) {\r
return $this->body;\r
\r
} else {\r
if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {\r
$oldEncoding = mb_internal_encoding();\r
- mb_internal_encoding('iso-8859-1');\r
+ mb_internal_encoding('8bit');\r
}\r
\r
try {\r
switch (strtolower($this->getHeader('content-encoding'))) {\r
- case 'gzip':\r
- $decoded = self::decodeGzip($this->body);\r
- break;\r
- case 'deflate':\r
- $decoded = self::decodeDeflate($this->body);\r
+ case 'gzip':\r
+ $decoded = self::decodeGzip($this->body);\r
+ break;\r
+ case 'deflate':\r
+ $decoded = self::decodeDeflate($this->body);\r
}\r
} catch (Exception $e) {\r
}\r
}\r
}\r
\r
- /**\r
- * Get the HTTP version of the response\r
- *\r
- * @return string\r
- */\r
+ /**\r
+ * Get the HTTP version of the response\r
+ *\r
+ * @return string\r
+ */\r
public function getVersion()\r
{\r
return $this->version;\r
}\r
\r
- /**\r
- * Decodes the message-body encoded by gzip\r
- *\r
- * The real decoding work is done by gzinflate() built-in function, this\r
- * method only parses the header and checks data for compliance with\r
- * RFC 1952\r
- *\r
- * @param string gzip-encoded data\r
- * @return string decoded data\r
- * @throws HTTP_Request2_LogicException\r
- * @throws HTTP_Request2_MessageException\r
- * @link http://tools.ietf.org/html/rfc1952\r
- */\r
+ /**\r
+ * Decodes the message-body encoded by gzip\r
+ *\r
+ * The real decoding work is done by gzinflate() built-in function, this\r
+ * method only parses the header and checks data for compliance with\r
+ * RFC 1952\r
+ *\r
+ * @param string $data gzip-encoded data\r
+ *\r
+ * @return string decoded data\r
+ * @throws HTTP_Request2_LogicException\r
+ * @throws HTTP_Request2_MessageException\r
+ * @link http://tools.ietf.org/html/rfc1952\r
+ */\r
public static function decodeGzip($data)\r
{\r
$length = strlen($data);\r
return $unpacked;\r
}\r
\r
- /**\r
- * Decodes the message-body encoded by deflate\r
- *\r
- * @param string deflate-encoded data\r
- * @return string decoded data\r
- * @throws HTTP_Request2_LogicException\r
- */\r
+ /**\r
+ * Decodes the message-body encoded by deflate\r
+ *\r
+ * @param string $data deflate-encoded data\r
+ *\r
+ * @return string decoded data\r
+ * @throws HTTP_Request2_LogicException\r
+ */\r
public static function decodeDeflate($data)\r
{\r
if (!function_exists('gzuncompress')) {\r