- 框架初始化
 - 安装插件
 - 修复PHP8.4报错
This commit is contained in:
2025-04-19 17:21:20 +08:00
commit c6a4e1f5f6
5306 changed files with 967782 additions and 0 deletions

View File

@@ -0,0 +1,468 @@
<?php
namespace PHPSocketIO;
use Closure;
use Exception;
use PHPSocketIO\Event\Emitter;
use PHPSocketIO\Parser\Parser;
class Socket extends Emitter
{
public $nsp = null;
public $server = null;
public $adapter = null;
public $id = null;
public $path = '/';
public $request = null;
public $client = null;
public $conn = null;
public $rooms = [];
public $_rooms = [];
public $flags = [];
public $acks = [];
public $connected = true;
public $disconnected = false;
public $handshake = [];
public $userId = null;
public $isGuest = false;
public $addedUser = null;
public $username = null;
public static $events = [
'error' => 'error',
'connect' => 'connect',
'disconnect' => 'disconnect',
'newListener' => 'newListener',
'removeListener' => 'removeListener'
];
public static $flagsMap = [
'json' => 'json',
'volatile' => 'volatile',
'broadcast' => 'broadcast'
];
public function __construct($nsp, $client)
{
$this->nsp = $nsp;
$this->server = $nsp->server;
$this->adapter = $this->nsp->adapter;
$this->id = ($nsp->name !== '/') ? $nsp->name . '#' . $client->id : $client->id;
$this->request = $client->request;
$this->client = $client;
$this->conn = $client->conn;
$this->handshake = $this->buildHandshake();
Debug::debug('IO Socket __construct');
}
public function __destruct()
{
Debug::debug('IO Socket __destruct');
}
public function buildHandshake(): array
{
//todo check this->request->_query
$info = ! empty($this->request->url) ? parse_url($this->request->url) : [];
$query = [];
if (isset($info['query'])) {
parse_str($info['query'], $query);
}
return [
'headers' => $this->request->headers ?? [],
'time' => date('D M d Y H:i:s') . ' GMT',
'address' => $this->conn->remoteAddress,
'xdomain' => isset($this->request->headers['origin']),
'secure' => ! empty($this->request->connection->encrypted),
'issued' => time(),
'url' => $this->request->url ?? '',
'query' => $query,
];
}
public function __get($name)
{
if ($name === 'broadcast') {
$this->flags['broadcast'] = true;
return $this;
}
return null;
}
/**
* @throws Exception
*/
public function emit($ev = null)
{
$args = func_get_args();
if (isset(self::$events[$ev])) {
call_user_func_array(array(get_parent_class(__CLASS__), 'emit'), $args);
} else {
$packet = [];
$packet['type'] = Parser::EVENT;
$packet['data'] = $args;
$flags = $this->flags;
// access last argument to see if it's an ACK callback
if (is_callable(end($args))) {
if ($this->_rooms || isset($flags['broadcast'])) {
throw new Exception('Callbacks are not supported when broadcasting');
}
echo('emitting packet with ack id ' . $this->nsp->ids);
$this->acks[$this->nsp->ids] = array_pop($args);
$packet['id'] = $this->nsp->ids++;
}
if ($this->_rooms || ! empty($flags['broadcast'])) {
$this->adapter->broadcast(
$packet,
[
'except' => [$this->id => $this->id],
'rooms' => $this->_rooms,
'flags' => $flags
]
);
} else {
// dispatch packet
$this->packet($packet);
}
// reset flags
$this->_rooms = [];
$this->flags = [];
}
return $this;
}
/**
* Targets a room when broadcasting.
*
* @param {String} name
* @return Socket {Socket} self
* @api public
*/
public function to($name): Socket
{
if (! isset($this->_rooms[$name])) {
$this->_rooms[$name] = $name;
}
return $this;
}
public function in($name): Socket
{
return $this->to($name);
}
/**
* Sends a `message` event.
*
* @return Socket {Socket} self
* @api public
*/
public function send(): Socket
{
$args = func_get_args();
array_unshift($args, 'message');
call_user_func_array([$this, 'emit'], $args);
return $this;
}
public function write(): Socket
{
$args = func_get_args();
array_unshift($args, 'message');
call_user_func_array([$this, 'emit'], $args);
return $this;
}
/**
* Writes a packet.
*
* @param {Object} packet object
* @param {Object} options
* @api private
*/
public function packet($packet, $preEncoded = false)
{
if (! $this->nsp || ! $this->client) {
return;
}
$packet['nsp'] = $this->nsp->name;
$this->client->packet($packet, $preEncoded, false);
}
/**
* Joins a room.
*
* @param {String} room
* @return Socket {Socket} self
* @api private
*/
public function join($room): Socket
{
if (! $this->connected) {
return $this;
}
if (isset($this->rooms[$room])) {
return $this;
}
$this->adapter->add($this->id, $room);
$this->rooms[$room] = $room;
return $this;
}
/**
* Leaves a room.
*
* @param {String} room
* @return Socket {Socket} self
* @api private
*/
public function leave($room): Socket
{
$this->adapter->del($this->id, $room);
unset($this->rooms[$room]);
return $this;
}
/**
* Leave all rooms.
*
* @api private
*/
public function leaveAll()
{
$this->adapter->delAll($this->id);
$this->rooms = [];
}
/**
* Called by `Namespace` upon succesful
* middleware execution (ie: authorization).
*
* @api private
*/
public function onconnect()
{
$this->nsp->connected[$this->id] = $this;
$this->join($this->id);
$this->packet(
[
'type' => Parser::CONNECT
]
);
}
/**
* Called with each packet. Called by `Client`.
*
* @param {Object} packet
* @throws Exception
* @api private
*/
public function onpacket($packet)
{
switch ($packet['type']) {
case Parser::BINARY_EVENT:
case Parser::EVENT:
$this->onevent($packet);
break;
case Parser::BINARY_ACK:
case Parser::ACK:
$this->onack($packet);
break;
case Parser::DISCONNECT:
$this->ondisconnect();
break;
case Parser::ERROR:
$this->emit('error', $packet['data']);
}
}
/**
* Called upon event packet.
*
* @param {Object} packet object
* @api private
*/
public function onevent($packet)
{
$args = $packet['data'] ?? [];
if (! empty($packet['id']) || (isset($packet['id']) && $packet['id'] === 0)) {
$args[] = $this->ack($packet['id']);
}
call_user_func_array(array(get_parent_class(__CLASS__), 'emit'), $args);
}
/**
* Produces an ack callback to emit with an event.
*
* @param {Number} packet id
* @api private
*/
public function ack($id): Closure
{
$sent = false;
return function () use (&$sent, $id) {
$self = $this;
// prevent double callbacks
if ($sent) {
return;
}
$args = func_get_args();
$type = $this->hasBin($args) ? Parser::BINARY_ACK : Parser::ACK;
$self->packet(
[
'id' => $id,
'type' => $type,
'data' => $args
]
);
};
}
/**
* Called upon ack packet.
*
* @api private
*/
public function onack($packet)
{
$ack = $this->acks[$packet['id']];
if (is_callable($ack)) {
call_user_func($ack, $packet['data']);
unset($this->acks[$packet['id']]);
} else {
echo('bad ack ' . $packet['id']);
}
}
/**
* Called upon client disconnect packet.
*
* @throws Exception
* @api private
*/
public function ondisconnect()
{
$this->onclose('client namespace disconnect');
}
/**
* Handles a client error.
*
* @throws Exception
* @api private
*/
public function onerror($err)
{
if ($this->listeners('error')) {
$this->emit('error', $err);
}
}
/**
* Called upon closing. Called by `Client`.
*
* @param {String} reason
* @param {Error} optional error object
* @throws Exception
* @api private
*/
public function onclose($reason)
{
if (! $this->connected) {
return $this;
}
$this->emit('disconnect', $reason);
$this->leaveAll();
$this->nsp->remove($this);
$this->client->remove($this);
$this->connected = false;
$this->disconnected = true;
unset($this->nsp->connected[$this->id]);
// ....
$this->nsp = null;
$this->server = null;
$this->adapter = null;
$this->request = null;
$this->client = null;
$this->conn = null;
$this->removeAllListeners();
}
/**
* Produces an `error` packet.
*
* @param {Object} error object
* @api private
*/
public function error($err)
{
$this->packet(
[
'type' => Parser::ERROR, 'data' => $err
]
);
}
/**
* Disconnects this client.
*
* @param bool $close
* @return Socket {Socket} self
* @throws Exception
* @api public
*/
public function disconnect(bool $close = false): Socket
{
if (! $this->connected) {
return $this;
}
if ($close) {
$this->client->disconnect();
} else {
$this->packet(
[
'type' => Parser::DISCONNECT
]
);
$this->onclose('server namespace disconnect');
}
return $this;
}
/**
* Sets the compress flag.
*
* @param {Boolean} if `true`, compresses the sending data
* @return Socket {Socket} self
* @api public
*/
public function compress($compress): Socket
{
$this->flags['compress'] = $compress;
return $this;
}
protected function hasBin($args): bool
{
$hasBin = false;
array_walk_recursive(
$args,
function ($item, $key) use ($hasBin) {
if (! ctype_print($item)) {
$hasBin = true;
}
}
);
return $hasBin;
}
}