null, 'key' => null, 'passphrase' => null, ]; protected array $requestHandlers = []; /** * @param array $certificate * @param string $hostname */ public function __construct( array $certificate, string $hostname = 'localhost' ) { $this->certificate = $certificate; $this->hostname = $hostname; } public function setCertificate(string $certificateFile, string $keyFile, string $passphrase = ''): static { $this->certificate = [ 'file' => $certificateFile, 'key' => $keyFile, 'passphrase' => $passphrase, ]; return $this; } public function onRequest(RequestHandlerInterface|callable $callable): static { $this->requestHandlers[] = $callable; return $this; } public function listen(int $port = 1965): void { $context = stream_context_create(options: [ 'ssl' => [ 'local_cert' => $this->certificate['file'], 'local_pk' => $this->certificate['key'], 'passphrase' => $this->certificate['passphrase'], 'allow_self_signed' => true, 'verify_peer' => false, ], ]); $socket = stream_socket_server( address: "tls://{$this->hostname}:{$port}", context: $context ); $connections = []; while (true) { $connection = stream_socket_accept( socket: $socket, timeout: empty($connections) ? -1 : 0, peer_name: $peer ); if ($connection) { $connections[$peer] = $connection; } if (count($connections) == 0) { continue; } $streams = stream_select( read: $connections, write: $write, except: $except, seconds: 5 ); if ($streams) { foreach ($connections as $peer => $connection) { if (feof($connection)) { fclose($connection); unset($connections[$peer]); continue; } $request = Request::fromResource($connection); $response = new Response(); foreach ($this->requestHandlers as $requestHandler) { $response = $requestHandler($response, $request); } $response->send($connection); fclose($connection); unset($connections[$peer]); } } } } }