Client.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <?php
  2. namespace PHPSocketIO;
  3. use PHPSocketIO\Parser\Parser;
  4. class Client
  5. {
  6. public $server = null;
  7. public $conn = null;
  8. public $encoder = null;
  9. public $decoder = null;
  10. public $id = null;
  11. public $request = null;
  12. public $nsps = array();
  13. public $connectBuffer = array();
  14. public function __construct($server, $conn)
  15. {
  16. $this->server = $server;
  17. $this->conn = $conn;
  18. $this->encoder = new \PHPSocketIO\Parser\Encoder();
  19. $this->decoder = new \PHPSocketIO\Parser\Decoder();
  20. $this->id = $conn->id;
  21. $this->request = $conn->request;
  22. $this->setup();
  23. Debug::debug('Client __construct');
  24. }
  25. public function __destruct()
  26. {
  27. Debug::debug('Client __destruct');
  28. }
  29. /**
  30. * Sets up event listeners.
  31. *
  32. * @api private
  33. */
  34. public function setup(){
  35. $this->decoder->on('decoded', array($this,'ondecoded'));
  36. $this->conn->on('data', array($this,'ondata'));
  37. $this->conn->on('error', array($this, 'onerror'));
  38. $this->conn->on('close' ,array($this, 'onclose'));
  39. }
  40. /**
  41. * Connects a client to a namespace.
  42. *
  43. * @param {String} namespace name
  44. * @api private
  45. */
  46. public function connect($name){
  47. if (!isset($this->server->nsps[$name]))
  48. {
  49. $this->packet(array('type'=> Parser::ERROR, 'nsp'=> $name, 'data'=> 'Invalid namespace'));
  50. return;
  51. }
  52. $nsp = $this->server->of($name);
  53. if ('/' !== $name && !isset($this->nsps['/']))
  54. {
  55. $this->connectBuffer[$name] = $name;
  56. return;
  57. }
  58. $nsp->add($this, $nsp, array($this, 'nspAdd'));
  59. }
  60. public function nspAdd($socket, $nsp)
  61. {
  62. $this->sockets[$socket->id] = $socket;
  63. $this->nsps[$nsp->name] = $socket;
  64. if ('/' === $nsp->name && $this->connectBuffer)
  65. {
  66. foreach($this->connectBuffer as $name)
  67. {
  68. $this->connect($name);
  69. }
  70. $this->connectBuffer = array();
  71. }
  72. }
  73. /**
  74. * Disconnects from all namespaces and closes transport.
  75. *
  76. * @api private
  77. */
  78. public function disconnect()
  79. {
  80. foreach($this->sockets as $socket)
  81. {
  82. $socket->disconnect();
  83. }
  84. $this->sockets = array();
  85. $this->close();
  86. }
  87. /**
  88. * Removes a socket. Called by each `Socket`.
  89. *
  90. * @api private
  91. */
  92. public function remove($socket)
  93. {
  94. if(isset($this->sockets[$socket->id]))
  95. {
  96. $nsp = $this->sockets[$socket->id]->nsp->name;
  97. unset($this->sockets[$socket->id]);
  98. unset($this->nsps[$nsp]);
  99. } else {
  100. //echo('ignoring remove for '. $socket->id);
  101. }
  102. }
  103. /**
  104. * Closes the underlying connection.
  105. *
  106. * @api private
  107. */
  108. public function close()
  109. {
  110. if (empty($this->conn)) return;
  111. if('open' === $this->conn->readyState)
  112. {
  113. //echo('forcing transport close');
  114. $this->conn->close();
  115. $this->onclose('forced server close');
  116. }
  117. }
  118. /**
  119. * Writes a packet to the transport.
  120. *
  121. * @param {Object} packet object
  122. * @param {Object} options
  123. * @api private
  124. */
  125. public function packet($packet, $preEncoded = false, $volatile = false)
  126. {
  127. if(!empty($this->conn) && 'open' === $this->conn->readyState)
  128. {
  129. if (!$preEncoded)
  130. {
  131. // not broadcasting, need to encode
  132. $encodedPackets = $this->encoder->encode($packet);
  133. $this->writeToEngine($encodedPackets, $volatile);
  134. } else { // a broadcast pre-encodes a packet
  135. $this->writeToEngine($packet);
  136. }
  137. } else {
  138. // todo check
  139. // echo('ignoring packet write ' . var_export($packet, true));
  140. }
  141. }
  142. public function writeToEngine($encodedPackets, $volatile = false)
  143. {
  144. if($volatile)echo new \Exception('volatile');
  145. if ($volatile && !$this->conn->transport->writable) return;
  146. // todo check
  147. if(isset($encodedPackets['nsp']))unset($encodedPackets['nsp']);
  148. foreach($encodedPackets as $packet)
  149. {
  150. $this->conn->write($packet);
  151. }
  152. }
  153. /**
  154. * Called with incoming transport data.
  155. *
  156. * @api private
  157. */
  158. public function ondata($data)
  159. {
  160. try {
  161. // todo chek '2["chat message","2"]' . "\0" . ''
  162. $this->decoder->add(trim($data));
  163. } catch(\Exception $e) {
  164. $this->onerror($e);
  165. }
  166. }
  167. /**
  168. * Called when parser fully decodes a packet.
  169. *
  170. * @api private
  171. */
  172. public function ondecoded($packet)
  173. {
  174. if(Parser::CONNECT == $packet['type'])
  175. {
  176. $this->connect($packet['nsp']);
  177. } else {
  178. if(isset($this->nsps[$packet['nsp']]))
  179. {
  180. $this->nsps[$packet['nsp']]->onpacket($packet);
  181. } else {
  182. //echo('no socket for namespace ' . $packet['nsp']);
  183. }
  184. }
  185. }
  186. /**
  187. * Handles an error.
  188. *
  189. * @param {Objcet} error object
  190. * @api private
  191. */
  192. public function onerror($err)
  193. {
  194. foreach($this->sockets as $socket)
  195. {
  196. $socket->onerror($err);
  197. }
  198. $this->onclose('client error');
  199. }
  200. /**
  201. * Called upon transport close.
  202. *
  203. * @param {String} reason
  204. * @api private
  205. */
  206. public function onclose($reason)
  207. {
  208. if (empty($this->conn)) return;
  209. // ignore a potential subsequent `close` event
  210. $this->destroy();
  211. // `nsps` and `sockets` are cleaned up seamlessly
  212. foreach($this->sockets as $socket)
  213. {
  214. $socket->onclose($reason);
  215. }
  216. $this->sockets = null;
  217. }
  218. /**
  219. * Cleans up event listeners.
  220. *
  221. * @api private
  222. */
  223. public function destroy()
  224. {
  225. if (!$this->conn) return;
  226. $this->conn->removeAllListeners();
  227. $this->decoder->removeAllListeners();
  228. $this->encoder->removeAllListeners();
  229. $this->server = $this->conn = $this->encoder = $this->decoder = $this->request = $this->nsps = null;
  230. }
  231. }