[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/zend/Zend/Http/ -> Response.php (source)

   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 [email protected] so we can send you a copy immediately.
  15   *
  16   * @category   Zend
  17   * @package    Zend_Http
  18   * @subpackage Response
  19   * @version    $Id$
  20   * @copyright  Copyright (c) 2005-2010 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-2010 Zend Technologies USA Inc. (http://www.zend.com)
  32   * @license    http://framework.zend.com/license/new-bsd     New BSD License
  33   */
  34  class 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 (strtolower($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       * Implements magic __toString()
 402       *
 403       * @return string
 404       */
 405      public function __toString()
 406      {
 407          return $this->asString();
 408      }
 409  
 410      /**
 411       * A convenience function that returns a text representation of
 412       * HTTP response codes. Returns 'Unknown' for unknown codes.
 413       * Returns array of all codes, if $code is not specified.
 414       *
 415       * Conforms to HTTP/1.1 as defined in RFC 2616 (except for 'Unknown')
 416       * See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 for reference
 417       *
 418       * @param int $code HTTP response code
 419       * @param boolean $http11 Use HTTP version 1.1
 420       * @return string
 421       */
 422      public static function responseCodeAsText($code = null, $http11 = true)
 423      {
 424          $messages = self::$messages;
 425          if (! $http11) $messages[302] = 'Moved Temporarily';
 426  
 427          if ($code === null) {
 428              return $messages;
 429          } elseif (isset($messages[$code])) {
 430              return $messages[$code];
 431          } else {
 432              return 'Unknown';
 433          }
 434      }
 435  
 436      /**
 437       * Extract the response code from a response string
 438       *
 439       * @param string $response_str
 440       * @return int
 441       */
 442      public static function extractCode($response_str)
 443      {
 444          preg_match("|^HTTP/[\d\.x]+ (\d+)|", $response_str, $m);
 445  
 446          if (isset($m[1])) {
 447              return (int) $m[1];
 448          } else {
 449              return false;
 450          }
 451      }
 452  
 453      /**
 454       * Extract the HTTP message from a response
 455       *
 456       * @param string $response_str
 457       * @return string
 458       */
 459      public static function extractMessage($response_str)
 460      {
 461          preg_match("|^HTTP/[\d\.x]+ \d+ ([^\r\n]+)|", $response_str, $m);
 462  
 463          if (isset($m[1])) {
 464              return $m[1];
 465          } else {
 466              return false;
 467          }
 468      }
 469  
 470      /**
 471       * Extract the HTTP version from a response
 472       *
 473       * @param string $response_str
 474       * @return string
 475       */
 476      public static function extractVersion($response_str)
 477      {
 478          preg_match("|^HTTP/([\d\.x]+) \d+|", $response_str, $m);
 479  
 480          if (isset($m[1])) {
 481              return $m[1];
 482          } else {
 483              return false;
 484          }
 485      }
 486  
 487      /**
 488       * Extract the headers from a response string
 489       *
 490       * @param string $response_str
 491       * @return array
 492       */
 493      public static function extractHeaders($response_str)
 494      {
 495          $headers = array();
 496  
 497          // First, split body and headers
 498          $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2);
 499          if (! $parts[0]) return $headers;
 500  
 501          // Split headers part to lines
 502          $lines = explode("\n", $parts[0]);
 503          unset($parts);
 504          $last_header = null;
 505  
 506          foreach($lines as $line) {
 507              $line = trim($line, "\r\n");
 508              if ($line == "") break;
 509  
 510              if (preg_match("|^([\w-]+):\s+(.+)|", $line, $m)) {
 511                  unset($last_header);
 512                  $h_name = strtolower($m[1]);
 513                  $h_value = $m[2];
 514  
 515                  if (isset($headers[$h_name])) {
 516                      if (! is_array($headers[$h_name])) {
 517                          $headers[$h_name] = array($headers[$h_name]);
 518                      }
 519  
 520                      $headers[$h_name][] = $h_value;
 521                  } else {
 522                      $headers[$h_name] = $h_value;
 523                  }
 524                  $last_header = $h_name;
 525              } elseif (preg_match("|^\s+(.+)$|", $line, $m) && $last_header !== null) {
 526                  if (is_array($headers[$last_header])) {
 527                      end($headers[$last_header]);
 528                      $last_header_key = key($headers[$last_header]);
 529                      $headers[$last_header][$last_header_key] .= $m[1];
 530                  } else {
 531                      $headers[$last_header] .= $m[1];
 532                  }
 533              }
 534          }
 535  
 536          return $headers;
 537      }
 538  
 539      /**
 540       * Extract the body from a response string
 541       *
 542       * @param string $response_str
 543       * @return string
 544       */
 545      public static function extractBody($response_str)
 546      {
 547          $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2);
 548          if (isset($parts[1])) {
 549              return $parts[1];
 550          }
 551          return '';
 552      }
 553  
 554      /**
 555       * Decode a "chunked" transfer-encoded body and return the decoded text
 556       *
 557       * @param string $body
 558       * @return string
 559       */
 560      public static function decodeChunkedBody($body)
 561      {
 562          $decBody = '';
 563  
 564          // If mbstring overloads substr and strlen functions, we have to
 565          // override it's internal encoding
 566          if (function_exists('mb_internal_encoding') &&
 567             ((int) ini_get('mbstring.func_overload')) & 2) {
 568  
 569              $mbIntEnc = mb_internal_encoding();
 570              mb_internal_encoding('ASCII');
 571          }
 572  
 573          while (trim($body)) {
 574              if (! preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm", $body, $m)) {
 575                  require_once 'Zend/Http/Exception.php';
 576                  throw new Zend_Http_Exception("Error parsing body - doesn't seem to be a chunked message");
 577              }
 578  
 579              $length = hexdec(trim($m[1]));
 580              $cut = strlen($m[0]);
 581              $decBody .= substr($body, $cut, $length);
 582              $body = substr($body, $cut + $length + 2);
 583          }
 584  
 585          if (isset($mbIntEnc)) {
 586              mb_internal_encoding($mbIntEnc);
 587          }
 588  
 589          return $decBody;
 590      }
 591  
 592      /**
 593       * Decode a gzip encoded message (when Content-encoding = gzip)
 594       *
 595       * Currently requires PHP with zlib support
 596       *
 597       * @param string $body
 598       * @return string
 599       */
 600      public static function decodeGzip($body)
 601      {
 602          if (! function_exists('gzinflate')) {
 603              require_once 'Zend/Http/Exception.php';
 604              throw new Zend_Http_Exception(
 605                  'zlib extension is required in order to decode "gzip" encoding'
 606              );
 607          }
 608  
 609          return gzinflate(substr($body, 10));
 610      }
 611  
 612      /**
 613       * Decode a zlib deflated message (when Content-encoding = deflate)
 614       *
 615       * Currently requires PHP with zlib support
 616       *
 617       * @param string $body
 618       * @return string
 619       */
 620      public static function decodeDeflate($body)
 621      {
 622          if (! function_exists('gzuncompress')) {
 623              require_once 'Zend/Http/Exception.php';
 624              throw new Zend_Http_Exception(
 625                  'zlib extension is required in order to decode "deflate" encoding'
 626              );
 627          }
 628  
 629          /**
 630           * Some servers (IIS ?) send a broken deflate response, without the
 631           * RFC-required zlib header.
 632           *
 633           * We try to detect the zlib header, and if it does not exsit we
 634           * teat the body is plain DEFLATE content.
 635           *
 636           * This method was adapted from PEAR HTTP_Request2 by (c) Alexey Borzov
 637           *
 638           * @link http://framework.zend.com/issues/browse/ZF-6040
 639           */
 640          $zlibHeader = unpack('n', substr($body, 0, 2));
 641          if ($zlibHeader[1] % 31 == 0) {
 642              return gzuncompress($body);
 643          } else {
 644              return gzinflate($body);
 645          }
 646      }
 647  
 648      /**
 649       * Create a new Zend_Http_Response object from a string
 650       *
 651       * @param string $response_str
 652       * @return Zend_Http_Response
 653       */
 654      public static function fromString($response_str)
 655      {
 656          $code    = self::extractCode($response_str);
 657          $headers = self::extractHeaders($response_str);
 658          $body    = self::extractBody($response_str);
 659          $version = self::extractVersion($response_str);
 660          $message = self::extractMessage($response_str);
 661  
 662          return new Zend_Http_Response($code, $headers, $body, $version, $message);
 663      }
 664  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1