SimplePie_HTTP_Parser

HTTP Response Parser.

Defined (1)

The class is defined in the following location(s).

/wp-includes/SimplePie/HTTP/Parser.php  
  1. class SimplePie_HTTP_Parser 
  2. /** 
  3. * HTTP Version 
  4. * @var float 
  5. */ 
  6. public $http_version = 0.0; 
  7.  
  8. /** 
  9. * Status code 
  10. * @var int 
  11. */ 
  12. public $status_code = 0; 
  13.  
  14. /** 
  15. * Reason phrase 
  16. * @var string 
  17. */ 
  18. public $reason = ''; 
  19.  
  20. /** 
  21. * Key/value pairs of the headers 
  22. * @var array 
  23. */ 
  24. public $headers = array(); 
  25.  
  26. /** 
  27. * Body of the response 
  28. * @var string 
  29. */ 
  30. public $body = ''; 
  31.  
  32. /** 
  33. * Current state of the state machine 
  34. * @var string 
  35. */ 
  36. protected $state = 'http_version'; 
  37.  
  38. /** 
  39. * Input data 
  40. * @var string 
  41. */ 
  42. protected $data = ''; 
  43.  
  44. /** 
  45. * Input data length (to avoid calling strlen() everytime this is needed) 
  46. * @var int 
  47. */ 
  48. protected $data_length = 0; 
  49.  
  50. /** 
  51. * Current position of the pointer 
  52. * @var int 
  53. */ 
  54. protected $position = 0; 
  55.  
  56. /** 
  57. * Name of the hedaer currently being parsed 
  58. * @var string 
  59. */ 
  60. protected $name = ''; 
  61.  
  62. /** 
  63. * Value of the hedaer currently being parsed 
  64. * @var string 
  65. */ 
  66. protected $value = ''; 
  67.  
  68. /** 
  69. * Create an instance of the class with the input data 
  70. * @param string $data Input data 
  71. */ 
  72. public function __construct($data) 
  73. $this->data = $data; 
  74. $this->data_length = strlen($this->data); 
  75.  
  76. /** 
  77. * Parse the input data 
  78. * @return bool true on success, false on failure 
  79. */ 
  80. public function parse() 
  81. while ($this->state && $this->state !== 'emit' && $this->has_data()) 
  82. $state = $this->state; 
  83. $this->$state(); 
  84. $this->data = ''; 
  85. if ($this->state === 'emit' || $this->state === 'body') 
  86. return true; 
  87. else 
  88. $this->http_version = ''; 
  89. $this->status_code = ''; 
  90. $this->reason = ''; 
  91. $this->headers = array(); 
  92. $this->body = ''; 
  93. return false; 
  94.  
  95. /** 
  96. * Check whether there is data beyond the pointer 
  97. * @return bool true if there is further data, false if not 
  98. */ 
  99. protected function has_data() 
  100. return (bool) ($this->position < $this->data_length); 
  101.  
  102. /** 
  103. * See if the next character is LWS 
  104. * @return bool true if the next character is LWS, false if not 
  105. */ 
  106. protected function is_linear_whitespace() 
  107. return (bool) ($this->data[$this->position] === "\x09" 
  108. || $this->data[$this->position] === "\x20" 
  109. || ($this->data[$this->position] === "\x0A" 
  110. && isset($this->data[$this->position + 1]) 
  111. && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); 
  112.  
  113. /** 
  114. * Parse the HTTP version 
  115. */ 
  116. protected function http_version() 
  117. if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') 
  118. $len = strspn($this->data, '0123456789.', 5); 
  119. $this->http_version = substr($this->data, 5, $len); 
  120. $this->position += 5 + $len; 
  121. if (substr_count($this->http_version, '.') <= 1) 
  122. $this->http_version = (float) $this->http_version; 
  123. $this->position += strspn($this->data, "\x09\x20", $this->position); 
  124. $this->state = 'status'; 
  125. else 
  126. $this->state = false; 
  127. else 
  128. $this->state = false; 
  129.  
  130. /** 
  131. * Parse the status code 
  132. */ 
  133. protected function status() 
  134. if ($len = strspn($this->data, '0123456789', $this->position)) 
  135. $this->status_code = (int) substr($this->data, $this->position, $len); 
  136. $this->position += $len; 
  137. $this->state = 'reason'; 
  138. else 
  139. $this->state = false; 
  140.  
  141. /** 
  142. * Parse the reason phrase 
  143. */ 
  144. protected function reason() 
  145. $len = strcspn($this->data, "\x0A", $this->position); 
  146. $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); 
  147. $this->position += $len + 1; 
  148. $this->state = 'new_line'; 
  149.  
  150. /** 
  151. * Deal with a new line, shifting data around as needed 
  152. */ 
  153. protected function new_line() 
  154. $this->value = trim($this->value, "\x0D\x20"); 
  155. if ($this->name !== '' && $this->value !== '') 
  156. $this->name = strtolower($this->name); 
  157. // We should only use the last Content-Type header. c.f. issue #1 
  158. if (isset($this->headers[$this->name]) && $this->name !== 'content-type') 
  159. $this->headers[$this->name] .= ', ' . $this->value; 
  160. else 
  161. $this->headers[$this->name] = $this->value; 
  162. $this->name = ''; 
  163. $this->value = ''; 
  164. if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") 
  165. $this->position += 2; 
  166. $this->state = 'body'; 
  167. elseif ($this->data[$this->position] === "\x0A") 
  168. $this->position++; 
  169. $this->state = 'body'; 
  170. else 
  171. $this->state = 'name'; 
  172.  
  173. /** 
  174. * Parse a header name 
  175. */ 
  176. protected function name() 
  177. $len = strcspn($this->data, "\x0A:", $this->position); 
  178. if (isset($this->data[$this->position + $len])) 
  179. if ($this->data[$this->position + $len] === "\x0A") 
  180. $this->position += $len; 
  181. $this->state = 'new_line'; 
  182. else 
  183. $this->name = substr($this->data, $this->position, $len); 
  184. $this->position += $len + 1; 
  185. $this->state = 'value'; 
  186. else 
  187. $this->state = false; 
  188.  
  189. /** 
  190. * Parse LWS, replacing consecutive LWS characters with a single space 
  191. */ 
  192. protected function linear_whitespace() 
  193. do 
  194. if (substr($this->data, $this->position, 2) === "\x0D\x0A") 
  195. $this->position += 2; 
  196. elseif ($this->data[$this->position] === "\x0A") 
  197. $this->position++; 
  198. $this->position += strspn($this->data, "\x09\x20", $this->position); 
  199. } while ($this->has_data() && $this->is_linear_whitespace()); 
  200. $this->value .= "\x20"; 
  201.  
  202. /** 
  203. * See what state to move to while within non-quoted header values 
  204. */ 
  205. protected function value() 
  206. if ($this->is_linear_whitespace()) 
  207. $this->linear_whitespace(); 
  208. else 
  209. switch ($this->data[$this->position]) 
  210. case '"': 
  211. // Workaround for ETags: we have to include the quotes as 
  212. // part of the tag. 
  213. if (strtolower($this->name) === 'etag') 
  214. $this->value .= '"'; 
  215. $this->position++; 
  216. $this->state = 'value_char'; 
  217. break; 
  218. $this->position++; 
  219. $this->state = 'quote'; 
  220. break; 
  221.  
  222. case "\x0A": 
  223. $this->position++; 
  224. $this->state = 'new_line'; 
  225. break; 
  226.  
  227. default: 
  228. $this->state = 'value_char'; 
  229. break; 
  230.  
  231. /** 
  232. * Parse a header value while outside quotes 
  233. */ 
  234. protected function value_char() 
  235. $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); 
  236. $this->value .= substr($this->data, $this->position, $len); 
  237. $this->position += $len; 
  238. $this->state = 'value'; 
  239.  
  240. /** 
  241. * See what state to move to while within quoted header values 
  242. */ 
  243. protected function quote() 
  244. if ($this->is_linear_whitespace()) 
  245. $this->linear_whitespace(); 
  246. else 
  247. switch ($this->data[$this->position]) 
  248. case '"': 
  249. $this->position++; 
  250. $this->state = 'value'; 
  251. break; 
  252.  
  253. case "\x0A": 
  254. $this->position++; 
  255. $this->state = 'new_line'; 
  256. break; 
  257.  
  258. case '\\': 
  259. $this->position++; 
  260. $this->state = 'quote_escaped'; 
  261. break; 
  262.  
  263. default: 
  264. $this->state = 'quote_char'; 
  265. break; 
  266.  
  267. /** 
  268. * Parse a header value while within quotes 
  269. */ 
  270. protected function quote_char() 
  271. $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); 
  272. $this->value .= substr($this->data, $this->position, $len); 
  273. $this->position += $len; 
  274. $this->state = 'value'; 
  275.  
  276. /** 
  277. * Parse an escaped character within quotes 
  278. */ 
  279. protected function quote_escaped() 
  280. $this->value .= $this->data[$this->position]; 
  281. $this->position++; 
  282. $this->state = 'quote'; 
  283.  
  284. /** 
  285. * Parse the body 
  286. */ 
  287. protected function body() 
  288. $this->body = substr($this->data, $this->position); 
  289. if (!empty($this->headers['transfer-encoding'])) 
  290. unset($this->headers['transfer-encoding']); 
  291. $this->state = 'chunked'; 
  292. else 
  293. $this->state = 'emit'; 
  294.  
  295. /** 
  296. * Parsed a "Transfer-Encoding: chunked" body 
  297. */ 
  298. protected function chunked() 
  299. if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body))) 
  300. $this->state = 'emit'; 
  301. return; 
  302.  
  303. $decoded = ''; 
  304. $encoded = $this->body; 
  305.  
  306. while (true) 
  307. $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches ); 
  308. if (!$is_chunked) 
  309. // Looks like it's not chunked after all 
  310. $this->state = 'emit'; 
  311. return; 
  312.  
  313. $length = hexdec(trim($matches[1])); 
  314. if ($length === 0) 
  315. // Ignore trailer headers 
  316. $this->state = 'emit'; 
  317. $this->body = $decoded; 
  318. return; 
  319.  
  320. $chunk_length = strlen($matches[0]); 
  321. $decoded .= $part = substr($encoded, $chunk_length, $length); 
  322. $encoded = substr($encoded, $chunk_length + $length + 2); 
  323.  
  324. if (trim($encoded) === '0' || empty($encoded)) 
  325. $this->state = 'emit'; 
  326. $this->body = $decoded; 
  327. return;