Client.php 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. /**
  3. * Hoa
  4. *
  5. *
  6. * @license
  7. *
  8. * New BSD License
  9. *
  10. * Copyright © 2007-2017, Hoa community. All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the Hoa nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. namespace Hoa\Socket;
  36. use Hoa\Consistency;
  37. use Hoa\Stream;
  38. /**
  39. * Class \Hoa\Socket\Client.
  40. *
  41. * Established a client connection.
  42. *
  43. * @copyright Copyright © 2007-2017 Hoa community
  44. * @license New BSD License
  45. */
  46. class Client extends Connection
  47. {
  48. /**
  49. * Open client socket asynchronously.
  50. *
  51. * @const int
  52. */
  53. const ASYNCHRONOUS = STREAM_CLIENT_ASYNC_CONNECT;
  54. /**
  55. * Open client socket connection.
  56. *
  57. * @const int
  58. */
  59. const CONNECT = STREAM_CLIENT_CONNECT;
  60. /**
  61. * Client socket should remain persistent between page loads.
  62. *
  63. * @const int
  64. */
  65. const PERSISTENT = STREAM_CLIENT_PERSISTENT;
  66. /**
  67. * Encryption: SSLv2.
  68. *
  69. * @const int
  70. */
  71. const ENCRYPTION_SSLv2 = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
  72. /**
  73. * Encryption: SSLv3.
  74. *
  75. * @const int
  76. */
  77. const ENCRYPTION_SSLv3 = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
  78. /**
  79. * Encryption: SSLv2.3.
  80. *
  81. * @const int
  82. */
  83. const ENCRYPTION_SSLv23 = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
  84. /**
  85. * Encryption: TLS.
  86. *
  87. * @const int
  88. */
  89. const ENCRYPTION_TLS = STREAM_CRYPTO_METHOD_TLS_CLIENT;
  90. /**
  91. * Encryption: TLSv1.0.
  92. *
  93. * @const int
  94. */
  95. const ENCRYPTION_TLSv1_0 = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT;
  96. /**
  97. * Encryption: TLSv1.1.
  98. *
  99. * @const int
  100. */
  101. const ENCRYPTION_TLSv1_1 = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
  102. /**
  103. * Encryption: TLSv1.2.
  104. *
  105. * @const int
  106. */
  107. const ENCRYPTION_TLSv1_2 = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
  108. /**
  109. * Encryption: ANY
  110. *
  111. * @const int
  112. */
  113. const ENCRYPTION_ANY = STREAM_CRYPTO_METHOD_ANY_CLIENT;
  114. /**
  115. * Stack of connections.
  116. *
  117. * @var array
  118. */
  119. protected $_stack = [];
  120. /**
  121. * Start a connection.
  122. *
  123. * @param string $socket Socket URI.
  124. * @param int $timeout Timeout.
  125. * @param int $flag Flag, see the child::* constants.
  126. * @param string $context Context ID (please, see the
  127. * \Hoa\Stream\Context class).
  128. */
  129. public function __construct(
  130. $socket,
  131. $timeout = 30,
  132. $flag = self::CONNECT,
  133. $context = null
  134. ) {
  135. parent::__construct($socket, $timeout, self::CONNECT | $flag, $context);
  136. return;
  137. }
  138. /**
  139. * Open the stream and return the associated resource.
  140. *
  141. * @param string $streamName Socket URI.
  142. * @param \Hoa\Stream\Context $context Context.
  143. * @return resource
  144. * @throws \Hoa\Socket\Exception
  145. */
  146. protected function &_open($streamName, Stream\Context $context = null)
  147. {
  148. if (null === $context) {
  149. $connection = @stream_socket_client(
  150. $streamName,
  151. $errno,
  152. $errstr,
  153. $this->getTimeout(),
  154. $this->getFlag()
  155. );
  156. } else {
  157. $connection = @stream_socket_client(
  158. $streamName,
  159. $errno,
  160. $errstr,
  161. $this->getTimeout(),
  162. $this->getFlag(),
  163. $context->getContext()
  164. );
  165. }
  166. if (false === $connection) {
  167. if ($errno === 0) {
  168. throw new Exception('Client cannot join %s.', 0, $streamName);
  169. } else {
  170. throw new Exception(
  171. 'Client returns an error (number %d): %s while trying ' .
  172. 'to join %s.',
  173. 1,
  174. [$errno, $errstr, $streamName]
  175. );
  176. }
  177. }
  178. $this->_stack[] = $connection;
  179. $id = $this->getNodeId($connection);
  180. $this->_node = Consistency\Autoloader::dnew(
  181. $this->getNodeName(),
  182. [$id, $connection, $this]
  183. );
  184. $this->_nodes[$id] = $this->_node;
  185. return $connection;
  186. }
  187. /**
  188. * Close the current stream.
  189. *
  190. * @return bool
  191. */
  192. protected function _close()
  193. {
  194. if (true === $this->isPersistent()) {
  195. return false;
  196. }
  197. return @fclose($this->getStream());
  198. }
  199. /**
  200. * Select connections.
  201. *
  202. * @return \Hoa\Socket\Client
  203. */
  204. public function select()
  205. {
  206. $read = $this->getStack();
  207. $write = null;
  208. $except = null;
  209. @stream_select($read, $write, $except, $this->getTimeout(), 0);
  210. foreach ($read as $socket) {
  211. $this->_iterator[] = $socket;
  212. }
  213. return $this;
  214. }
  215. /**
  216. * Consider another client when selecting connection.
  217. *
  218. * @param \Hoa\Socket\Connection $other Other client.
  219. * @return \Hoa\Socket\Client
  220. * @throws \Hoa\Socket\Exception
  221. */
  222. public function consider(Connection $other)
  223. {
  224. if (!($other instanceof self)) {
  225. throw new Exception(
  226. 'Other client must be of type %s.',
  227. 2,
  228. __CLASS__
  229. );
  230. }
  231. if (true === $other->isDisconnected()) {
  232. $other->connect();
  233. }
  234. $otherNode = $other->getCurrentNode();
  235. $this->_stack[] = $otherNode->getSocket();
  236. $this->_nodes[$otherNode->getId()] = $otherNode;
  237. return $this;
  238. }
  239. /**
  240. * Check if the current node belongs to a specific server.
  241. *
  242. * @param \Hoa\Socket\Connection $server Server.
  243. * @return bool
  244. */
  245. public function is(Connection $server)
  246. {
  247. return $this->getStream() === $server->getStream();
  248. }
  249. /**
  250. * Set and get the current selected connection.
  251. *
  252. * @return \Hoa\Socket\Node
  253. */
  254. public function current()
  255. {
  256. $current = parent::_current();
  257. return $this->_node = $this->_nodes[$this->getNodeId($current)];
  258. }
  259. /**
  260. * Check if the connection is connected or not.
  261. *
  262. * @return bool
  263. */
  264. public function isConnected()
  265. {
  266. return (bool) ($this->getFlag() & self::CONNECT);
  267. }
  268. /**
  269. * Check if the connection is asynchronous or not.
  270. *
  271. * @return bool
  272. */
  273. public function isAsynchronous()
  274. {
  275. return (bool) ($this->getFlag() & self::ASYNCHRONOUS);
  276. }
  277. /**
  278. * Check if the connection is persistent or not.
  279. *
  280. * @return bool
  281. */
  282. public function isPersistent()
  283. {
  284. return (bool) ($this->getFlag() & self::PERSISTENT);
  285. }
  286. /**
  287. * Return internal node stack.
  288. *
  289. * @return array
  290. */
  291. protected function getStack()
  292. {
  293. return $this->_stack;
  294. }
  295. }