PageRenderTime 39ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/foody.blogtamsudev.vn/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php

https://gitlab.com/ntphuc/BackendFeedy
PHP | 393 lines | 215 code | 64 blank | 114 comment | 35 complexity | 2af9add6146a6f65d18207963520e012 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Predis package.
  4. *
  5. * (c) Daniele Alessandri <suppakilla@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Predis\Connection;
  11. use Predis\Command\CommandInterface;
  12. use Predis\NotSupportedException;
  13. use Predis\Response\Error as ErrorResponse;
  14. use Predis\Response\Status as StatusResponse;
  15. /**
  16. * This class provides the implementation of a Predis connection that uses the
  17. * PHP socket extension for network communication and wraps the phpiredis C
  18. * extension (PHP bindings for hiredis) to parse the Redis protocol.
  19. *
  20. * This class is intended to provide an optional low-overhead alternative for
  21. * processing responses from Redis compared to the standard pure-PHP classes.
  22. * Differences in speed when dealing with short inline responses are practically
  23. * nonexistent, the actual speed boost is for big multibulk responses when this
  24. * protocol processor can parse and return responses very fast.
  25. *
  26. * For instructions on how to build and install the phpiredis extension, please
  27. * consult the repository of the project.
  28. *
  29. * The connection parameters supported by this class are:
  30. *
  31. * - scheme: it can be either 'redis', 'tcp' or 'unix'.
  32. * - host: hostname or IP address of the server.
  33. * - port: TCP port of the server.
  34. * - path: path of a UNIX domain socket when scheme is 'unix'.
  35. * - timeout: timeout to perform the connection.
  36. * - read_write_timeout: timeout of read / write operations.
  37. *
  38. * @link http://github.com/nrk/phpiredis
  39. *
  40. * @author Daniele Alessandri <suppakilla@gmail.com>
  41. */
  42. class PhpiredisSocketConnection extends AbstractConnection
  43. {
  44. private $reader;
  45. /**
  46. * {@inheritdoc}
  47. */
  48. public function __construct(ParametersInterface $parameters)
  49. {
  50. $this->assertExtensions();
  51. parent::__construct($parameters);
  52. $this->reader = $this->createReader();
  53. }
  54. /**
  55. * Disconnects from the server and destroys the underlying resource and the
  56. * protocol reader resource when PHP's garbage collector kicks in.
  57. */
  58. public function __destruct()
  59. {
  60. phpiredis_reader_destroy($this->reader);
  61. parent::__destruct();
  62. }
  63. /**
  64. * Checks if the socket and phpiredis extensions are loaded in PHP.
  65. */
  66. protected function assertExtensions()
  67. {
  68. if (!extension_loaded('sockets')) {
  69. throw new NotSupportedException(
  70. 'The "sockets" extension is required by this connection backend.'
  71. );
  72. }
  73. if (!extension_loaded('phpiredis')) {
  74. throw new NotSupportedException(
  75. 'The "phpiredis" extension is required by this connection backend.'
  76. );
  77. }
  78. }
  79. /**
  80. * {@inheritdoc}
  81. */
  82. protected function assertParameters(ParametersInterface $parameters)
  83. {
  84. parent::assertParameters($parameters);
  85. if (isset($parameters->persistent)) {
  86. throw new NotSupportedException(
  87. 'Persistent connections are not supported by this connection backend.'
  88. );
  89. }
  90. return $parameters;
  91. }
  92. /**
  93. * Creates a new instance of the protocol reader resource.
  94. *
  95. * @return resource
  96. */
  97. private function createReader()
  98. {
  99. $reader = phpiredis_reader_create();
  100. phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
  101. phpiredis_reader_set_error_handler($reader, $this->getErrorHandler());
  102. return $reader;
  103. }
  104. /**
  105. * Returns the underlying protocol reader resource.
  106. *
  107. * @return resource
  108. */
  109. protected function getReader()
  110. {
  111. return $this->reader;
  112. }
  113. /**
  114. * Returns the handler used by the protocol reader for inline responses.
  115. *
  116. * @return \Closure
  117. */
  118. private function getStatusHandler()
  119. {
  120. return function ($payload) {
  121. return StatusResponse::get($payload);
  122. };
  123. }
  124. /**
  125. * Returns the handler used by the protocol reader for error responses.
  126. *
  127. * @return \Closure
  128. */
  129. protected function getErrorHandler()
  130. {
  131. return function ($payload) {
  132. return new ErrorResponse($payload);
  133. };
  134. }
  135. /**
  136. * Helper method used to throw exceptions on socket errors.
  137. */
  138. private function emitSocketError()
  139. {
  140. $errno = socket_last_error();
  141. $errstr = socket_strerror($errno);
  142. $this->disconnect();
  143. $this->onConnectionError(trim($errstr), $errno);
  144. }
  145. /**
  146. * Gets the address of an host from connection parameters.
  147. *
  148. * @param ParametersInterface $parameters Parameters used to initialize the connection.
  149. *
  150. * @return string
  151. */
  152. protected static function getAddress(ParametersInterface $parameters)
  153. {
  154. if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) {
  155. return $host;
  156. }
  157. if ($host === $address = gethostbyname($host)) {
  158. return false;
  159. }
  160. return $address;
  161. }
  162. /**
  163. * {@inheritdoc}
  164. */
  165. protected function createResource()
  166. {
  167. $parameters = $this->parameters;
  168. if ($parameters->scheme === 'unix') {
  169. $address = $parameters->path;
  170. $domain = AF_UNIX;
  171. $protocol = 0;
  172. } else {
  173. if (false === $address = self::getAddress($parameters)) {
  174. $this->onConnectionError("Cannot resolve the address of '$parameters->host'.");
  175. }
  176. $domain = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? AF_INET6 : AF_INET;
  177. $protocol = SOL_TCP;
  178. }
  179. $socket = @socket_create($domain, SOCK_STREAM, $protocol);
  180. if (!is_resource($socket)) {
  181. $this->emitSocketError();
  182. }
  183. $this->setSocketOptions($socket, $parameters);
  184. $this->connectWithTimeout($socket, $address, $parameters);
  185. return $socket;
  186. }
  187. /**
  188. * Sets options on the socket resource from the connection parameters.
  189. *
  190. * @param resource $socket Socket resource.
  191. * @param ParametersInterface $parameters Parameters used to initialize the connection.
  192. */
  193. private function setSocketOptions($socket, ParametersInterface $parameters)
  194. {
  195. if ($parameters->scheme !== 'unix') {
  196. if (!socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1)) {
  197. $this->emitSocketError();
  198. }
  199. if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) {
  200. $this->emitSocketError();
  201. }
  202. }
  203. if (isset($parameters->read_write_timeout)) {
  204. $rwtimeout = (float) $parameters->read_write_timeout;
  205. $timeoutSec = floor($rwtimeout);
  206. $timeoutUsec = ($rwtimeout - $timeoutSec) * 1000000;
  207. $timeout = array(
  208. 'sec' => $timeoutSec,
  209. 'usec' => $timeoutUsec,
  210. );
  211. if (!socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout)) {
  212. $this->emitSocketError();
  213. }
  214. if (!socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout)) {
  215. $this->emitSocketError();
  216. }
  217. }
  218. }
  219. /**
  220. * Opens the actual connection to the server with a timeout.
  221. *
  222. * @param resource $socket Socket resource.
  223. * @param string $address IP address (DNS-resolved from hostname)
  224. * @param ParametersInterface $parameters Parameters used to initialize the connection.
  225. *
  226. * @return string
  227. */
  228. private function connectWithTimeout($socket, $address, ParametersInterface $parameters)
  229. {
  230. socket_set_nonblock($socket);
  231. if (@socket_connect($socket, $address, (int) $parameters->port) === false) {
  232. $error = socket_last_error();
  233. if ($error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) {
  234. $this->emitSocketError();
  235. }
  236. }
  237. socket_set_block($socket);
  238. $null = null;
  239. $selectable = array($socket);
  240. $timeout = (float) $parameters->timeout;
  241. $timeoutSecs = floor($timeout);
  242. $timeoutUSecs = ($timeout - $timeoutSecs) * 1000000;
  243. $selected = socket_select($selectable, $selectable, $null, $timeoutSecs, $timeoutUSecs);
  244. if ($selected === 2) {
  245. $this->onConnectionError('Connection refused.', SOCKET_ECONNREFUSED);
  246. }
  247. if ($selected === 0) {
  248. $this->onConnectionError('Connection timed out.', SOCKET_ETIMEDOUT);
  249. }
  250. if ($selected === false) {
  251. $this->emitSocketError();
  252. }
  253. }
  254. /**
  255. * {@inheritdoc}
  256. */
  257. public function connect()
  258. {
  259. if (parent::connect() && $this->initCommands) {
  260. foreach ($this->initCommands as $command) {
  261. $this->executeCommand($command);
  262. }
  263. }
  264. }
  265. /**
  266. * {@inheritdoc}
  267. */
  268. public function disconnect()
  269. {
  270. if ($this->isConnected()) {
  271. socket_close($this->getResource());
  272. parent::disconnect();
  273. }
  274. }
  275. /**
  276. * {@inheritdoc}
  277. */
  278. protected function write($buffer)
  279. {
  280. $socket = $this->getResource();
  281. while (($length = strlen($buffer)) > 0) {
  282. $written = socket_write($socket, $buffer, $length);
  283. if ($length === $written) {
  284. return;
  285. }
  286. if ($written === false) {
  287. $this->onConnectionError('Error while writing bytes to the server.');
  288. }
  289. $buffer = substr($buffer, $written);
  290. }
  291. }
  292. /**
  293. * {@inheritdoc}
  294. */
  295. public function read()
  296. {
  297. $socket = $this->getResource();
  298. $reader = $this->reader;
  299. while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) {
  300. if (@socket_recv($socket, $buffer, 4096, 0) === false || $buffer === '' || $buffer === null) {
  301. $this->emitSocketError();
  302. }
  303. phpiredis_reader_feed($reader, $buffer);
  304. }
  305. if ($state === PHPIREDIS_READER_STATE_COMPLETE) {
  306. return phpiredis_reader_get_reply($reader);
  307. } else {
  308. $this->onProtocolError(phpiredis_reader_get_error($reader));
  309. return;
  310. }
  311. }
  312. /**
  313. * {@inheritdoc}
  314. */
  315. public function writeRequest(CommandInterface $command)
  316. {
  317. $arguments = $command->getArguments();
  318. array_unshift($arguments, $command->getId());
  319. $this->write(phpiredis_format_command($arguments));
  320. }
  321. /**
  322. * {@inheritdoc}
  323. */
  324. public function __wakeup()
  325. {
  326. $this->assertExtensions();
  327. $this->reader = $this->createReader();
  328. }
  329. }