<?php namespace App\Classes\Sock; use Illuminate\Support\Facades\Log; final class SocketServer { private const LOGKEY = 'SS-'; private $server; // Our server resource private string $bind; // The IP address to bind to private int $port; // The Port to bind to private int $backlog = 5; // Number of incoming connections queued private array $handler; // The class and method that will handle our connection public function __construct(int $port,string $bind='0.0.0.0') { $this->bind = $bind; $this->port = $port; $this->_init(); } /** * Bind to our Socket * * @throws SocketException */ private function _bindSocket(): void { if (socket_bind($this->server,$this->bind,$this->port) === FALSE) throw new SocketException(SocketException::CANT_BIND_SOCKET,socket_strerror(socket_last_error($this->server))); } /** * Create our Socket * * @throws SocketException */ private function _createSocket(): void { /** * Check dependencies */ if (! extension_loaded('sockets')) throw new SocketException(SocketException::CANT_ACCEPT,'Missing sockets extension'); if (! extension_loaded('pcntl')) throw new SocketException(SocketException::CANT_ACCEPT,'Missing pcntl extension'); $this->server = socket_create(AF_INET,SOCK_STREAM,SOL_TCP); if ($this->server === FALSE) throw new SocketException(SocketException::CANT_CREATE_SOCKET,socket_strerror(socket_last_error())); socket_set_option($this->server,SOL_SOCKET,SO_REUSEADDR,1); } /** * Setup Socket and Bind * * @throws SocketException */ private function _init(): void { $this->_createSocket(); $this->_bindSocket(); } /** * Our main loop where we listen for connections * * @throws SocketException */ public function listen() { if (! $this->handler) throw new SocketException(SocketException::CANT_LISTEN,'Handler not set.'); if (socket_listen($this->server,$this->backlog) === FALSE) throw new SocketException(SocketException::CANT_LISTEN,socket_strerror(socket_last_error($this->server))); Log::info(sprintf('%s:- Listening on [%s:%d]',self::LOGKEY,$this->bind,$this->port)); $this->loop(); socket_close($this->server); Log::info(sprintf('%s:= Closed [%s:%d]',self::LOGKEY,$this->bind,$this->port)); } /** * Manage and execute incoming connections * * @throws SocketException */ private function loop() { while (TRUE) { if (($accept = socket_accept($this->server)) === FALSE) throw new SocketException(SocketException::CANT_ACCEPT,socket_strerror(socket_last_error($this->server))); $this->handler[0]->{$this->handler[1]}(new SocketClient($accept)); } } /** * Set our connection handler Class and Method * * @param array $handler */ public function setConnectionHandler(array $handler): void { $this->handler = $handler; } }