diff --git a/composer.json b/composer.json index 57db884..d007d1a 100755 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "arthurkushman/php-wss", - "description": "Web-socket server with URI parse and multi-process support", + "description": "Web-socket server/client with URI parse and multi-process support", "keywords": [ "web-sockets", "web socket server", @@ -13,7 +13,7 @@ "license": "MIT", "homepage": "https://github.com/arthurkushman/php-wss", "require": { - "php": ">=7.1", + "php": ">=7.4", "ext-pcntl": "*" }, "autoload": { diff --git a/src/Components/ClientConfig.php b/src/Components/ClientConfig.php index 5d528e9..8d68cba 100644 --- a/src/Components/ClientConfig.php +++ b/src/Components/ClientConfig.php @@ -10,24 +10,75 @@ */ class ClientConfig { - private $scheme; - private $host; - private $user; - private $password; - private $port; - - private $timeout = WscCommonsContract::DEFAULT_TIMEOUT; - private $headers = []; - private $fragmentSize = WscCommonsContract::DEFAULT_FRAGMENT_SIZE; + /** + * @var string + */ + private string $scheme; + + /** + * @var string + */ + private string $host; + + /** + * @var string + */ + private string $user; + + /** + * @var string + */ + private string $password; + + /** + * @var string + */ + private string $port; + + /** + * @var int + */ + private int $timeout = WscCommonsContract::DEFAULT_TIMEOUT; + + /** + * @var array + */ + private array $headers = []; + + /** + * @var int + */ + private int $fragmentSize = WscCommonsContract::DEFAULT_FRAGMENT_SIZE; + + /** + * @var null|resource + */ private $context; - // proxy settings - private $hasProxy = false; - private $proxyIp; - private $proxyPort; - private $proxyAuth; + /** + * @var bool + */ + private bool $hasProxy = false; - private $contextOptions = []; + /** + * @var string + */ + private string $proxyIp; + + /** + * @var string + */ + private string $proxyPort; + + /** + * @var string|null + */ + private ?string $proxyAuth; + + /** + * @var array + */ + private array $contextOptions = []; /** * @return int @@ -110,10 +161,10 @@ public function getScheme(): string } /** - * @param void $scheme + * @param string $scheme * @return ClientConfig */ - public function setScheme($scheme): ClientConfig + public function setScheme(string $scheme): ClientConfig { $this->scheme = $scheme; return $this; @@ -128,10 +179,10 @@ public function getHost(): string } /** - * @param void $host + * @param string $host * @return ClientConfig */ - public function setHost($host): ClientConfig + public function setHost(string $host): ClientConfig { $this->host = $host; return $this; @@ -165,6 +216,7 @@ public function getPassword(): string /** * @param array $urlParts + * @return ClientConfig */ public function setPassword(array $urlParts): ClientConfig { @@ -182,6 +234,7 @@ public function getPort(): string /** * @param array $urlParts + * @return ClientConfig */ public function setPort(array $urlParts): ClientConfig { @@ -199,7 +252,7 @@ public function getContextOptions(): array /** * @param array $contextOptions - * @return ServerConfig + * @return ClientConfig */ public function setContextOptions(array $contextOptions): ClientConfig { @@ -210,6 +263,7 @@ public function setContextOptions(array $contextOptions): ClientConfig /** * @param string $ip * @param string $port + * @return ClientConfig */ public function setProxy(string $ip, string $port): ClientConfig { diff --git a/src/Components/Connection.php b/src/Components/Connection.php index 67d9ac7..5ddedfc 100755 --- a/src/Components/Connection.php +++ b/src/Components/Connection.php @@ -6,10 +6,21 @@ use WSSC\Contracts\ConnectionContract; use WSSC\Contracts\WebSocketServerContract; +/** + * Class Connection + * @package WSSC\Components + */ class Connection implements ConnectionContract, CommonsContract { + /** + * @var false|resource + */ private $socketConnection; - private $clients; + + /** + * @var array + */ + private array $clients; /** * Connection constructor. @@ -169,7 +180,7 @@ private function getOpType(string $type): array * @return string * @throws \Exception */ - private function getComposedFrame(array $frameHead, string $payload, int $payloadLength, bool $masked) + private function getComposedFrame(array $frameHead, string $payload, int $payloadLength, bool $masked): string { // convert frame-head to string: foreach (array_keys($frameHead) as $i) { diff --git a/src/Components/OriginComponent.php b/src/Components/OriginComponent.php index 20b0207..60ae46c 100644 --- a/src/Components/OriginComponent.php +++ b/src/Components/OriginComponent.php @@ -2,18 +2,27 @@ namespace WSSC\Components; -use PHPUnit\Framework\OutputError; -use WSSC\Exceptions\ConnectionException; - +/** + * Class OriginComponent + * @package WSSC\Components + */ class OriginComponent { + /** + * @var false|resource + */ private $client; - private $config; + + + /** + * @var ServerConfig + */ + private ServerConfig $config; /** * OriginComponent constructor. * @param ServerConfig $config - * @param $client + * @param false|resource $client */ public function __construct(ServerConfig $config, $client) { @@ -25,6 +34,7 @@ public function __construct(ServerConfig $config, $client) * Checks if there is a compatible origin header came from client * @param string $headers * @return bool + * @throws \Exception */ public function checkOrigin(string $headers): bool { @@ -32,14 +42,15 @@ public function checkOrigin(string $headers): bool if (empty($matches[1])) { $this->sendAndClose('No Origin header found.'); return false; - } else { - $originHost = $matches[1]; - $allowedOrigins = $this->config->getOrigins(); - if (in_array($originHost, $allowedOrigins, true) === false) { - $this->sendAndClose('Host ' . $originHost . ' is not allowed to pass access control as origin.'); - return false; - } } + + $originHost = $matches[1]; + $allowedOrigins = $this->config->getOrigins(); + if (in_array($originHost, $allowedOrigins, true) === false) { + $this->sendAndClose('Host ' . $originHost . ' is not allowed to pass access control as origin.'); + return false; + } + return true; } @@ -47,7 +58,7 @@ public function checkOrigin(string $headers): bool * @param string $msg * @throws \Exception */ - private function sendAndClose(string $msg) + private function sendAndClose(string $msg): void { $conn = new Connection($this->client); $conn->send($msg); diff --git a/src/Components/ServerConfig.php b/src/Components/ServerConfig.php index fe2de4b..544b48a 100644 --- a/src/Components/ServerConfig.php +++ b/src/Components/ServerConfig.php @@ -9,73 +9,73 @@ class ServerConfig /** * @var int */ - private $clientsPerFork = WebSocketServerContract::CLIENTS_PER_FORK; + private int $clientsPerFork = WebSocketServerContract::CLIENTS_PER_FORK; /** * @var int */ - private $streamSelectTimeout = WebSocketServerContract::STREAM_SELECT_TIMEOUT; + private int $streamSelectTimeout = WebSocketServerContract::STREAM_SELECT_TIMEOUT; /** * @var string */ - private $host = WebSocketServerContract::DEFAULT_HOST; + private string $host = WebSocketServerContract::DEFAULT_HOST; /** * @var int */ - private $port = WebSocketServerContract::DEFAULT_PORT; + private int $port = WebSocketServerContract::DEFAULT_PORT; /** * @var bool */ - private $isForking = true; + private bool $isForking = true; /** * @var string */ - private $processName = WebSocketServerContract::PROC_TITLE; + private string $processName = WebSocketServerContract::PROC_TITLE; /** * @var bool */ - private $checkOrigin = false; + private bool $checkOrigin = false; /** * @var array */ - private $origins = []; + private array $origins = []; /** * @var bool */ - private $originHeader = false; + private bool $originHeader = false; /** * @var bool */ - private $isSsl = false; + private bool $isSsl = false; /** * @var string */ - private $localCert; + private string $localCert; /** * @var string */ - private $localPk; + private string $localPk; /** * @var bool */ - private $allowSelfSigned; + private bool $allowSelfSigned; /** * @var int */ - private $cryptoType; + private int $cryptoType; /** * @return mixed @@ -159,6 +159,7 @@ public function isForking(): bool /** * @param bool $isForking + * @return ServerConfig */ public function setForking(bool $isForking): ServerConfig { diff --git a/src/Components/WSClientTrait.php b/src/Components/WSClientTrait.php index 68f247a..9edcd59 100644 --- a/src/Components/WSClientTrait.php +++ b/src/Components/WSClientTrait.php @@ -119,7 +119,7 @@ private function getPayloadData(string $data, int $payloadLength): string * @throws ConnectionException * @throws \Exception */ - protected function receiveFragment() + protected function receiveFragment(): ?string { // Just read the main fragment information first. $data = $this->read(2); diff --git a/src/Components/WscMain.php b/src/Components/WscMain.php index 5e776ef..9c94e6c 100755 --- a/src/Components/WscMain.php +++ b/src/Components/WscMain.php @@ -19,33 +19,56 @@ class WscMain implements WscCommonsContract { use WSClientTrait; + /** + * @var resource|bool + */ private $socket; - private $isConnected = false; - private $isClosing = false; - private $lastOpcode; + + /** + * @var bool + */ + private bool $isConnected = false; + + /** + * @var bool + */ + private bool $isClosing = false; + + /** + * @var string + */ + private string $lastOpcode; + + /** + * @var float|int + */ private $closeStatus; - private $hugePayload; - private static $opcodes = [ + /** + * @var string|null + */ + private ?string $hugePayload; + + private static array $opcodes = [ CommonsContract::EVENT_TYPE_CONTINUATION => 0, - CommonsContract::EVENT_TYPE_TEXT => 1, - CommonsContract::EVENT_TYPE_BINARY => 2, - CommonsContract::EVENT_TYPE_CLOSE => 8, - CommonsContract::EVENT_TYPE_PING => 9, - CommonsContract::EVENT_TYPE_PONG => 10, + CommonsContract::EVENT_TYPE_TEXT => 1, + CommonsContract::EVENT_TYPE_BINARY => 2, + CommonsContract::EVENT_TYPE_CLOSE => 8, + CommonsContract::EVENT_TYPE_PING => 9, + CommonsContract::EVENT_TYPE_PONG => 10, ]; - protected $socketUrl = ''; - protected $config; + protected string $socketUrl = ''; + protected ClientConfig $config; /** - * @throws \InvalidArgumentException + * @param ClientConfig $config * @throws BadUriException * @throws ConnectionException - * @throws \Exception */ - protected function connect() + protected function connect(ClientConfig $config): void { + $this->config = $config; $urlParts = parse_url($this->socketUrl); $this->config->setScheme($urlParts['scheme']); @@ -261,7 +284,7 @@ public function setTimeout(int $timeout, $microSecs = null): WscMain * @throws ConnectionException * @throws \Exception */ - public function send($payload, $opcode = CommonsContract::EVENT_TYPE_TEXT) + public function send($payload, $opcode = CommonsContract::EVENT_TYPE_TEXT): void { if (!$this->isConnected) { $this->connect(); @@ -305,7 +328,7 @@ public function send($payload, $opcode = CommonsContract::EVENT_TYPE_TEXT) * @throws ConnectionException * @throws \Exception */ - public function receive() + public function receive(): ?string { if (!$this->isConnected) { $this->connect(); @@ -351,7 +374,7 @@ public function close(int $status = 1000, string $message = 'ttfn') * @param $data * @throws ConnectionException */ - protected function write(string $data) + protected function write(string $data): void { $written = fwrite($this->socket, $data); diff --git a/src/Components/WssMain.php b/src/Components/WssMain.php index 095900c..2196880 100644 --- a/src/Components/WssMain.php +++ b/src/Components/WssMain.php @@ -16,7 +16,7 @@ */ class WssMain implements CommonsContract { - private $isPcntlLoaded = false; + private bool $isPcntlLoaded = false; /** * Message frames decoder diff --git a/src/WebSocketClient.php b/src/WebSocketClient.php index 14051e0..2ac41a8 100755 --- a/src/WebSocketClient.php +++ b/src/WebSocketClient.php @@ -20,8 +20,6 @@ class WebSocketClient extends WscMain public function __construct(string $url, ClientConfig $config) { $this->socketUrl = $url; - $this->config = $config; - - $this->connect(); + $this->connect($config); } } diff --git a/src/WebSocketServer.php b/src/WebSocketServer.php index e47050f..14ec085 100755 --- a/src/WebSocketServer.php +++ b/src/WebSocketServer.php @@ -13,30 +13,54 @@ use WSSC\Exceptions\WebSocketException; /** - * Create by Arthur Kushman - * - * @property ServerConfig config - * @property WebSocket handler + * Class WebSocketServer + * @package WSSC */ class WebSocketServer extends WssMain implements WebSocketServerContract { - protected $config; + private const MAX_BYTES_READ = 8192; + private const HEADER_BYTES_READ = 1024; - private $clients = []; - // set any template You need ex.: GET /subscription/messenger/token - private $pathParams = []; - private $handshakes = []; - private $headersUpgrade = []; - private $totalClients = 0; - private $maxClients = 1; - private $handler; - private $cureentConn; + /** + * @var ServerConfig + */ + protected ServerConfig $config; - // for the very 1st time must be true - private $stepRecursion = true; + /** + * @var array + */ + private array $clients = []; - private const MAX_BYTES_READ = 8192; - private const HEADER_BYTES_READ = 1024; + /** + * set any template You need ex.: GET /subscription/messenger/token + * @var array + */ + private array $pathParams = []; + + /** + * @var array + */ + private array $handshakes = []; + + /** + * @var array + */ + private array $headersUpgrade = []; + + /** + * @var int + */ + private int $maxClients = 1; + + /** + * @var WebSocket + */ + private WebSocket $handler; + + /** + * @var bool + */ + private bool $stepRecursion = true; /** * WebSocketServer constructor. @@ -62,7 +86,7 @@ public function __construct( * @throws WebSocketException * @throws ConnectionException */ - public function run() + public function run(): void { $context = stream_context_create(); $errno = null; @@ -104,7 +128,7 @@ public function run() * @throws WebSocketException * @throws ConnectionException */ - private function eventLoop($server, bool $fork = false) + private function eventLoop($server, bool $fork = false): void { if ($fork === true && $this->isPcntlLoaded()) { $pid = pcntl_fork(); @@ -126,23 +150,23 @@ private function eventLoop($server, bool $fork = false) private function looping($server): void { while (true) { - $this->totalClients = count($this->clients) + 1; + $totalClients = count($this->clients) + 1; // maxClients prevents process fork on count down - if ($this->totalClients > $this->maxClients) { - $this->maxClients = $this->totalClients; + if ($totalClients > $this->maxClients) { + $this->maxClients = $totalClients; } $doFork = $this->config->isForking() === true - && $this->totalClients !== 0 // avoid 0 process creation + && $totalClients !== 0 // avoid 0 process creation && $this->stepRecursion === true // only once - && $this->maxClients === $this->totalClients // only if stack grows - && $this->totalClients % $this->config->getClientsPerFork() === 0; // only when N is there + && $this->maxClients === $totalClients // only if stack grows + && $totalClients % $this->config->getClientsPerFork() === 0; // only when N is there if ($doFork) { $this->stepRecursion = false; $this->eventLoop($server, true); } - $this->lessConnThanProc($this->totalClients, $this->maxClients); + $this->lessConnThanProc($totalClients, $this->maxClients); //prepare readable sockets $readSocks = $this->clients; @@ -225,11 +249,11 @@ private function messagesWorker(array $readSocks): void $dataPayload = $data['payload']; // to manipulate connection through send/close methods via handler, specified in IConnection - $this->cureentConn = new Connection($sock, $this->clients); + $cureentConn = new Connection($sock, $this->clients); if (empty($data) || $dataType === self::EVENT_TYPE_CLOSE) { // close event triggered from client - browser tab or close socket event // trigger CLOSE event try { - $this->handler->onClose($this->cureentConn); + $this->handler->onClose($cureentConn); } catch (WebSocketException $e) { $e->printStack(); } @@ -244,7 +268,7 @@ private function messagesWorker(array $readSocks): void if ($isSupportedMethod) { try { // dynamic call: onMessage, onPing, onPong - $this->handler->{self::MAP_EVENT_TYPE_TO_METHODS[$dataType]}($this->cureentConn, $dataPayload); + $this->handler->{self::MAP_EVENT_TYPE_TO_METHODS[$dataType]}($cureentConn, $dataPayload); } catch (WebSocketException $e) { $e->printStack(); } @@ -287,7 +311,7 @@ private function handshake($client, string $headers): string * * @param string $secWebSocketAccept base64 encoded Sec-WebSocket-Accept header */ - private function setHeadersUpgrade($secWebSocketAccept) + private function setHeadersUpgrade(string $secWebSocketAccept): void { $this->headersUpgrade = [ self::HEADERS_UPGRADE_KEY => self::HEADERS_UPGRADE_VALUE,