[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/simplepie/library/SimplePie/HTTP/ -> Parser.php (source)

   1  <?php
   2  /**
   3   * SimplePie
   4   *
   5   * A PHP-Based RSS and Atom Feed Framework.
   6   * Takes the hard work out of managing a complete RSS/Atom solution.
   7   *
   8   * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
   9   * All rights reserved.
  10   *
  11   * Redistribution and use in source and binary forms, with or without modification, are
  12   * permitted provided that the following conditions are met:
  13   *
  14   *     * Redistributions of source code must retain the above copyright notice, this list of
  15   *       conditions and the following disclaimer.
  16   *
  17   *     * Redistributions in binary form must reproduce the above copyright notice, this list
  18   *       of conditions and the following disclaimer in the documentation and/or other materials
  19   *       provided with the distribution.
  20   *
  21   *     * Neither the name of the SimplePie Team nor the names of its contributors may be used
  22   *       to endorse or promote products derived from this software without specific prior
  23   *       written permission.
  24   *
  25   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
  26   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  27   * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
  28   * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  29   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  30   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  31   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  32   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33   * POSSIBILITY OF SUCH DAMAGE.
  34   *
  35   * @package SimplePie
  36   * @version 1.3.1
  37   * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
  38   * @author Ryan Parman
  39   * @author Geoffrey Sneddon
  40   * @author Ryan McCue
  41   * @link http://simplepie.org/ SimplePie
  42   * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  43   */
  44  
  45  
  46  /**
  47   * HTTP Response Parser
  48   *
  49   * @package SimplePie
  50   * @subpackage HTTP
  51   */
  52  class SimplePie_HTTP_Parser
  53  {
  54      /**
  55       * HTTP Version
  56       *
  57       * @var float
  58       */
  59      public $http_version = 0.0;
  60  
  61      /**
  62       * Status code
  63       *
  64       * @var int
  65       */
  66      public $status_code = 0;
  67  
  68      /**
  69       * Reason phrase
  70       *
  71       * @var string
  72       */
  73      public $reason = '';
  74  
  75      /**
  76       * Key/value pairs of the headers
  77       *
  78       * @var array
  79       */
  80      public $headers = array();
  81  
  82      /**
  83       * Body of the response
  84       *
  85       * @var string
  86       */
  87      public $body = '';
  88  
  89      /**
  90       * Current state of the state machine
  91       *
  92       * @var string
  93       */
  94      protected $state = 'http_version';
  95  
  96      /**
  97       * Input data
  98       *
  99       * @var string
 100       */
 101      protected $data = '';
 102  
 103      /**
 104       * Input data length (to avoid calling strlen() everytime this is needed)
 105       *
 106       * @var int
 107       */
 108      protected $data_length = 0;
 109  
 110      /**
 111       * Current position of the pointer
 112       *
 113       * @var int
 114       */
 115      protected $position = 0;
 116  
 117      /**
 118       * Name of the hedaer currently being parsed
 119       *
 120       * @var string
 121       */
 122      protected $name = '';
 123  
 124      /**
 125       * Value of the hedaer currently being parsed
 126       *
 127       * @var string
 128       */
 129      protected $value = '';
 130  
 131      /**
 132       * Create an instance of the class with the input data
 133       *
 134       * @param string $data Input data
 135       */
 136  	public function __construct($data)
 137      {
 138          $this->data = $data;
 139          $this->data_length = strlen($this->data);
 140      }
 141  
 142      /**
 143       * Parse the input data
 144       *
 145       * @return bool true on success, false on failure
 146       */
 147  	public function parse()
 148      {
 149          while ($this->state && $this->state !== 'emit' && $this->has_data())
 150          {
 151              $state = $this->state;
 152              $this->$state();
 153          }
 154          $this->data = '';
 155          if ($this->state === 'emit' || $this->state === 'body')
 156          {
 157              return true;
 158          }
 159          else
 160          {
 161              $this->http_version = '';
 162              $this->status_code = '';
 163              $this->reason = '';
 164              $this->headers = array();
 165              $this->body = '';
 166              return false;
 167          }
 168      }
 169  
 170      /**
 171       * Check whether there is data beyond the pointer
 172       *
 173       * @return bool true if there is further data, false if not
 174       */
 175  	protected function has_data()
 176      {
 177          return (bool) ($this->position < $this->data_length);
 178      }
 179  
 180      /**
 181       * See if the next character is LWS
 182       *
 183       * @return bool true if the next character is LWS, false if not
 184       */
 185  	protected function is_linear_whitespace()
 186      {
 187          return (bool) ($this->data[$this->position] === "\x09"
 188              || $this->data[$this->position] === "\x20"
 189              || ($this->data[$this->position] === "\x0A"
 190                  && isset($this->data[$this->position + 1])
 191                  && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
 192      }
 193  
 194      /**
 195       * Parse the HTTP version
 196       */
 197  	protected function http_version()
 198      {
 199          if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
 200          {
 201              $len = strspn($this->data, '0123456789.', 5);
 202              $this->http_version = substr($this->data, 5, $len);
 203              $this->position += 5 + $len;
 204              if (substr_count($this->http_version, '.') <= 1)
 205              {
 206                  $this->http_version = (float) $this->http_version;
 207                  $this->position += strspn($this->data, "\x09\x20", $this->position);
 208                  $this->state = 'status';
 209              }
 210              else
 211              {
 212                  $this->state = false;
 213              }
 214          }
 215          else
 216          {
 217              $this->state = false;
 218          }
 219      }
 220  
 221      /**
 222       * Parse the status code
 223       */
 224  	protected function status()
 225      {
 226          if ($len = strspn($this->data, '0123456789', $this->position))
 227          {
 228              $this->status_code = (int) substr($this->data, $this->position, $len);
 229              $this->position += $len;
 230              $this->state = 'reason';
 231          }
 232          else
 233          {
 234              $this->state = false;
 235          }
 236      }
 237  
 238      /**
 239       * Parse the reason phrase
 240       */
 241  	protected function reason()
 242      {
 243          $len = strcspn($this->data, "\x0A", $this->position);
 244          $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
 245          $this->position += $len + 1;
 246          $this->state = 'new_line';
 247      }
 248  
 249      /**
 250       * Deal with a new line, shifting data around as needed
 251       */
 252  	protected function new_line()
 253      {
 254          $this->value = trim($this->value, "\x0D\x20");
 255          if ($this->name !== '' && $this->value !== '')
 256          {
 257              $this->name = strtolower($this->name);
 258              // We should only use the last Content-Type header. c.f. issue #1
 259              if (isset($this->headers[$this->name]) && $this->name !== 'content-type')
 260              {
 261                  $this->headers[$this->name] .= ', ' . $this->value;
 262              }
 263              else
 264              {
 265                  $this->headers[$this->name] = $this->value;
 266              }
 267          }
 268          $this->name = '';
 269          $this->value = '';
 270          if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
 271          {
 272              $this->position += 2;
 273              $this->state = 'body';
 274          }
 275          elseif ($this->data[$this->position] === "\x0A")
 276          {
 277              $this->position++;
 278              $this->state = 'body';
 279          }
 280          else
 281          {
 282              $this->state = 'name';
 283          }
 284      }
 285  
 286      /**
 287       * Parse a header name
 288       */
 289  	protected function name()
 290      {
 291          $len = strcspn($this->data, "\x0A:", $this->position);
 292          if (isset($this->data[$this->position + $len]))
 293          {
 294              if ($this->data[$this->position + $len] === "\x0A")
 295              {
 296                  $this->position += $len;
 297                  $this->state = 'new_line';
 298              }
 299              else
 300              {
 301                  $this->name = substr($this->data, $this->position, $len);
 302                  $this->position += $len + 1;
 303                  $this->state = 'value';
 304              }
 305          }
 306          else
 307          {
 308              $this->state = false;
 309          }
 310      }
 311  
 312      /**
 313       * Parse LWS, replacing consecutive LWS characters with a single space
 314       */
 315  	protected function linear_whitespace()
 316      {
 317          do
 318          {
 319              if (substr($this->data, $this->position, 2) === "\x0D\x0A")
 320              {
 321                  $this->position += 2;
 322              }
 323              elseif ($this->data[$this->position] === "\x0A")
 324              {
 325                  $this->position++;
 326              }
 327              $this->position += strspn($this->data, "\x09\x20", $this->position);
 328          } while ($this->has_data() && $this->is_linear_whitespace());
 329          $this->value .= "\x20";
 330      }
 331  
 332      /**
 333       * See what state to move to while within non-quoted header values
 334       */
 335  	protected function value()
 336      {
 337          if ($this->is_linear_whitespace())
 338          {
 339              $this->linear_whitespace();
 340          }
 341          else
 342          {
 343              switch ($this->data[$this->position])
 344              {
 345                  case '"':
 346                      // Workaround for ETags: we have to include the quotes as
 347                      // part of the tag.
 348                      if (strtolower($this->name) === 'etag')
 349                      {
 350                          $this->value .= '"';
 351                          $this->position++;
 352                          $this->state = 'value_char';
 353                          break;
 354                      }
 355                      $this->position++;
 356                      $this->state = 'quote';
 357                      break;
 358  
 359                  case "\x0A":
 360                      $this->position++;
 361                      $this->state = 'new_line';
 362                      break;
 363  
 364                  default:
 365                      $this->state = 'value_char';
 366                      break;
 367              }
 368          }
 369      }
 370  
 371      /**
 372       * Parse a header value while outside quotes
 373       */
 374  	protected function value_char()
 375      {
 376          $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
 377          $this->value .= substr($this->data, $this->position, $len);
 378          $this->position += $len;
 379          $this->state = 'value';
 380      }
 381  
 382      /**
 383       * See what state to move to while within quoted header values
 384       */
 385  	protected function quote()
 386      {
 387          if ($this->is_linear_whitespace())
 388          {
 389              $this->linear_whitespace();
 390          }
 391          else
 392          {
 393              switch ($this->data[$this->position])
 394              {
 395                  case '"':
 396                      $this->position++;
 397                      $this->state = 'value';
 398                      break;
 399  
 400                  case "\x0A":
 401                      $this->position++;
 402                      $this->state = 'new_line';
 403                      break;
 404  
 405                  case '\\':
 406                      $this->position++;
 407                      $this->state = 'quote_escaped';
 408                      break;
 409  
 410                  default:
 411                      $this->state = 'quote_char';
 412                      break;
 413              }
 414          }
 415      }
 416  
 417      /**
 418       * Parse a header value while within quotes
 419       */
 420  	protected function quote_char()
 421      {
 422          $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
 423          $this->value .= substr($this->data, $this->position, $len);
 424          $this->position += $len;
 425          $this->state = 'value';
 426      }
 427  
 428      /**
 429       * Parse an escaped character within quotes
 430       */
 431  	protected function quote_escaped()
 432      {
 433          $this->value .= $this->data[$this->position];
 434          $this->position++;
 435          $this->state = 'quote';
 436      }
 437  
 438      /**
 439       * Parse the body
 440       */
 441  	protected function body()
 442      {
 443          $this->body = substr($this->data, $this->position);
 444          if (!empty($this->headers['transfer-encoding']))
 445          {
 446              unset($this->headers['transfer-encoding']);
 447              $this->state = 'chunked';
 448          }
 449          else
 450          {
 451              $this->state = 'emit';
 452          }
 453      }
 454  
 455      /**
 456       * Parsed a "Transfer-Encoding: chunked" body
 457       */
 458  	protected function chunked()
 459      {
 460          if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body)))
 461          {
 462              $this->state = 'emit';
 463              return;
 464          }
 465  
 466          $decoded = '';
 467          $encoded = $this->body;
 468  
 469          while (true)
 470          {
 471              $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
 472              if (!$is_chunked)
 473              {
 474                  // Looks like it's not chunked after all
 475                  $this->state = 'emit';
 476                  return;
 477              }
 478  
 479              $length = hexdec(trim($matches[1]));
 480              if ($length === 0)
 481              {
 482                  // Ignore trailer headers
 483                  $this->state = 'emit';
 484                  $this->body = $decoded;
 485                  return;
 486              }
 487  
 488              $chunk_length = strlen($matches[0]);
 489              $decoded .= $part = substr($encoded, $chunk_length, $length);
 490              $encoded = substr($encoded, $chunk_length + $length + 2);
 491  
 492              if (trim($encoded) === '0' || empty($encoded))
 493              {
 494                  $this->state = 'emit';
 495                  $this->body = $decoded;
 496                  return;
 497              }
 498          }
 499      }
 500  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1