phasync
Production / Examples

Production

Examples

Every example of this topic, with its output.

StopReason#

Example 1Tell the client why
use Swerve\ClientRequest;
use Swerve\RequestHandler;
use Swerve\StopReason;
use Swerve\WorkerStoppingException;

$app = new RequestHandler(function (ClientRequest $request) {
    $request->write("waiting for news\n");
    try {
        phasync::sleep(30);
    } catch (WorkerStoppingException $e) {
        phasync::shielded(fn () => $request->write(match ($e->reason) {
            StopReason::Shutdown => "the server is going away\n",
            StopReason::Reload   => "new code is starting: reconnect\n",
            StopReason::Recycle  => "this worker is replaced: reconnect\n",
        }));
    }
});

if (debug_backtrace()) {
    return $app;   // swerve loads this file: it is the application's swerve.php
}

// Run directly: serve the application with swerve, and send it requests
require __DIR__ . '/../serve.php';
serve(__FILE__, ["GET /news HTTP/1.1\r\nHost: example\r\n\r\n"], stop: SIGHUP);   // a reload, once it answers
Output
HTTP/1.1 200 OK
Server: Swerve
Transfer-Encoding: chunked
Connection: close

11
waiting for news

20
new code is starting: reconnect

0

Swerve::log#

Example 1The logger before swerve sets one
use Swerve\Swerve;

// Outside swerve's command line, log() is a NullLogger until setLog() is called
echo get_class(Swerve::log()), "\n";
Swerve::log()->error('nobody sees this');

// It is the same object on every call: hand it to libraries that take a PSR-3 logger
var_dump(Swerve::log() === Swerve::log());
Output
Psr\Log\NullLogger
bool(true)

Swerve::log#

Example 1
Swerve::log()->warning('disk almost full: {free} MB', ['free' => $free]);

Swerve::setLog#

Example 1Log through a PSR-3 logger of your own
use Psr\Log\AbstractLogger;
use Swerve\Swerve;

// Any PSR-3 logger; this one prints each line
Swerve::setLog(new class () extends AbstractLogger {
    public function log($level, string|Stringable $message, array $context = []): void
    {
        echo "[$level] $message ", json_encode($context), "\n";
    }
});

Swerve::log()->info('started');
Swerve::log()->warning('disk almost full: {free} MB', ['free' => 512]);
Output
[info] started []
[warning] disk almost full: {free} MB {"free":512}

Swerve::setLog#

Example 1
Swerve::setLog(new Monolog\Logger('app'));

Swerve::draining#

Example 1False while serving, true once the drain begins
use Swerve\ClientRequest;
use Swerve\RequestHandler;
use Swerve\Swerve;
use Swerve\WebSocket;

$app = new RequestHandler(function (ClientRequest $request) {
    WebSocket::from($request, function (WebSocket $ws) {
        Swerve::onShutdown(fn () => phasync::shielded(function () use ($ws) {
            $ws->send('draining: ' . var_export(Swerve::draining(), true));
            $ws->end(1001);
        }));
        $ws->send('draining: ' . var_export(Swerve::draining(), true));
        foreach ($ws as $message) {
        }
    });
});

if (debug_backtrace()) {
    return $app;   // swerve loads this file: it is the application's swerve.php
}

// Run directly: serve the application with swerve, and talk to it as a browser would
require __DIR__ . '/../serve.php';
websocket(__FILE__, '/live', [], stop: SIGTERM);   // swerve is stopped after the first message
Output
HTTP/1.1 101 Switching Protocols
upgrade: websocket
connection: Upgrade
sec-websocket-accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: Swerve

< draining: false
< draining: true
< close 1001

Swerve::draining#

Example 1
while (!Swerve::draining()) {
    $out->append(": keep-alive\n\n");
    phasync::sleep(15);
}

Swerve::onShutdown#

Example 1Close a WebSocket with a reason when the server stops
use Swerve\ClientRequest;
use Swerve\RequestHandler;
use Swerve\Swerve;
use Swerve\WebSocket;

