narya/php-sdk

PHP SDK for the Narya Runtime Engine (Go) - Worker, protocol, lifecycle, UDS + MessagePack bridge

Installs: 12

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/narya/php-sdk

v1.0.0 2026-01-30 13:46 UTC

This package is not auto-updated.

Last update: 2026-02-14 14:50:13 UTC


README

PHP library that integrates PHP userland with the Narya Runtime Engine (Go).
Namespace: Narya\SDK.

Protocol: UDS (Unix Domain Sockets) + MessagePack, handshake NARYA1/OK, 4-byte BE length framing + payload.

Components

Component Description
Worker (Runtime\Worker) Orchestrates the loop: receives request from Go, calls application or handler, sends response. Resets state between requests.
WorkerBridge (Runtime\WorkerBridge) UDS + MessagePack bridge: handshake, read/write frames, invokes handler.
NaryaRequest / WorkerRequest Request contract and implementation (id, method, uri, path, query, headers, body, remote_addr, host, scheme, timeout_ms, meta, worker_id, runtime_version).
NaryaResponse / WorkerResponse Response contract and implementation (status, headers, body, error). The Bridge adds id and _meta.
ApplicationWorker Application (framework) contract: `handle(NaryaRequest): array
LifecycleInterface / LifecycleManager Worker lifecycle: boot() before the loop (e.g. connect to socket), shutdown() on exit (max_requests or EOF). Passed as Worker’s 4th argument or via setLifecycle().

Requirements

  • PHP 8.2+
  • msgpack extension (pecl install msgpack)
  • Linux or WSL (UDS not supported on native Windows)

Installation

composer require narya/php-sdk

Basic usage

The Go runtime starts each process with: php worker.php --sock /path/to.sock.

<?php

declare(strict_types=1);

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

use Narya\SDK\Runtime\Worker;
use Narya\SDK\Runtime\WorkerResponse;
use Narya\SDK\Contracts\ApplicationWorker;
use Narya\SDK\Contracts\NaryaRequest;

$app = new class () implements ApplicationWorker {
    public function handle(NaryaRequest $request): array|WorkerResponse
    {
        if ($request->getPath() === '/health' && $request->getMethod() === 'GET') {
            return WorkerResponse::create(200, ['Content-Type' => ['application/json']], '{"status":"ok"}', '');
        }
        return WorkerResponse::create(404, [], '', 'Not Found');
    }

    public function reset(): void
    {
        // Clear per-request state (superglobals, connections, etc.)
    }
};

(new Worker($app))->run();

With lifecycle (boot before the loop, shutdown on exit):

use Narya\SDK\Lifecycle\LifecycleManager;

$lifecycle = new LifecycleManager();

// Run once when the worker starts (before connecting to the socket)
$lifecycle->onBoot(function (): void {
    // e.g. open persistent DB connection, warm cache, load config
    // MyApp::connectDb();
    // MyApp::warmCache();
});

// Run when the loop ends (max_requests reached, EOF from Go, or exception)
$lifecycle->onShutdown(function (): void {
    // e.g. close connections, flush logs, cleanup
    // MyApp::closeDb();
    // MyApp::flushLogs();
});

(new Worker($app, null, 10000, $lifecycle))->run();

Example with a shared resource in the worker (e.g. connection opened in boot, closed in shutdown):

// Simple container: boot() sets it, shutdown() clears it, app uses it in handle()
class WorkerContainer {
    public static ?PDO $db = null;
}

$lifecycle = new LifecycleManager();
$lifecycle->onBoot(function (): void {
    WorkerContainer::$db = new PDO('sqlite::memory:'); // or real DSN
});
$lifecycle->onShutdown(function (): void {
    WorkerContainer::$db = null;
});

$app = new class () implements ApplicationWorker {
    public function handle(NaryaRequest $request): array|WorkerResponse {
        $db = WorkerContainer::$db; // available for the whole loop (until shutdown)
        // use $db for queries...
        return WorkerResponse::create(200, ['Content-Type' => ['application/json']], '{"ok":true}', '');
    }
    public function reset(): void {}
};

(new Worker($app, null, 10000, $lifecycle))->run();

With a callable handler (no framework):

$handler = function (array $request): array {
    return [
        'status' => 200,
        'headers' => ['Content-Type' => ['application/json']],
        'body' => '{"message":"Hello"}',
        'error' => '',
    ];
};

(new Worker(null, $handler))->run();

Dependency injection container

If your application already uses a DI container:

  • boot() — Configure the container once when the worker starts (bindings, persistent connections).
  • ApplicationWorker — Receive the container in the constructor and use it in handle() to resolve services.
  • reset()Here you clear the per-request context (ctx): request-scoped container state (e.g. $container->resetRequestScope()). The Worker calls reset() after each request.
  • shutdown() — Cleanup when the worker exits (close connections, flush logs). Called once when leaving the loop.

So: clearing context on each request → inside reset(), not in shutdown. See a full example in examples/worker_with_container.php.

Tests

composer install
./vendor/bin/phpunit

License

MIT