async

A cooperative event loop for HolyPHP: timers, socket watchers, line-oriented TCP servers, and HTTP — all in one loop, no threads needed. Pure HolyPHP on top of the runtime's non-blocking socket primitives.

stdlib — embedded timers socket watchers HTTP client + server single event loop

Timers

import "async";

$loop = new Loop();
$loop->after(1000, function() { echo "one second passed"; });
$loop->every(250, function() { echo "tick"; });
$loop->run();          // blocks until stop() or nothing left to wait on
MethodReturnsWhat it does
after(int $ms, $fn)intone-shot timer; returns an id for cancelTimer
every(int $ms, $fn)intrepeating timer (self-reschedules)
cancelTimer(int $id)voidcancel a pending timer
stop()voidleave run() from inside a callback
tick(int $maxWaitMs = 25)boolrun one poll iteration yourself
run()voidloop until stop() or idle

TCP: servers, watchers, lines

import "async";

// one-shot TCP server: accept callback per connection
$loop = new Loop();
$loop->listen("tcp://127.0.0.1:8080", function($client, $peer) {
    stream_send($client, "hello $peer\n");
    stream_close($client);
});

// raw readable-socket watcher
$loop->watch($sock, function($sock, $chunk, $eof) {
    if ($eof) { return; }
    echo "got: $chunk";
});

// line-oriented server with automatic ACK replies
$lp = async_line_server("tcp://0.0.0.0:9000", function($client, $line) {
    return "ACK:" . strtoupper($line);   // non-null return = reply line
});
$lp->run();
Loop async_line_server(string $addr, $onLine, $onConnect = null)

LineReader assembles stream chunks into complete \n lines — handy for simple TCP protocols (works with raw threads too).

HTTP

import "async";

$loop = new Loop();

// in-loop GET: the loop stays live (timers + other sockets keep firing)
$loop->httpGet("http://service.local/api", function($resp) {
    echo "status: " . substr($resp, 0, 15);
}, function($err) {
    echo "failed: $err";
});

// blocking shape: runs a private loop, returns when done
async_get("http://example.invalid/", function($resp) {
    file_put_contents("out.html", $resp);
});

$loop->run();
bool Loop::httpGet(string $url, $onDone, $onError = null) // composable primitive, 10 s timeout bool async_get(string $url, $onDone, $onError = null) // blocking convenience wrapper

Responses arrive as the full HTTP payload (status line + headers + body). For parallel fetches call async_get from several thread workers.

Sockets + threads

Sockets are process-global, so one thread's loop serves connections while the main thread blocks in async_get — the two libraries compose:

import "async";
import "thread";

$ready = chan_new(1);
thr_spawn(function() use ($ready) {
    $lp = new Loop();
    $lp->listen("tcp://127.0.0.1:8934", function($c, $peer) {
        stream_send($c, "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nConnection: close\r\n\r\nHELLO");
        stream_close($c);
    });
    chan_send($ready, "listening");
    $lp->run();
});

chan_recv($ready);
async_get("http://127.0.0.1:8934/x", function($resp) {
    echo $resp;                       // HTTP/1.1 200 OK ...
});

Try it

hphp pkg install async
hphp run examples/async_demo.hphp

Ships embedded in the compiler — import "async" works offline, no install needed. Source: lib/async/async.hphp.