PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/app/vendors/Zend/Http/Response.php

https://github.com/rogerwu99/randomizr
PHP | 624 lines | 331 code | 67 blank | 226 comment | 44 complexity | 864429b7fa3323ae219ae3b99d9fb5b2 MD5 | raw file
Possible License(s): MIT, LGPL-3.0
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Http
  17. * @subpackage Response
  18. * @version $Id: Response.php 12519 2008-11-10 18:41:24Z alexander $
  19. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  20. * @license http://framework.zend.com/license/new-bsd New BSD License
  21. */
  22. /**
  23. * Zend_Http_Response represents an HTTP 1.0 / 1.1 response message. It
  24. * includes easy access to all the response's different elemts, as well as some
  25. * convenience methods for parsing and validating HTTP responses.
  26. *
  27. * @package Zend_Http
  28. * @subpackage Response
  29. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  30. * @license http://framework.zend.com/license/new-bsd New BSD License
  31. */
  32. class Zend_Http_Response
  33. {
  34. /**
  35. * List of all known HTTP response codes - used by responseCodeAsText() to
  36. * translate numeric codes to messages.
  37. *
  38. * @var array
  39. */
  40. protected static $messages = array(
  41. // Informational 1xx
  42. 100 => 'Continue',
  43. 101 => 'Switching Protocols',
  44. // Success 2xx
  45. 200 => 'OK',
  46. 201 => 'Created',
  47. 202 => 'Accepted',
  48. 203 => 'Non-Authoritative Information',
  49. 204 => 'No Content',
  50. 205 => 'Reset Content',
  51. 206 => 'Partial Content',
  52. // Redirection 3xx
  53. 300 => 'Multiple Choices',
  54. 301 => 'Moved Permanently',
  55. 302 => 'Found', // 1.1
  56. 303 => 'See Other',
  57. 304 => 'Not Modified',
  58. 305 => 'Use Proxy',
  59. // 306 is deprecated but reserved
  60. 307 => 'Temporary Redirect',
  61. // Client Error 4xx
  62. 400 => 'Bad Request',
  63. 401 => 'Unauthorized',
  64. 402 => 'Payment Required',
  65. 403 => 'Forbidden',
  66. 404 => 'Not Found',
  67. 405 => 'Method Not Allowed',
  68. 406 => 'Not Acceptable',
  69. 407 => 'Proxy Authentication Required',
  70. 408 => 'Request Timeout',
  71. 409 => 'Conflict',
  72. 410 => 'Gone',
  73. 411 => 'Length Required',
  74. 412 => 'Precondition Failed',
  75. 413 => 'Request Entity Too Large',
  76. 414 => 'Request-URI Too Long',
  77. 415 => 'Unsupported Media Type',
  78. 416 => 'Requested Range Not Satisfiable',
  79. 417 => 'Expectation Failed',
  80. // Server Error 5xx
  81. 500 => 'Internal Server Error',
  82. 501 => 'Not Implemented',
  83. 502 => 'Bad Gateway',
  84. 503 => 'Service Unavailable',
  85. 504 => 'Gateway Timeout',
  86. 505 => 'HTTP Version Not Supported',
  87. 509 => 'Bandwidth Limit Exceeded'
  88. );
  89. /**
  90. * The HTTP version (1.0, 1.1)
  91. *
  92. * @var string
  93. */
  94. protected $version;
  95. /**
  96. * The HTTP response code
  97. *
  98. * @var int
  99. */
  100. protected $code;
  101. /**
  102. * The HTTP response code as string
  103. * (e.g. 'Not Found' for 404 or 'Internal Server Error' for 500)
  104. *
  105. * @var string
  106. */
  107. protected $message;
  108. /**
  109. * The HTTP response headers array
  110. *
  111. * @var array
  112. */
  113. protected $headers = array();
  114. /**
  115. * The HTTP response body
  116. *
  117. * @var string
  118. */
  119. protected $body;
  120. /**
  121. * HTTP response constructor
  122. *
  123. * In most cases, you would use Zend_Http_Response::fromString to parse an HTTP
  124. * response string and create a new Zend_Http_Response object.
  125. *
  126. * NOTE: The constructor no longer accepts nulls or empty values for the code and
  127. * headers and will throw an exception if the passed values do not form a valid HTTP
  128. * responses.
  129. *
  130. * If no message is passed, the message will be guessed according to the response code.
  131. *
  132. * @param int $code Response code (200, 404, ...)
  133. * @param array $headers Headers array
  134. * @param string $body Response body
  135. * @param string $version HTTP version
  136. * @param string $message Response code as text
  137. * @throws Zend_Http_Exception
  138. */
  139. public function __construct($code, $headers, $body = null, $version = '1.1', $message = null)
  140. {
  141. // Make sure the response code is valid and set it
  142. if (self::responseCodeAsText($code) === null) {
  143. require_once 'Zend/Http/Exception.php';
  144. throw new Zend_Http_Exception("{$code} is not a valid HTTP response code");
  145. }
  146. $this->code = $code;
  147. // Make sure we got valid headers and set them
  148. if (! is_array($headers)) {
  149. require_once 'Zend/Http/Exception.php';
  150. throw new Zend_Http_Exception('No valid headers were passed');
  151. }
  152. foreach ($headers as $name => $value) {
  153. if (is_int($name))
  154. list($name, $value) = explode(": ", $value, 1);
  155. $this->headers[ucwords(strtolower($name))] = $value;
  156. }
  157. // Set the body
  158. $this->body = $body;
  159. // Set the HTTP version
  160. if (! preg_match('|^\d\.\d$|', $version)) {
  161. require_once 'Zend/Http/Exception.php';
  162. throw new Zend_Http_Exception("Invalid HTTP response version: $version");
  163. }
  164. $this->version = $version;
  165. // If we got the response message, set it. Else, set it according to
  166. // the response code
  167. if (is_string($message)) {
  168. $this->message = $message;
  169. } else {
  170. $this->message = self::responseCodeAsText($code);
  171. }
  172. }
  173. /**
  174. * Check whether the response is an error
  175. *
  176. * @return boolean
  177. */
  178. public function isError()
  179. {
  180. $restype = floor($this->code / 100);
  181. if ($restype == 4 || $restype == 5) {
  182. return true;
  183. }
  184. return false;
  185. }
  186. /**
  187. * Check whether the response in successful
  188. *
  189. * @return boolean
  190. */
  191. public function isSuccessful()
  192. {
  193. $restype = floor($this->code / 100);
  194. if ($restype == 2 || $restype == 1) { // Shouldn't 3xx count as success as well ???
  195. return true;
  196. }
  197. return false;
  198. }
  199. /**
  200. * Check whether the response is a redirection
  201. *
  202. * @return boolean
  203. */
  204. public function isRedirect()
  205. {
  206. $restype = floor($this->code / 100);
  207. if ($restype == 3) {
  208. return true;
  209. }
  210. return false;
  211. }
  212. /**
  213. * Get the response body as string
  214. *
  215. * This method returns the body of the HTTP response (the content), as it
  216. * should be in it's readable version - that is, after decoding it (if it
  217. * was decoded), deflating it (if it was gzip compressed), etc.
  218. *
  219. * If you want to get the raw body (as transfered on wire) use
  220. * $this->getRawBody() instead.
  221. *
  222. * @return string
  223. */
  224. public function getBody()
  225. {
  226. $body = '';
  227. // Decode the body if it was transfer-encoded
  228. switch ($this->getHeader('transfer-encoding')) {
  229. // Handle chunked body
  230. case 'chunked':
  231. $body = self::decodeChunkedBody($this->body);
  232. break;
  233. // No transfer encoding, or unknown encoding extension:
  234. // return body as is
  235. default:
  236. $body = $this->body;
  237. break;
  238. }
  239. // Decode any content-encoding (gzip or deflate) if needed
  240. switch (strtolower($this->getHeader('content-encoding'))) {
  241. // Handle gzip encoding
  242. case 'gzip':
  243. $body = self::decodeGzip($body);
  244. break;
  245. // Handle deflate encoding
  246. case 'deflate':
  247. $body = self::decodeDeflate($body);
  248. break;
  249. default:
  250. break;
  251. }
  252. return $body;
  253. }
  254. /**
  255. * Get the raw response body (as transfered "on wire") as string
  256. *
  257. * If the body is encoded (with Transfer-Encoding, not content-encoding -
  258. * IE "chunked" body), gzip compressed, etc. it will not be decoded.
  259. *
  260. * @return string
  261. */
  262. public function getRawBody()
  263. {
  264. return $this->body;
  265. }
  266. /**
  267. * Get the HTTP version of the response
  268. *
  269. * @return string
  270. */
  271. public function getVersion()
  272. {
  273. return $this->version;
  274. }
  275. /**
  276. * Get the HTTP response status code
  277. *
  278. * @return int
  279. */
  280. public function getStatus()
  281. {
  282. return $this->code;
  283. }
  284. /**
  285. * Return a message describing the HTTP response code
  286. * (Eg. "OK", "Not Found", "Moved Permanently")
  287. *
  288. * @return string
  289. */
  290. public function getMessage()
  291. {
  292. return $this->message;
  293. }
  294. /**
  295. * Get the response headers
  296. *
  297. * @return array
  298. */
  299. public function getHeaders()
  300. {
  301. return $this->headers;
  302. }
  303. /**
  304. * Get a specific header as string, or null if it is not set
  305. *
  306. * @param string$header
  307. * @return string|array|null
  308. */
  309. public function getHeader($header)
  310. {
  311. $header = ucwords(strtolower($header));
  312. if (! is_string($header) || ! isset($this->headers[$header])) return null;
  313. return $this->headers[$header];
  314. }
  315. /**
  316. * Get all headers as string
  317. *
  318. * @param boolean $status_line Whether to return the first status line (IE "HTTP 200 OK")
  319. * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
  320. * @return string
  321. */
  322. public function getHeadersAsString($status_line = true, $br = "\n")
  323. {
  324. $str = '';
  325. if ($status_line) {
  326. $str = "HTTP/{$this->version} {$this->code} {$this->message}{$br}";
  327. }
  328. // Iterate over the headers and stringify them
  329. foreach ($this->headers as $name => $value)
  330. {
  331. if (is_string($value))
  332. $str .= "{$name}: {$value}{$br}";
  333. elseif (is_array($value)) {
  334. foreach ($value as $subval) {
  335. $str .= "{$name}: {$subval}{$br}";
  336. }
  337. }
  338. }
  339. return $str;
  340. }
  341. /**
  342. * Get the entire response as string
  343. *
  344. * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
  345. * @return string
  346. */
  347. public function asString($br = "\n")
  348. {
  349. return $this->getHeadersAsString(true, $br) . $br . $this->getRawBody();
  350. }
  351. /**
  352. * A convenience function that returns a text representation of
  353. * HTTP response codes. Returns 'Unknown' for unknown codes.
  354. * Returns array of all codes, if $code is not specified.
  355. *
  356. * Conforms to HTTP/1.1 as defined in RFC 2616 (except for 'Unknown')
  357. * See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 for reference
  358. *
  359. * @param int $code HTTP response code
  360. * @param boolean $http11 Use HTTP version 1.1
  361. * @return string
  362. */
  363. public static function responseCodeAsText($code = null, $http11 = true)
  364. {
  365. $messages = self::$messages;
  366. if (! $http11) $messages[302] = 'Moved Temporarily';
  367. if ($code === null) {
  368. return $messages;
  369. } elseif (isset($messages[$code])) {
  370. return $messages[$code];
  371. } else {
  372. return 'Unknown';
  373. }
  374. }
  375. /**
  376. * Extract the response code from a response string
  377. *
  378. * @param string $response_str
  379. * @return int
  380. */
  381. public static function extractCode($response_str)
  382. {
  383. preg_match("|^HTTP/[\d\.x]+ (\d+)|", $response_str, $m);
  384. if (isset($m[1])) {
  385. return (int) $m[1];
  386. } else {
  387. return false;
  388. }
  389. }
  390. /**
  391. * Extract the HTTP message from a response
  392. *
  393. * @param string $response_str
  394. * @return string
  395. */
  396. public static function extractMessage($response_str)
  397. {
  398. preg_match("|^HTTP/[\d\.x]+ \d+ ([^\r\n]+)|", $response_str, $m);
  399. if (isset($m[1])) {
  400. return $m[1];
  401. } else {
  402. return false;
  403. }
  404. }
  405. /**
  406. * Extract the HTTP version from a response
  407. *
  408. * @param string $response_str
  409. * @return string
  410. */
  411. public static function extractVersion($response_str)
  412. {
  413. preg_match("|^HTTP/([\d\.x]+) \d+|", $response_str, $m);
  414. if (isset($m[1])) {
  415. return $m[1];
  416. } else {
  417. return false;
  418. }
  419. }
  420. /**
  421. * Extract the headers from a response string
  422. *
  423. * @param string $response_str
  424. * @return array
  425. */
  426. public static function extractHeaders($response_str)
  427. {
  428. $headers = array();
  429. // First, split body and headers
  430. $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2);
  431. if (! $parts[0]) return $headers;
  432. // Split headers part to lines
  433. $lines = explode("\n", $parts[0]);
  434. unset($parts);
  435. $last_header = null;
  436. foreach($lines as $line) {
  437. $line = trim($line, "\r\n");
  438. if ($line == "") break;
  439. if (preg_match("|^([\w-]+):\s+(.+)|", $line, $m)) {
  440. unset($last_header);
  441. $h_name = strtolower($m[1]);
  442. $h_value = $m[2];
  443. if (isset($headers[$h_name])) {
  444. if (! is_array($headers[$h_name])) {
  445. $headers[$h_name] = array($headers[$h_name]);
  446. }
  447. $headers[$h_name][] = $h_value;
  448. } else {
  449. $headers[$h_name] = $h_value;
  450. }
  451. $last_header = $h_name;
  452. } elseif (preg_match("|^\s+(.+)$|", $line, $m) && $last_header !== null) {
  453. if (is_array($headers[$last_header])) {
  454. end($headers[$last_header]);
  455. $last_header_key = key($headers[$last_header]);
  456. $headers[$last_header][$last_header_key] .= $m[1];
  457. } else {
  458. $headers[$last_header] .= $m[1];
  459. }
  460. }
  461. }
  462. return $headers;
  463. }
  464. /**
  465. * Extract the body from a response string
  466. *
  467. * @param string $response_str
  468. * @return string
  469. */
  470. public static function extractBody($response_str)
  471. {
  472. $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2);
  473. if (isset($parts[1])) {
  474. return $parts[1];
  475. }
  476. return '';
  477. }
  478. /**
  479. * Decode a "chunked" transfer-encoded body and return the decoded text
  480. *
  481. * @param string $body
  482. * @return string
  483. */
  484. public static function decodeChunkedBody($body)
  485. {
  486. $decBody = '';
  487. while (trim($body)) {
  488. if (! preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm", $body, $m)) {
  489. require_once 'Zend/Http/Exception.php';
  490. throw new Zend_Http_Exception("Error parsing body - doesn't seem to be a chunked message");
  491. }
  492. $length = hexdec(trim($m[1]));
  493. $cut = strlen($m[0]);
  494. $decBody .= substr($body, $cut, $length);
  495. $body = substr($body, $cut + $length + 2);
  496. }
  497. return $decBody;
  498. }
  499. /**
  500. * Decode a gzip encoded message (when Content-encoding = gzip)
  501. *
  502. * Currently requires PHP with zlib support
  503. *
  504. * @param string $body
  505. * @return string
  506. */
  507. public static function decodeGzip($body)
  508. {
  509. if (! function_exists('gzinflate')) {
  510. require_once 'Zend/Http/Exception.php';
  511. throw new Zend_Http_Exception('Unable to decode gzipped response ' .
  512. 'body: perhaps the zlib extension is not loaded?');
  513. }
  514. return gzinflate(substr($body, 10));
  515. }
  516. /**
  517. * Decode a zlib deflated message (when Content-encoding = deflate)
  518. *
  519. * Currently requires PHP with zlib support
  520. *
  521. * @param string $body
  522. * @return string
  523. */
  524. public static function decodeDeflate($body)
  525. {
  526. if (! function_exists('gzuncompress')) {
  527. require_once 'Zend/Http/Exception.php';
  528. throw new Zend_Http_Exception('Unable to decode deflated response ' .
  529. 'body: perhaps the zlib extension is not loaded?');
  530. }
  531. return gzuncompress($body);
  532. }
  533. /**
  534. * Create a new Zend_Http_Response object from a string
  535. *
  536. * @param string $response_str
  537. * @return Zend_Http_Response
  538. */
  539. public static function fromString($response_str)
  540. {
  541. $code = self::extractCode($response_str);
  542. $headers = self::extractHeaders($response_str);
  543. $body = self::extractBody($response_str);
  544. $version = self::extractVersion($response_str);
  545. $message = self::extractMessage($response_str);
  546. return new Zend_Http_Response($code, $headers, $body, $version, $message);
  547. }
  548. }