$app = new RequestHandler(function (ClientRequest $request) {
    WebSocket::from($request, function (WebSocket $ws) {
        // When the worker must close its connections: say why, in the protocol
        Swerve::onShutdown(fn () => phasync::shielded(fn () => $ws->end(1001, 'server restarting')));

        $ws->send('welcome');
        foreach ($ws as $message) {
            $ws->send($message);
        }
    });
});

if (debug_backtrace()) {
    return $app;   // swerve loads this file: it is the application's swerve.php
}

// Run directly: serve the application with swerve, and talk to it as a browser would
require __DIR__ . '/../serve.php';
websocket(__FILE__, '/live', [], stop: SIGTERM);   // swerve is stopped after the welcome
Output
HTTP/1.1 101 Switching Protocols
upgrade: websocket
connection: Upgrade
sec-websocket-accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: Swerve

< welcome
< close 1001 server restarting

Swerve::onShutdown#

Example 1
YourProtocol::from($request, function (YourProtocol $conn) {
    Swerve::onShutdown(fn () => phasync::shielded(fn () => $conn->end('restarting')));
    foreach ($conn as $message) {
        $conn->send(handle($message));
    }
});

Swerve::awaitShutdown#

Example 1Wait with a timeout
use Swerve\ClientRequest;
use Swerve\RequestHandler;
use Swerve\Swerve;

$app = new RequestHandler(function (ClientRequest $request) {
    // false when the timeout passes first
    $request->write('must close within 0.1 s: ' . var_export(Swerve::awaitShutdown(0.1), true) . "\n");
});

if (debug_backtrace()) {
    return $app;   // swerve loads this file: it is the application's swerve.php
}

// Run directly: serve the application with swerve, and send it requests
require __DIR__ . '/../serve.php';
serve(__FILE__, ["GET / HTTP/1.1\r\nHost: example\r\n\r\n"]);
Output
HTTP/1.1 200 OK
Server: Swerve
Transfer-Encoding: chunked
Connection: close

1f
must close within 0.1 s: false

0

Swerve::awaitShutdown#

Example 1
if (Swerve::awaitShutdown(30)) {
    $ws->end(1001);
}

Swerve::ini#

Example 1
Swerve::ini(['memory_limit' => '512M']);

Swerve::onWorkerStart#

Example 1
// a file in your package's composer.json "files"
Swerve::onWorkerStart(function () {
    phasync::go(function () {
        while (true) {
            refresh_feature_flags();
            phasync::sleep(60);
        }
    });
});

Swerve::getVersion#

Example 1The installed version
use Swerve\Swerve;

// The installed version of phasync/swerve, as Composer knows it
echo 'swerve ', Swerve::getVersion(), "\n";
Output
swerve 0.1.0

WorkerStoppingException#

Example 1Clean up when the worker stops
use Swerve\ClientRequest;
use Swerve\RequestHandler;
use Swerve\WorkerStoppingException;

$app = new RequestHandler(function (ClientRequest $request) {
    $request->write("export started\n");
    try {
        phasync::sleep(30);                      // a long job; the worker stops meanwhile
        $request->write("export done\n");
    } catch (WorkerStoppingException $e) {
        // Clean up at once; a write waits, so it goes inside shielded()
        phasync::shielded(fn () => $request->write("stopped: {$e->reason->name}, try again\n"));
    }
});

if (debug_backtrace()) {
    return $app;   // swerve loads this file: it is the application's swerve.php
}

// Run directly: serve the application with swerve, and send it requests
require __DIR__ . '/../serve.php';
serve(__FILE__, ["GET /export HTTP/1.1\r\nHost: example\r\n\r\n"], stop: SIGTERM);   // stop swerve once it answers
Output
HTTP/1.1 200 OK
Server: Swerve
Transfer-Encoding: chunked
Connection: close

f
export started

1d
stopped: Shutdown, try again

0