obray/socket-server

Generic socket server with non-blocking reads and writes.

Maintainers

Package info

github.com/nateobray/SocketServer

pkg:composer/obray/socket-server

Statistics

Installs: 1 946

Dependents: 2

Suggesters: 0

Stars: 0

Open Issues: 0

v1.1.0 2026-06-05 13:43 UTC

README

Small PHP socket server library with non-blocking reads and queued writes.

This package is intentionally low level. It accepts socket connections, invokes a handler contract, and exposes a small timer and logging surface for libraries or applications built on top of it. Version 1.x is intentionally single-process and event-loop based.

Install

composer require obray/socket-server

Echo Server

<?php

require __DIR__ . '/vendor/autoload.php';

$server = new \obray\SocketServer('tcp', '127.0.0.1', 8080);
$server->registerHandler(new \obray\handlers\EchoServer());
$server->start();

You can also pass the handler directly:

$server->start(new \obray\handlers\EchoServer());

Handler Contract

Handlers implement obray\interfaces\SocketServerHandlerInterface.

class Handler extends \obray\base\SocketServerBaseHandler
{
    public function onData(string $data, int $readLength, \obray\interfaces\SocketConnectionInterface $connection)
    {
        $connection->qWrite($data);
        return false;
    }
}

The base handler is quiet by default. Pass a logger if you want lifecycle output:

$handler = new \obray\base\SocketServerBaseHandler(function ($message) {
    error_log($message);
});

Key callbacks:

  • onStart($server) runs after the server is bound and the event loop exists.
  • onData($data, $readLength, $connection) runs when data is read.
  • onConnect, onConnected, and onConnectFailed cover connection setup.
  • onDisconnect and onDisconnected cover connection cleanup.
  • onReadFailed and onWriteFailed expose IO failures.

Queued Writes

Use qWrite($data) to enqueue bytes for non-blocking writes.

Use qDisconnect() when a connection should close after queued writes flush. Use disconnect() when the connection should close immediately.

Timers

Register timers from onStart():

public function onStart(\obray\SocketServer $server): void
{
    $server->watchTimer(5, 30, function () {
        // Recurring application work.
    });
}

Timers are event-loop callbacks. Keep them quick and push long-running work out to your application.

Logging

Server status output can be disabled:

$server->showServerStatus(false);

Or routed through a logger:

$server->setLogger(function ($message) {
    error_log($message);
});

Event Loop

The server uses the built-in stream_select event loop by default. If the EV extension is installed, you can request it explicitly:

$server->setEventLoopType(\obray\SocketServer::EV);

Smoke Test

Run the local echo smoke test:

php tools/socket_smoke.php

Production Notes

  • Run long-lived servers under a process supervisor.
  • Keep protocol-specific concerns in higher-level libraries or handlers.
  • Keep application state in your handler or application container.
  • Keep timer callbacks short.
  • Use separate worker processes at the application/process-manager layer if you need horizontal concurrency.