/vendor/nmred/kafka-php/src/Kafka/Socket.php

https://gitlab.com/link233/bootmw · PHP · 383 lines · 164 code · 44 blank · 175 comment · 27 complexity · 0854f4ab0221c3e8ff8ba14f7a7f492b MD5 · raw file

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  3. // +---------------------------------------------------------------------------
  4. // | SWAN [ $_SWANBR_SLOGAN_$ ]
  5. // +---------------------------------------------------------------------------
  6. // | Copyright $_SWANBR_COPYRIGHT_$
  7. // +---------------------------------------------------------------------------
  8. // | Version $_SWANBR_VERSION_$
  9. // +---------------------------------------------------------------------------
  10. // | Licensed ( $_SWANBR_LICENSED_URL_$ )
  11. // +---------------------------------------------------------------------------
  12. // | $_SWANBR_WEB_DOMAIN_$
  13. // +---------------------------------------------------------------------------
  14. namespace Kafka;
  15. /**
  16. +------------------------------------------------------------------------------
  17. * Kafka protocol since Kafka v0.8
  18. +------------------------------------------------------------------------------
  19. *
  20. * @package
  21. * @version $_SWANBR_VERSION_$
  22. * @copyright Copyleft
  23. * @author $_SWANBR_AUTHOR_$
  24. +------------------------------------------------------------------------------
  25. */
  26. class Socket
  27. {
  28. // {{{ consts
  29. const READ_MAX_LEN = 5242880; // read socket max length 5MB
  30. /**
  31. * max write socket buffer
  32. * fixed:send of 8192 bytes failed with errno=11 Resource temporarily
  33. * unavailable error info
  34. */
  35. const MAX_WRITE_BUFFER = 4096;
  36. // }}}
  37. // {{{ members
  38. /**
  39. * Send timeout in seconds.
  40. *
  41. * @var float
  42. * @access private
  43. */
  44. private $sendTimeoutSec = 0;
  45. /**
  46. * Send timeout in microseconds.
  47. *
  48. * @var float
  49. * @access private
  50. */
  51. private $sendTimeoutUsec = 100000;
  52. /**
  53. * Recv timeout in seconds
  54. *
  55. * @var float
  56. * @access private
  57. */
  58. private $recvTimeoutSec = 0;
  59. /**
  60. * Recv timeout in microseconds
  61. *
  62. * @var float
  63. * @access private
  64. */
  65. private $recvTimeoutUsec = 750000;
  66. /**
  67. * Stream resource
  68. *
  69. * @var mixed
  70. * @access private
  71. */
  72. private $stream = null;
  73. /**
  74. * Socket host
  75. *
  76. * @var mixed
  77. * @access private
  78. */
  79. private $host = null;
  80. /**
  81. * Socket port
  82. *
  83. * @var mixed
  84. * @access private
  85. */
  86. private $port = -1;
  87. // }}}
  88. // {{{ functions
  89. // {{{ public function __construct()
  90. /**
  91. * __construct
  92. *
  93. * @access public
  94. * @param $host
  95. * @param $port
  96. * @param int $recvTimeoutSec
  97. * @param int $recvTimeoutUsec
  98. * @param int $sendTimeoutSec
  99. * @param int $sendTimeoutUsec
  100. */
  101. public function __construct($host, $port, $recvTimeoutSec = 0, $recvTimeoutUsec = 750000, $sendTimeoutSec = 0, $sendTimeoutUsec = 100000)
  102. {
  103. $this->host = $host;
  104. $this->port = $port;
  105. $this->setRecvTimeoutSec($recvTimeoutSec);
  106. $this->setRecvTimeoutUsec($recvTimeoutUsec);
  107. $this->setSendTimeoutSec($sendTimeoutSec);
  108. $this->setSendTimeoutUsec($sendTimeoutUsec);
  109. }
  110. /**
  111. * @param float $sendTimeoutSec
  112. */
  113. public function setSendTimeoutSec($sendTimeoutSec)
  114. {
  115. $this->sendTimeoutSec = $sendTimeoutSec;
  116. }
  117. /**
  118. * @param float $sendTimeoutUsec
  119. */
  120. public function setSendTimeoutUsec($sendTimeoutUsec)
  121. {
  122. $this->sendTimeoutUsec = $sendTimeoutUsec;
  123. }
  124. /**
  125. * @param float $recvTimeoutSec
  126. */
  127. public function setRecvTimeoutSec($recvTimeoutSec)
  128. {
  129. $this->recvTimeoutSec = $recvTimeoutSec;
  130. }
  131. /**
  132. * @param float $recvTimeoutUsec
  133. */
  134. public function setRecvTimeoutUsec($recvTimeoutUsec)
  135. {
  136. $this->recvTimeoutUsec = $recvTimeoutUsec;
  137. }
  138. // }}}
  139. // {{{ public static function createFromStream()
  140. /**
  141. * Optional method to set the internal stream handle
  142. *
  143. * @static
  144. * @access public
  145. * @param $stream
  146. * @return Socket
  147. */
  148. public static function createFromStream($stream)
  149. {
  150. $socket = new self('localhost', 0);
  151. $socket->setStream($stream);
  152. return $socket;
  153. }
  154. // }}}
  155. // {{{ public function setStream()
  156. /**
  157. * Optional method to set the internal stream handle
  158. *
  159. * @param mixed $stream
  160. * @access public
  161. * @return void
  162. */
  163. public function setStream($stream)
  164. {
  165. $this->stream = $stream;
  166. }
  167. // }}}
  168. // {{{ public function connect()
  169. /**
  170. * Connects the socket
  171. *
  172. * @access public
  173. * @return void
  174. */
  175. public function connect()
  176. {
  177. if (is_resource($this->stream)) {
  178. return;
  179. }
  180. if (empty($this->host)) {
  181. throw new \Kafka\Exception('Cannot open null host.');
  182. }
  183. if ($this->port <= 0) {
  184. throw new \Kafka\Exception('Cannot open without port.');
  185. }
  186. $this->stream = @fsockopen(
  187. $this->host,
  188. $this->port,
  189. $errno,
  190. $errstr,
  191. $this->sendTimeoutSec + ($this->sendTimeoutUsec / 1000000)
  192. );
  193. if ($this->stream == false) {
  194. $error = 'Could not connect to '
  195. . $this->host . ':' . $this->port
  196. . ' ('.$errstr.' ['.$errno.'])';
  197. throw new \Kafka\Exception\SocketConnect($error);
  198. }
  199. stream_set_blocking($this->stream, 0);
  200. }
  201. // }}}
  202. // {{{ public function close()
  203. /**
  204. * close the socket
  205. *
  206. * @access public
  207. * @return void
  208. */
  209. public function close()
  210. {
  211. if (is_resource($this->stream)) {
  212. fclose($this->stream);
  213. }
  214. }
  215. // }}}
  216. // {{{ public function read()
  217. /**
  218. * Read from the socket at most $len bytes.
  219. *
  220. * This method will not wait for all the requested data, it will return as
  221. * soon as any data is received.
  222. *
  223. * @param integer $len Maximum number of bytes to read.
  224. * @param boolean $verifyExactLength Throw an exception if the number of read bytes is less than $len
  225. *
  226. * @return string Binary data
  227. * @throws \Kafka\Exception\SocketEOF
  228. */
  229. public function read($len, $verifyExactLength = false)
  230. {
  231. if ($len > self::READ_MAX_LEN) {
  232. throw new \Kafka\Exception\SocketEOF('Could not read '.$len.' bytes from stream, length too longer.');
  233. }
  234. $null = null;
  235. $read = array($this->stream);
  236. $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec, $this->recvTimeoutUsec);
  237. if ($readable > 0) {
  238. $remainingBytes = $len;
  239. $data = $chunk = '';
  240. while ($remainingBytes > 0) {
  241. $chunk = fread($this->stream, $remainingBytes);
  242. if ($chunk === false) {
  243. $this->close();
  244. throw new \Kafka\Exception\SocketEOF('Could not read '.$len.' bytes from stream (no data)');
  245. }
  246. if (strlen($chunk) === 0) {
  247. // Zero bytes because of EOF?
  248. if (feof($this->stream)) {
  249. $this->close();
  250. throw new \Kafka\Exception\SocketEOF('Unexpected EOF while reading '.$len.' bytes from stream (no data)');
  251. }
  252. // Otherwise wait for bytes
  253. $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec, $this->recvTimeoutUsec);
  254. if ($readable !== 1) {
  255. throw new \Kafka\Exception\SocketTimeout('Timed out reading socket while reading ' . $len . ' bytes with ' . $remainingBytes . ' bytes to go');
  256. }
  257. continue; // attempt another read
  258. }
  259. $data .= $chunk;
  260. $remainingBytes -= strlen($chunk);
  261. }
  262. if ($len === $remainingBytes || ($verifyExactLength && $len !== strlen($data))) {
  263. // couldn't read anything at all OR reached EOF sooner than expected
  264. $this->close();
  265. throw new \Kafka\Exception\SocketEOF('Read ' . strlen($data) . ' bytes instead of the requested ' . $len . ' bytes');
  266. }
  267. return $data;
  268. }
  269. if (false !== $readable) {
  270. $res = stream_get_meta_data($this->stream);
  271. if (!empty($res['timed_out'])) {
  272. $this->close();
  273. throw new \Kafka\Exception\SocketTimeout('Timed out reading '.$len.' bytes from stream');
  274. }
  275. }
  276. $this->close();
  277. throw new \Kafka\Exception\SocketEOF('Could not read '.$len.' bytes from stream (not readable)');
  278. }
  279. // }}}
  280. // {{{ public function write()
  281. /**
  282. * Write to the socket.
  283. *
  284. * @param string $buf The data to write
  285. *
  286. * @return integer
  287. * @throws \Kafka\Exception\SocketEOF
  288. */
  289. public function write($buf)
  290. {
  291. $null = null;
  292. $write = array($this->stream);
  293. // fwrite to a socket may be partial, so loop until we
  294. // are done with the entire buffer
  295. $written = 0;
  296. $buflen = strlen($buf);
  297. while ( $written < $buflen ) {
  298. // wait for stream to become available for writing
  299. $writable = stream_select($null, $write, $null, $this->sendTimeoutSec, $this->sendTimeoutUsec);
  300. if ($writable > 0) {
  301. if ($buflen - $written > self::MAX_WRITE_BUFFER) {
  302. // write max buffer size
  303. $wrote = fwrite($this->stream, substr($buf, $written, self::MAX_WRITE_BUFFER));
  304. } else {
  305. // write remaining buffer bytes to stream
  306. $wrote = fwrite($this->stream, substr($buf, $written));
  307. }
  308. if ($wrote === -1 || $wrote === false) {
  309. throw new \Kafka\Exception\Socket('Could not write ' . strlen($buf) . ' bytes to stream, completed writing only ' . $written . ' bytes');
  310. }
  311. $written += $wrote;
  312. continue;
  313. }
  314. if (false !== $writable) {
  315. $res = stream_get_meta_data($this->stream);
  316. if (!empty($res['timed_out'])) {
  317. throw new \Kafka\Exception\SocketTimeout('Timed out writing ' . strlen($buf) . ' bytes to stream after writing ' . $written . ' bytes');
  318. }
  319. }
  320. throw new \Kafka\Exception\Socket('Could not write ' . strlen($buf) . ' bytes to stream');
  321. }
  322. return $written;
  323. }
  324. // }}}
  325. // {{{ public function rewind()
  326. /**
  327. * Rewind the stream
  328. *
  329. * @return void
  330. */
  331. public function rewind()
  332. {
  333. if (is_resource($this->stream)) {
  334. rewind($this->stream);
  335. }
  336. }
  337. // }}}
  338. // }}}
  339. }