Runnable HTTP, WebSocket and SSE recipes
These complete examples target the reviewed Server 7 development revision, not Triangle Engine’s Server 4 dependency. The HTTP, WebSocket and SSE scenarios were executed against the source revision linked below on PHP 8.5.4. This is not a supported-version matrix or a released package promise.
The same examples also passed the pinned-source CI check on PHP 8.1, 8.3 and 8.5. This covers these three recipes, not every loop backend, operating system or Server feature. CI evidence.
Prepare a disposable application
Section titled “Prepare a disposable application”mkdir server-recipes && cd server-recipescomposer require 'localzet/server:dev-main#7e7cbdaec299f2e0d677d6770a33ac09db8d92f2'composer check-platform-reqsThe commit reference is for reproducing this documentation check. Review and pin a released version before production. Save each example next to vendor/; run one server per terminal in the foreground, then stop with Ctrl+C. The examples bind to loopback; expose them only behind an explicitly configured TLS proxy.
HTTP JSON
Section titled “HTTP JSON”http.php
<?php
declare(strict_types=1);
use localzet\Server;use localzet\Server\Connection\TcpConnection;use localzet\Server\Protocols\Http\Request;use localzet\Server\Protocols\Http\Response;
require __DIR__ . '/vendor/autoload.php';
$server = new Server('http://127.0.0.1:' . (getenv('PORT') ?: '18080'));$server->name = 'docs-http';$server->count = 1;$server->onMessage = static function (TcpConnection $connection, Request $request): void { if ($request->method() !== 'GET') { $connection->send(new Response(405, ['Allow' => 'GET'], 'Method not allowed')); return; }
if ($request->path() !== '/health') { $connection->send(new Response(404, ['Content-Type' => 'text/plain'], 'Not found')); return; }
$body = json_encode(['status' => 'ok'], JSON_THROW_ON_ERROR); $connection->send(new Response(200, ['Content-Type' => 'application/json'], $body));};
Server::runAll();php http.php startcurl -i http://127.0.0.1:18080/healthExpected: 200 and {"status":"ok"}. An unknown path returns 404; POST returns 405 with Allow: GET. No authentication is implemented.
WebSocket echo
Section titled “WebSocket echo”websocket.php
<?php
declare(strict_types=1);
use localzet\Server;use localzet\Server\Connection\TcpConnection;
require __DIR__ . '/vendor/autoload.php';
$server = new Server('websocket://127.0.0.1:' . (getenv('PORT') ?: '18081'));$server->name = 'docs-websocket';$server->count = 1;$server->onMessage = static function (TcpConnection $connection, string $message): void { $connection->send($message);};
Server::runAll();php websocket.php startConnect to ws://127.0.0.1:18081 with a WebSocket client; every message is echoed. Echo is not a chat server: authentication, allowed Origins, message validation and per-user limits must be added before external access.
Server-Sent Events
Section titled “Server-Sent Events”sse.php
<?php
declare(strict_types=1);
use localzet\Server;use localzet\Timer;use localzet\Server\Connection\TcpConnection;use localzet\Server\Protocols\Http\Chunk;use localzet\Server\Protocols\Http\Request;use localzet\Server\Protocols\Http\Response;use localzet\Server\Protocols\Http\ServerSentEvents;
require __DIR__ . '/vendor/autoload.php';
$server = new Server('http://127.0.0.1:' . (getenv('PORT') ?: '18082'));$server->name = 'docs-sse';$server->count = 1;$server->onMessage = static function (TcpConnection $connection, Request $request): void { if ($request->method() !== 'GET' || $request->path() !== '/events') { $connection->send(new Response(404, ['Content-Type' => 'text/plain'], 'Not found')); return; }
if (isset($connection->context->docsSseTimer)) { $connection->close(); return; }
$response = new Response(200, [ 'Content-Type' => 'text/event-stream; charset=utf-8', 'Cache-Control' => 'no-cache', 'X-Accel-Buffering' => 'no', ]); $connection->send($response->withChunkedTransfer()); $sequence = 0; $connection->context->docsSseTimer = Timer::add(1.0, static function () use ($connection, &$sequence): void { $event = new ServerSentEvents('hello', 'message', (string) ++$sequence, 1000); $connection->send(new Chunk((string) $event)); }); $connection->onClose = static function (TcpConnection $connection): void { Timer::del($connection->context->docsSseTimer); unset($connection->context->docsSseTimer); }; $connection->onBufferFull = static function (TcpConnection $connection): void { $connection->close(); };};
Server::runAll();php sse.php startcurl -N http://127.0.0.1:18082/eventsA message arrives every second; IDs start at 1 for each connection. retry is in milliseconds. The response is HTTP chunked: events are wrapped in Chunk, not sent as new HTTP responses. Disconnect removes the timer. A full send buffer closes a slow consumer. Reconnection restarts the sequence: Last-Event-ID replay and a durable event log are not implemented.
Operational boundaries
Section titled “Operational boundaries”Keep blocking database calls, filesystem scans and external HTTP out of hot callbacks unless the selected backend/driver explicitly supports asynchronous execution. Worker memory persists between requests. Store shared state outside process-local globals. Use Timer::add() for scheduling; Timer::sleep() may block with the ordinary event loop. Set connection/package/buffer limits for your workload and verify shutdown/reload against the exact deployed version.