WS ext-websocket

PHP extension manual

Native WebSocket runtime for PHP

ext-websocket keeps RFC 6455 protocol work in C and exposes a small PHP API for callback-based WebSocket servers, synchronous PHP code, and protocol helpers that can be reused by higher-level runtimes.

Native core HTTP Upgrade, frame parsing, masking, fragmentation, ping/pong, close frames.
PHP 8.1+ Small public API with typed value objects and IDE/static-analysis stubs.
Event-driven runtime Non-blocking sockets with epoll, kqueue, poll, and select drivers.

Installation

Install the extension with PIE:

pie install axcherednikov/php-websocket

Or build from source:

phpize
./configure --enable-websocket
make
make test
sudo make install

Enable the extension in PHP:

extension=websocket

Verify that PHP can load it:

php -m | grep websocket

See the project installation notes for repository-specific commands.

Quick Start

A minimal echo server:

<?php

use WebSocket\Connection;
use WebSocket\MessageType;
use WebSocket\Server;
use WebSocket\ServerOptions;

$server = new Server(new ServerOptions(
    maxMessageSize: 1024 * 1024,
));

$server->listen('127.0.0.1', 8080);

$server->onOpen(static function (Connection $connection): void {
    echo "open {$connection->id}\n";
});

$server->onMessage(static function (Connection $connection, string $message): void {
    $connection->send($message, MessageType::Text);
});

$server->onClose(static function (Connection $connection): void {
    echo "close {$connection->id}\n";
});

$server->run();

A fuller runnable example is available in examples/run_server.php.

Runtime Model

The server runtime is asynchronous internally and callback-based externally. The extension owns its event loop when WebSocket\Server::run() is called.

PHP API Server, Connection, callbacks, typed options.
Native runtime accept, upgrade, read/write queues, frame processing, backpressure.
I/O drivers epoll on Linux, kqueue on macOS/BSD, poll/select fallback.

Protocol helpers such as Protocol::encode() and Protocol::decode() are stateless and can be used by AMPHP, Revolt, Workerman, or other runtimes without adopting the built-in server loop.

Protocol Helpers

Encode and decode RFC 6455 frames directly:

<?php

use WebSocket\MessageType;
use WebSocket\Protocol;

$bytes = Protocol::encode('hello', MessageType::Text);
$frame = Protocol::decode($bytes);

echo $frame->payload; // hello

Class Reference

The reference follows the PHP manual style: each class lists its synopsis, methods, parameters, common errors, and examples.

WebSocket\Server

final class WebSocket\Server

Native callback-based WebSocket server. It binds a TCP listener, performs HTTP Upgrade, processes RFC 6455 frames, and dispatches user callbacks.

Constructor

public function __construct(WebSocket\ServerOptions|array $options = [])

OptionDescription
maxMessageSizeMaximum accepted text or binary message size. Defaults to 16 MiB.
maxQueuedBytesMaximum queued outgoing bytes per connection. Defaults to 16 MiB.

Methods

listen(string $host, int $port): void
Bind the TCP listener used by run().
onOpen(Closure $handler): void
Register a callback called after a successful HTTP Upgrade. Returning false closes the accepted connection.
onMessage(Closure $handler): void
Register a callback called for complete text and binary messages.
onClose(Closure $handler): void
Register a callback called when an upgraded connection closes.
onError(Closure $handler): void
Register a callback for runtime errors.
run(): void
Start the accept, HTTP Upgrade, and frame processing loop.
stop(): void
Request graceful shutdown of the running server loop.
getDriver(): string
Return the active or best available I/O driver name.

WebSocket\ServerOptions

final class WebSocket\ServerOptions

Explicit immutable configuration for WebSocket\Server.

Synopsis

final class ServerOptions
{
    public readonly int $maxMessageSize;
    public readonly int $maxQueuedBytes;

    public function __construct(
        int $maxMessageSize = 16 * 1024 * 1024,
        int $maxQueuedBytes = 16 * 1024 * 1024,
    ) {}
}

Errors/Exceptions

Throws ValueError if a limit is less than 1.

WebSocket\Connection

final class WebSocket\Connection

Runtime connection accepted by WebSocket\Server.

Properties

PropertyDescription
readonly string $idRuntime connection identifier.
readonly string $remoteAddressRemote peer address.

Methods

send(string $payload, MessageType $type = MessageType::Text): void
Send a text, binary, ping, pong, or close frame.
close(int $code = 1000, string $reason = ''): void
Send a close frame and close the connection.
isOpen(): bool
Check whether the underlying connection is still open.

WebSocket\Protocol

final class WebSocket\Protocol

Stateless RFC 6455 protocol helpers.

Methods

acceptKey(string $key): string
Build Sec-WebSocket-Accept from Sec-WebSocket-Key.
encode(string $payload, MessageType $type = MessageType::Text, bool $masked = false): string
Encode one complete frame.
decode(string $buffer): Frame|CloseFrame|null
Decode one complete frame. Returns null when the buffer is incomplete.
pack(string|Frame|CloseFrame $data, int $opcode = Protocol::OPCODE_TEXT, int $flags = Protocol::FLAG_FIN): string
Encode a raw WebSocket frame.
unpack(string $buffer): Frame|CloseFrame|null
Decode one raw WebSocket frame.

Frames and Types

ClassDescription
WebSocket\FrameDecoded non-close frame with type, opcode, flags, payload, FIN state, and consumed byte count.
WebSocket\CloseFrameDecoded close frame with status code, reason, flags, and consumed byte count.
WebSocket\MessageTypeEnum cases: Continuation, Text, Binary, Ping, Pong, Close.

Production

Terminate TLS outside the extension with nginx, Caddy, Traefik, HAProxy, or stunnel. Run multiple PHP server processes behind a load balancer or process manager when you need to use multiple CPU cores.

Always set explicit maxMessageSize and maxQueuedBytes values for the workload. These limits protect memory under large messages and slow clients.

Read the full production guide for TLS, process management, limits, backpressure, and deployment notes.

Benchmarks

The benchmark suite covers protocol encode/decode, server accept/upgrade runtime, and real ws:// / wss:// message runtime against AMPHP, Workerman, and OpenSwoole.

The important distinction is layer ownership: Protocol helpers can accelerate existing async/userland runtimes, while Server::run() is a standalone native loop.

Benchmark commands and current comparison notes live in bench/README.md.

IDE Support

Use websocket.stub.php as an IDE/static-analysis stub for PhpStorm, PHPStan, Psalm, or similar tools. It documents callback signatures, immutable options, typed frames, value ranges, and common exceptions.

Download or reference the stub from websocket.stub.php.

Scope

ext-websocket is not a Centrifugo replacement and does not implement Socket.IO. It is a low-level native WebSocket runtime and codec for building custom realtime protocols directly in PHP.

Project Resources

ResourceLink
Source codegithub.com/axcherednikov/php-websocket
Benchmarksbench/README.md
Production guidedocs/production.md
IDE stubwebsocket.stub.php
ChangelogCHANGELOG.md