Menu

Reference / Http\WebSocket

Handler

interface
interface Handler

Event callbacks for a connection, as an alternative to a foreach loop.

Implement it and pass it to handler() (server, as the session of upgrade()) or run() (client). The calls arrive in this order: onOpen once, onMessage per message, onClose once, whichever way the connection ends.

Example

// Callback style: a Handler on each side, and a Hub that fans a message out.

use function Async\async;
use function Async\spawn;
use Http\WebSocket as WS;

final class Greeter implements WS\Handler
{
    public function __construct(private WS\Hub $hub) {}

    public function onOpen(WS\Connection $c): void
    {
        $this->hub->add($c);
        $c->send('welcome');
    }

    public function onMessage(WS\Connection $c, WS\Message $m): void
    {
        $n = $this->hub->broadcast('said: ' . $m->data);
        echo "server: broadcast to $n\n";
    }

    public function onClose(WS\Connection $c, int $code, string $reason): void
    {
        $this->hub->remove($c);
        echo "server: closed with $code '$reason', hub has ", $this->hub->count(), "\n";
    }

    public function onError(WS\Connection $c, \Throwable $e): void
    {
        echo "server: error ", $e->getMessage(), "\n";
    }
}

$l = stream_socket_server('tcp://127.0.0.1:0');
stream_set_blocking($l, false);
$name = stream_socket_get_name($l, false);
$port = (int)substr($name, strrpos($name, ':') + 1);
$server = \Http\Server::onListener($l)->acceptWait(0.02);
$hub = new WS\Hub();

async(function () use ($server, $port, $hub) {
    spawn(function () use ($server, $hub) {
        $server->serve(function (\Http\Request $req) use ($hub): \Http\Response {
            return WS\upgrade($req, WS\handler(new Greeter($hub)));
        });
    });

    $ws = WS\connect("ws://127.0.0.1:$port/");
    $m = $ws->receive();
    echo 'client: ', $m->data, "\n";
    $ws->send('hi');
    $m = $ws->receive();
    echo 'client: ', $m->data, "\n";
    $ws->close(4000, 'bye');
    \Async\delay(0.05);
    echo 'client: peer close code ', $ws->closeCode(), "\n";
    $server->stop();
});

Output

client: welcome
server: broadcast to 1
client: said: hi
server: closed with 4000 'bye', hub has 0
client: peer close code 4000

See also: handler, run

Methods

onOpen()The connection is ready; called before the first message is read.
onMessage()A complete message arrived.
onClose()The connection ended; called exactly once, after any onError.
onError()onOpen or onMessage threw; the connection is then closed with 1011.

onOpen()#source

function onOpen(Connection $c): void

The connection is ready; called before the first message is read.

Parameters

$c Connection
the connection

onMessage()#source

function onMessage(Connection $c, Message $m): void

A complete message arrived.

Parameters

$c Connection
the connection
$m Message
the message

onClose()#source

function onClose(Connection $c, int $code, string $reason): void

The connection ended; called exactly once, after any onError.

Parameters

$c Connection
the connection
$code int
the peer's close code (see Connection::closeCode(); 1006 if no Close arrived)
$reason string
the peer's close reason, possibly empty

onError()#source

function onError(Connection $c, \Throwable $e): void

onOpen or onMessage threw; the connection is then closed with 1011. An exception thrown by onError itself is discarded.

Parameters

$c Connection
the connection
$e \Throwable
the exception

View source