server = $server; $this->conn = $conn; $this->encoder = new \PHPSocketIO\Parser\Encoder(); $this->decoder = new \PHPSocketIO\Parser\Decoder(); $this->id = $conn->id; $this->request = $conn->request; $this->setup(); Debug::debug('Client __construct'); } public function __destruct() { Debug::debug('Client __destruct'); } /** * Sets up event listeners. * * @api private */ public function setup(){ $this->decoder->on('decoded', array($this,'ondecoded')); $this->conn->on('data', array($this,'ondata')); $this->conn->on('error', array($this, 'onerror')); $this->conn->on('close' ,array($this, 'onclose')); } /** * Connects a client to a namespace. * * @param {String} namespace name * @api private */ public function connect($name){ if (!isset($this->server->nsps[$name])) { $this->packet(array('type'=> Parser::ERROR, 'nsp'=> $name, 'data'=> 'Invalid namespace')); return; } $nsp = $this->server->of($name); if ('/' !== $name && !isset($this->nsps['/'])) { $this->connectBuffer[$name] = $name; return; } $nsp->add($this, $nsp, array($this, 'nspAdd')); } public function nspAdd($socket, $nsp) { $this->sockets[$socket->id] = $socket; $this->nsps[$nsp->name] = $socket; if ('/' === $nsp->name && $this->connectBuffer) { foreach($this->connectBuffer as $name) { $this->connect($name); } $this->connectBuffer = array(); } } /** * Disconnects from all namespaces and closes transport. * * @api private */ public function disconnect() { foreach($this->sockets as $socket) { $socket->disconnect(); } $this->sockets = array(); $this->close(); } /** * Removes a socket. Called by each `Socket`. * * @api private */ public function remove($socket) { if(isset($this->sockets[$socket->id])) { $nsp = $this->sockets[$socket->id]->nsp->name; unset($this->sockets[$socket->id]); unset($this->nsps[$nsp]); } else { //echo('ignoring remove for '. $socket->id); } } /** * Closes the underlying connection. * * @api private */ public function close() { if (empty($this->conn)) return; if('open' === $this->conn->readyState) { //echo('forcing transport close'); $this->conn->close(); $this->onclose('forced server close'); } } /** * Writes a packet to the transport. * * @param {Object} packet object * @param {Object} options * @api private */ public function packet($packet, $preEncoded = false, $volatile = false) { if(!empty($this->conn) && 'open' === $this->conn->readyState) { if (!$preEncoded) { // not broadcasting, need to encode $encodedPackets = $this->encoder->encode($packet); $this->writeToEngine($encodedPackets, $volatile); } else { // a broadcast pre-encodes a packet $this->writeToEngine($packet); } } else { // todo check // echo('ignoring packet write ' . var_export($packet, true)); } } public function writeToEngine($encodedPackets, $volatile = false) { if($volatile)echo new \Exception('volatile'); if ($volatile && !$this->conn->transport->writable) return; // todo check if(isset($encodedPackets['nsp']))unset($encodedPackets['nsp']); foreach($encodedPackets as $packet) { $this->conn->write($packet); } } /** * Called with incoming transport data. * * @api private */ public function ondata($data) { try { // todo chek '2["chat message","2"]' . "\0" . '' $this->decoder->add(trim($data)); } catch(\Exception $e) { $this->onerror($e); } } /** * Called when parser fully decodes a packet. * * @api private */ public function ondecoded($packet) { if(Parser::CONNECT == $packet['type']) { $this->connect($packet['nsp']); } else { if(isset($this->nsps[$packet['nsp']])) { $this->nsps[$packet['nsp']]->onpacket($packet); } else { //echo('no socket for namespace ' . $packet['nsp']); } } } /** * Handles an error. * * @param {Objcet} error object * @api private */ public function onerror($err) { foreach($this->sockets as $socket) { $socket->onerror($err); } $this->onclose('client error'); } /** * Called upon transport close. * * @param {String} reason * @api private */ public function onclose($reason) { if (empty($this->conn)) return; // ignore a potential subsequent `close` event $this->destroy(); // `nsps` and `sockets` are cleaned up seamlessly foreach($this->sockets as $socket) { $socket->onclose($reason); } $this->sockets = null; } /** * Cleans up event listeners. * * @api private */ public function destroy() { if (!$this->conn) return; $this->conn->removeAllListeners(); $this->decoder->removeAllListeners(); $this->encoder->removeAllListeners(); $this->server = $this->conn = $this->encoder = $this->decoder = $this->request = $this->nsps = null; } }