Запускаемые примеры HTTP, WebSocket и SSE
Полные примеры рассчитаны на сверенную development-ревизию Server 7, а не на Server 4 из зависимостей Triangle Engine. HTTP, WebSocket и SSE запущены на PHP 8.5.4 против указанной ниже ревизии исходников. Это не матрица поддерживаемых версий и не обещание опубликованного релиза.
Эти же примеры прошли CI против закреплённой ревизии на PHP 8.1, 8.3 и 8.5. Проверка покрывает три сценария, а не все event loops, ОС или возможности Server. Результат CI.
Подготовка отдельного приложения
Заголовок раздела «Подготовка отдельного приложения»mkdir server-recipes && cd server-recipescomposer require 'localzet/server:dev-main#7e7cbdaec299f2e0d677d6770a33ac09db8d92f2'composer check-platform-reqsСсылка на commit нужна для повторения проверки документации. Для production отдельно выберите и проверьте релиз. Сохраните каждый файл рядом с vendor/; запускайте сервер в переднем плане, каждый в отдельном терминале, останавливайте Ctrl+C. Примеры слушают loopback; внешний доступ подключайте через явно настроенный TLS-прокси.
HTTP JSON
Заголовок раздела «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/healthОжидается 200 и {"status":"ok"}. Неизвестный путь возвращает 404, POST — 405 с Allow: GET. Авторизация не реализована.
WebSocket echo
Заголовок раздела «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 startПодключите WebSocket-клиент к ws://127.0.0.1:18081: каждое сообщение возвращается обратно. Echo — не готовый чат: перед внешним доступом нужны авторизация, проверка Origin, валидация сообщений и лимиты пользователей.
Server-Sent Events
Заголовок раздела «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/eventsКаждую секунду приходит сообщение; ID начинается с 1 для каждого соединения. retry задаётся в миллисекундах. Ответ использует HTTP chunked: событие помещается в Chunk, а не отправляется как новый HTTP-ответ. Разрыв удаляет таймер; переполнение буфера закрывает медленного клиента. После переподключения счётчик начинается заново: replay по Last-Event-ID и постоянный журнал не реализованы.
Границы примеров
Заголовок раздела «Границы примеров»Блокирующие запросы к БД, обход файловой системы и внешний HTTP не помещайте в горячие callbacks без явно асинхронного backend/драйвера. Память worker сохраняется между запросами; общее состояние храните вне process-local globals. Для расписания используйте Timer::add(); Timer::sleep() с обычным event loop может блокировать процесс. Задайте лимиты соединений, пакетов и буферов под нагрузку; shutdown/reload проверяйте на конкретной развёртываемой версии.