/library/Diggin/Http/Client/Adapter/SocketProgressBar.php

https://github.com/GunioRobot/diggin · PHP · 232 lines · 150 code · 44 blank · 38 comment · 60 complexity · d27175bf67af2e9b701b92308e42bc9b MD5 · raw file

  1. <?php
  2. // require_once 'Zend/Http/Client/Adapter/Socket.php';
  3. class SocketProgressBar
  4. extends \Zend\Http\Client\Adapter\Socket
  5. {
  6. private $_max = 0;
  7. private $_progressBar;
  8. public function setMax($max)
  9. {
  10. $this->_max = $max;
  11. }
  12. public function getMax()
  13. {
  14. return $this->_max;
  15. }
  16. private function getProgressBar()
  17. {
  18. if (PHP_SAPI != 'cli') throw new \RuntimeException();
  19. if (!$this->_progressBar) {
  20. // require_once 'Zend/ProgressBar/Adapter/Console.php';
  21. // require_once 'Zend/ProgressBar.php';
  22. $adapter = new \Zend\ProgressBar\Adapter\Console();
  23. $this->_progressBar = new \Zend\ProgressBar($adapter, 0, $this->getMax());
  24. }
  25. return $this->_progressBar;
  26. }
  27. /**
  28. * Read response from server
  29. *
  30. * @return string
  31. */
  32. public function read()
  33. {
  34. // First, read headers only
  35. $response = '';
  36. $gotStatus = false;
  37. $stream = !empty($this->config['stream']);
  38. while (($line = @fgets($this->socket)) !== false) {
  39. $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
  40. if ($gotStatus) {
  41. $response .= $line;
  42. if (rtrim($line) === '') break;
  43. }
  44. }
  45. $this->_checkSocketReadTimeout();
  46. $statusCode = \Zend\Http\Response::extractCode($response);
  47. // Handle 100 and 101 responses internally by restarting the read again
  48. if ($statusCode == 100 || $statusCode == 101) return $this->read();
  49. // Check headers to see what kind of connection / transfer encoding we have
  50. $headers = \Zend\Http\Response::extractHeaders($response);
  51. /**
  52. * Responses to HEAD requests and 204 or 304 responses are not expected
  53. * to have a body - stop reading here
  54. */
  55. if ($statusCode == 304 || $statusCode == 204 ||
  56. $this->method == \Zend\Http\Client::HEAD) {
  57. // Close the connection if requested to do so by the server
  58. if (isset($headers['connection']) && $headers['connection'] == 'close') {
  59. $this->close();
  60. }
  61. return $response;
  62. }
  63. // If we got a 'transfer-encoding: chunked' header
  64. if (isset($headers['transfer-encoding'])) {
  65. if (strtolower($headers['transfer-encoding']) == 'chunked') {
  66. do {
  67. $line = @fgets($this->socket);
  68. $this->_checkSocketReadTimeout();
  69. $chunk = $line;
  70. // Figure out the next chunk size
  71. $chunksize = trim($line);
  72. if (! ctype_xdigit($chunksize)) {
  73. $this->close();
  74. // require_once 'Zend/Http/Client/Adapter/Exception.php';
  75. throw new \Zend\Http\Client\Adapter\Exception('Invalid chunk size "' .
  76. $chunksize . '" unable to read chunked body');
  77. }
  78. // Convert the hexadecimal value to plain integer
  79. $chunksize = hexdec($chunksize);
  80. // Read next chunk
  81. $read_to = ftell($this->socket) + $chunksize;
  82. do {
  83. $current_pos = ftell($this->socket);
  84. if ($current_pos >= $read_to) break;
  85. if($this->out_stream) {
  86. if(stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) {
  87. $this->_checkSocketReadTimeout();
  88. break;
  89. }
  90. } else {
  91. $line = @fread($this->socket, $read_to - $current_pos);
  92. if ($line === false || strlen($line) === 0) {
  93. $this->_checkSocketReadTimeout();
  94. break;
  95. }
  96. $chunk .= $line;
  97. }
  98. } while (! feof($this->socket));
  99. $chunk .= @fgets($this->socket);
  100. $this->_checkSocketReadTimeout();
  101. if(!$this->out_stream) {
  102. $response .= $chunk;
  103. }
  104. } while ($chunksize > 0);
  105. } else {
  106. $this->close();
  107. throw new \Zend\Http\Client\Adapter\Exception('Cannot handle "' .
  108. $headers['transfer-encoding'] . '" transfer encoding');
  109. }
  110. // We automatically decode chunked-messages when writing to a stream
  111. // this means we have to disallow the Zend_Http_Response to do it again
  112. if ($this->out_stream) {
  113. $response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $response);
  114. }
  115. // Else, if we got the content-length header, read this number of bytes
  116. } elseif (isset($headers['content-length'])) {
  117. // If we got more than one Content-Length header (see ZF-9404) use
  118. // the last value sent
  119. if (is_array($headers['content-length'])) {
  120. $contentLength = $headers['content-length'][count($headers['content-length']) - 1];
  121. } else {
  122. $contentLength = $headers['content-length'];
  123. }
  124. if ($contentLength != 0) $this->setMax($contentLength);
  125. $current_pos = ftell($this->socket);
  126. $chunk = '';
  127. $startProgressBar = false;
  128. for ($read_to = $current_pos + $contentLength;
  129. $read_to > $current_pos;
  130. $current_pos = ftell($this->socket)) {
  131. if ($startProgressBar === false) {
  132. $this->setMax($read_to);
  133. $startProgressBar = true;
  134. }
  135. $this->getProgressBar()->update($current_pos);
  136. //if($this->out_stream) {
  137. /*
  138. if(@stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) {
  139. $this->_checkSocketReadTimeout();
  140. break;
  141. }
  142. */
  143. //} else {
  144. $chunk = @fread($this->socket, $read_to - $current_pos);
  145. if ($chunk === false || strlen($chunk) === 0) {
  146. $this->_checkSocketReadTimeout();
  147. break;
  148. }
  149. if ($this->out_stream) {
  150. $fwrite = fwrite($this->out_stream, $chunk);
  151. if ($fwrite === false) {
  152. throw new \Exception();
  153. }
  154. } else {
  155. $response .= $chunk;
  156. }
  157. //}
  158. // Break if the connection ended prematurely
  159. if (feof($this->socket)) break;
  160. }
  161. if ($startProgressBar) $this->getProgressBar()->finish();
  162. // Fallback: just read the response until EOF
  163. } else {
  164. do {
  165. if($this->out_stream) {
  166. if(@stream_copy_to_stream($this->socket, $this->out_stream) == 0) {
  167. $this->_checkSocketReadTimeout();
  168. break;
  169. }
  170. } else {
  171. $buff = @fread($this->socket, 8192);
  172. if ($buff === false || strlen($buff) === 0) {
  173. $this->_checkSocketReadTimeout();
  174. break;
  175. } else {
  176. $response .= $buff;
  177. }
  178. }
  179. } while (feof($this->socket) === false);
  180. $this->close();
  181. }
  182. // Close the connection if requested to do so by the server
  183. if (isset($headers['connection']) && $headers['connection'] == 'close') {
  184. $this->close();
  185. }
  186. return $response;
  187. }
  188. }