Index

Websocket

MMO\core\server · 1 symbol · generated 30 Sep 2026

class

Websocket

Websocket.php:22

A hand-rolled RFC 6455 WebSocket server: a single instance listens for one TCP connection (listen), performs the HTTP-Upgrade handshake, then exchanges framed messages over it (readMessage/write).

class MMO\core\server\Websocket implements Psr\Http\Message\StreamInterface, MMO\core\interfaces\iRenderable
Namespace
MMO\core\server
Implements
StreamInterface, iRenderable
$ws = new Websocket('localhost', 49666, false); $ws->connect(); $ws->listen(); // blocks until one client connects and completes the handshake $msg = $ws->readMessage(); // blocks until a full message arrives $ws->write("you said: $msg"); $ws->close();

Index

MODE_BINARY
MODE_RAW
MODE_TEXT
OP_BINARY
OP_CLOSE
OP_CONTINUATION
OP_PING
OP_PONG
OP_TEXT
WEBSOCKET_KEYThe magic GUID RFC 6455 requires appending to the client's Sec-WebSocket-Key before hashing it into the handshake's Sec-WebSocket-Accept.
address
closing
port
secure
socket
stream
timeout
close($message, $status)StreamInterface::close Send a close frame (OP_CLOSE) and wait for the peer's acknowledgment, then release the client stream ($stream) -- always, whether the handshake actually completed o…
connect()Open the listening server socket ($socket) -- does NOT accept a connection yet (listen for that).
debugName()
detach()StreamInterface::detach NOTE: unlike close, does not fclose the resource -- the caller takes ownership and must close it themselves.
disconnect()Close the client connection (close) and the listening server socket (connect).
eof()StreamInterface::eof
getContents()StreamInterface::getContents NOTE: reads raw, unframed bytes from the underlying socket -- not a WebSocket message (readMessage for that).
getMetadata($key)StreamInterface::getMetadata
getResource()
getSize()StreamInterface::getSize
getTimeout()
getType()
getUri()
hasMessage()Test if stream has awaiting command before calling stream->read.
isConnected()
isListening()
isReadable()StreamInterface::isReadable
isSeekable()StreamInterface::isSeekable
isWritable()StreamInterface::isWritable
listen()Wait for one client to connect (up to setTimeout, or indefinitely) and complete the WebSocket handshake (parses the HTTP Upgrade request's headers, verifies Sec-WebSocket-Key, and replies with…
ping($message)Send a ping frame (OP_PING); the peer is expected to reply with a pong.
read($length)StreamInterface::read NOTE: reads raw, unframed bytes from the underlying socket -- not a WebSocket frame/message (readFrame/
readFrame()Read and parse one raw WebSocket frame (RFC 6455 section 5.2) from the stream, unmasking its payload if masked.
readLine($length, $ending)Read line from stream.
readMessage($code)Read one full WebSocket message, transparently reassembling continuation frames (OP_CONTINUATION) and auto-handling control frames along the way: a ping (OP_PING) gets an automatic pong r…
render($opts)Get content of socket
rewind()StreamInterface::rewind
run()Run the websocket main loop.
seek($offset, $whence)StreamInterface::seek
setTimeout($timeout)Set timeout.
Setup($url, $mainFile, $xdebugPort) staticSetup the websocket for a html exchange.
tell()StreamInterface::tell
Url($address, $port, $secure) staticGenerate a valid Javascript url for the websocket
write($data, $mode)StreamInterface::write A non-string $data is JSON-encoded first (jsonEncode).
writeFrame($code, $payload, $final, $masked)Write one raw WebSocket frame (RFC 6455 section 5.2): header (opcode/final bit/payload length) plus the (optionally masked) payload.
writeMessage($code, $content, $masked, $framesize)Write a full WebSocket message, splitting it into multiple frames (writeFrame) if it exceeds $framesize.
__construct($address, $port, $secure)constructor
__destruct()Destructor. Closes the stream
__toString()StreamInterface::__toString

Constants10

MODE_BINARY

const MODE_BINARY = 2
See
write's $mode: frame $data as a binary message (OP_BINARY).

MODE_RAW

const MODE_RAW = 0
See
write's $mode: write raw bytes with no WebSocket framing (used internally for the handshake and frame bytes themselves).

MODE_TEXT

const MODE_TEXT = 1
See
write's $mode: frame $data as a text message (OP_TEXT).

WEBSOCKET_KEY

The magic GUID RFC 6455 requires appending to the client's Sec-WebSocket-Key before hashing it into the handshake's Sec-WebSocket-Accept.

const WEBSOCKET_KEY = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

Properties7

address

protected $address = null

closing

protected bool $closing = false
Type
bool

port

protected $port = null

secure

protected $secure = null

socket

protected $socket = null

stream

protected $stream = null

timeout

protected $timeout = null

Methods39

close($message, $status)

Websocket.php:366

StreamInterface::close Send a close frame (OP_CLOSE) and wait for the peer's acknowledgment, then release the client stream ($stream) -- always, whether the handshake actually completed or the peer was already gone (any failure along the way is logged, not thrown -- disconnect/__destruct rely on this never throwing).

function close(string $message = '', int $status = 1000): void
$message = ''stringa human-readable close reason, sent to the peer.
$status = 1000inta WebSocket close status code (e.g. 1000 = normal closure, 1001 = going away).
returnsvoid

connect()

Websocket.php:230

Open the listening server socket ($socket) -- does NOT accept a connection yet (listen for that).

function connect(): void
returnsvoid
throwsif already connected, or the socket couldn't be created (e.g. the port is already in use).

detach()

Websocket.php:408

StreamInterface::detach NOTE: unlike close, does not fclose the resource -- the caller takes ownership and must close it themselves.

function detach(): mixed
returnsmixedthe client stream (now detached from this Websocket), or null if none was connected.

eof()

Websocket.php:455

StreamInterface::eof

function eof(): bool
returnsbooltrue if no client is connected, or the client stream has reached EOF.

getContents()

Websocket.php:479

StreamInterface::getContents NOTE: reads raw, unframed bytes from the underlying socket -- not a WebSocket message (readMessage for that).

function getContents(): string
returnsstring
throwsif no client is connected, or reading fails.

getResource()

Websocket.php:194
function getResource()
returnsthe accepted client stream (listen), or null before a client has connected.

getSize()

Websocket.php:420

StreamInterface::getSize

function getSize(): ?int
returnsnull | intnull if no client is connected, or the size can't be determined.

hasMessage()

Websocket.php:302

Test if stream has awaiting command before calling stream->read.

function hasMessage(): boolNOTE: use this when inside a long command. As stream->read is blocking, call this before reading a command.
returnsbool

isReadable()

Websocket.php:431

StreamInterface::isReadable

function isReadable(): bool
returnsboolalways true.

isSeekable()

Websocket.php:447

StreamInterface::isSeekable

function isSeekable(): bool
returnsboolalways false -- a socket cannot be seeked.

isWritable()

Websocket.php:439

StreamInterface::isWritable

function isWritable(): bool
returnsboolalways true.

listen()

Websocket.php:246

Wait for one client to connect (up to setTimeout, or indefinitely) and complete the WebSocket handshake (parses the HTTP Upgrade request's headers, verifies Sec-WebSocket-Key, and replies with 101 Switching Protocols).

function listen(): void
returnsvoid
throwsif connect wasn't called first, or the handshake's headers are invalid.

ping($message)

Websocket.php:593

Send a ping frame (OP_PING); the peer is expected to reply with a pong. @return int bytes written.

function ping(string $message = ''): int
$message = ''string
returnsint

read($length)

Websocket.php:535

StreamInterface::read NOTE: reads raw, unframed bytes from the underlying socket -- not a WebSocket frame/message (readFrame/

function read(int $length): string
See
readMessage for that). Any read failure (timeout, empty read, short read) closes the connection (close, with an appropriate WebSocket status code) before throwing.
$lengthintexact number of bytes to read.
returnsstringexactly $length bytes.
throwsif $length is negative.
throwsif the read times out (setTimeout).
throwsif no client is connected, the peer closed the connection, or fewer than $length bytes were read.

readFrame()

Websocket.php:739

Read and parse one raw WebSocket frame (RFC 6455 section 5.2) from the stream, unmasking its payload if masked.

protected function readFrame(): array
returnsarray[opcode, payload, final, masked].

readLine($length, $ending)

Websocket.php:513

Read line from stream.

function readLine(int $length, string $ending = ''): string
$lengthintMaximum number of bytes to read
$ending = ''stringLine delimiter
returnsstringRead data

readMessage($code)

Websocket.php:633

Read one full WebSocket message, transparently reassembling continuation frames (OP_CONTINUATION) and auto-handling control frames along the way: a ping (OP_PING) gets an automatic pong reply, and a close frame (OP_CLOSE) is acknowledged (unless this side already initiated the close -- $closing) and ends the connection (closes and nulls $stream directly, without going through close).

function readMessage(?int &$code = null): string
$code = nullnull | int
returnsstringthe reassembled message payload.
throwspropagated from read/readFrame on a connection failure.

rewind()

Websocket.php:494

StreamInterface::rewind

function rewind(): void
returnsvoid
throwsalways -- a socket cannot be seeked.

run()

Websocket.php:170

Run the websocket main loop. It yields the command that it receives.

function run(): GeneratorA read timeout ([`setTimeout`](https://mmoframework.fr/documentation/mmo.core/php/server/Websocket.md#websocket-settimeout)) just skips to the next iteration rather than ending the loop; any other error is logged ([`Log::FromException`](https://mmoframework.fr/documentation/mmo.core/php/events/Log.md#log-fromexception)) and also doesn't end the loop -- only [`isListening`](https://mmoframework.fr/documentation/mmo.core/php/server/Websocket.md#websocket-islistening) turning false (e.g. after the client disconnects) does. WARNING: only use this function with a call to Websocket::Setup()
returnsGenerator

seek($offset, $whence)

Websocket.php:502

StreamInterface::seek

function seek($offset, $whence = SEEK_SET): void
$offset
$whence = SEEK_SET
returnsvoid
throwsalways -- a socket cannot be seeked.

setTimeout($timeout)

Websocket.php:216

Set timeout.

function setTimeout(?float $timeout): void
$timeoutnull | floatTimeout in seconds to wait for a client connection (listen); null waits indefinitely.
returnsvoid

Setup($url, $mainFile, $xdebugPort)

staticWebsocket.php:128

Setup the websocket for a html exchange.

static function Setup(string $url, string|int $mainFile = 1, string|bool $xdebugPort = false): ?selfIt returns a websocket on the command line. If not on the command line, it will execute the mainFile script and returns null. WARNING: the mainFile MUST be able to be executed as standalone. It cannot be a file executed by `index.php` or an `.htaccess` redirection.
$urlstring
$mainFile = 1string | intthe main file to execute when not on command line. if an int, it will look back at the $mainFile trace to find which file need to be executed on the command line
$xdebugPort = falsestring | boolif true or a port, will start the script with xdebug (default port for xdebug is 9003)
returnsnull | self
```php $url = \MMO\core\server\Websocket::Url(); if ($websocket = \MMO\core\server\Websocket::Setup($url)) { //the following code is only executed on the command line foreach ($websocket->run() as $cmd) { ... } } //the following code is only executed by apache <script> let socket = "&lt;? $url ?&gt;"; socket.send('hello'); </script> ```

tell()

Websocket.php:463

StreamInterface::tell

function tell(): int
returnsint
throwsif no client is connected, or the position can't be determined.

Url($address, $port, $secure)

staticWebsocket.php:92

Generate a valid Javascript url for the websocket

static function Url(string $address = 'localhost', int $port = 49666, bool $secure = false): string
$address = 'localhost'stringthe host (ex: localhost)
$port = 49666intthe port
$secure = falseboolif the connection is secured
returnsstring

write($data, $mode)

Websocket.php:571

StreamInterface::write A non-string $data is JSON-encoded first (jsonEncode).

function write(JsonSerializable|array|string $data, int $mode = self::MODE_TEXT): int
$dataJsonSerializable | array | string
$mode = self::MODE_TEXTintMODE_TEXT/MODE_BINARY (framed as a WebSocket message) or MODE_RAW (raw bytes, no framing).
returnsintthe number of bytes written.
throwsif no client is connected, or the raw write fails/is incomplete (MODE_RAW).
throwsif $mode isn't one of the MODE_* constants.

writeFrame($code, $payload, $final, $masked)

Websocket.php:697

Write one raw WebSocket frame (RFC 6455 section 5.2): header (opcode/final bit/payload length) plus the (optionally masked) payload.

protected function writeFrame(int $code, string $payload, bool $final = false, bool $masked = true): int
$codeintthe opcode (OP_CONTINUATION/OP_TEXT/...).
$payloadstringthis frame's chunk of the message.
$final = falseboolwhether this is the last frame of the message.
$masked = trueboolwhether to XOR-mask the payload with a random 4-byte key (as RFC 6455 requires from a client).
returnsintbytes written (verified against a real RFC-6455 frame parser, including the 16-bit extended-length form -- writeMessage's default 4096-byte $framesize keeps individual frames under the 64-bit form's 65535-byte threshold, so that path is exercised only with a $framesize above it).

writeMessage($code, $content, $masked, $framesize)

Websocket.php:609

Write a full WebSocket message, splitting it into multiple frames (writeFrame) if it exceeds $framesize.

function writeMessage(int $code, string $content, bool $masked = true, int $framesize = 4096): int
$codeintan opcode (OP_TEXT/OP_BINARY/OP_CLOSE/OP_PING/OP_PONG).
$contentstringthe message payload.
$masked = trueboolwhether to mask the frame(s) (per RFC 6455, a server should send unmasked frames to a client, but this class defaults to true regardless of which side it's acting as -- masking a server frame is harmless to a spec-compliant client, which unmasks either way).
$framesize = 4096intmaximum payload bytes per frame; larger content is split across continuation frames (OP_CONTINUATION).
returnsinttotal bytes written across all frames (verified end-to-end with a 10000-byte message split across multiple frames).

__construct($address, $port, $secure)

Websocket.php:71

constructor

function __construct(string $address, int $port = 49666, bool $secure = true)
$addressstringcan be the direct host (ex: localhost) or an entire url of the form ws://localhost:4445
$port = 49666intmake sure you use port between 49152-65534 for your connection
$secure = trueboolif true, use tls