source: trunk/www.guidonia.net/wp/wp-content/plugins/webtv/Drivers/Zend/Http/Response.php@ 44

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