Worker Class
OxPHP\Server\Worker is the unified runtime handle for everything tied to a single OxPHP OS worker thread. It is a final, stateless wrapper over the bridge thread-local state, registered by the SAPI extension itself, so it is always available in both traditional mode and worker mode. Each call reads live state directly from the runtime; the object itself caches nothing.
Worker::current() returns a singleton per OS thread: two calls on the same thread always return the same instance.
Quick reference
| Method | Description |
|---|---|
Worker::current(): self |
Returns the singleton handle for the current OS thread. |
Worker::isWorkerMode(): bool |
Returns true if the server is running in worker mode (i.e. WORKER_MODE_ENABLED=true). |
id(): int |
Numeric worker identifier in the range 0..N-1 for the current OS thread. |
startTime(): float |
Unix timestamp (seconds) when this OS worker thread was spawned. |
requestCount(): int |
1-based count of requests handled by this OS thread. Increases in both modes. |
memoryUsage(): int |
Live PHP memory usage in bytes (zend_memory_usage(0)). |
rss(): int |
Process resident set size in bytes. Uncached, so call it at most once per request. |
maxMemoryBytes(): int |
Configured memory cap in bytes. 0 means unlimited. |
scheduleExit(): void |
Marks the worker for graceful exit after the current request completes. No-op in traditional mode. |
isExitScheduled(): bool |
Returns true once an exit with reason 'scheduled' is pending for the current worker: scheduleExit() was called, or the worker retired itself. The 'max_memory' and 'error' exits do not set it. Always false in traditional mode. |
exitReason(): ?string |
Pending exit reason: 'scheduled' (scheduleExit() was called, or the worker retired itself: after a fatal error it cannot fully recover from, or because a request that ended with an internal function's call on its stack left it holding much more memory), 'max_memory', 'error', or null when no exit is pending. Always null in traditional mode. |
serve(callable $h): void |
Enters the request loop. Throws InvalidServeContextException outside worker mode. |
Mode matrix
| Method | Traditional mode | Worker mode |
|---|---|---|
current() |
Singleton per OS thread. | Singleton per OS thread. |
isWorkerMode() |
false |
true |
id() |
OS-thread index in the worker pool. | OS-thread index in the worker pool. |
startTime() |
Time the OS thread was spawned (typically server start). | Time the OS thread was spawned. |
requestCount() |
1-based, increments across requests reusing the same OS thread (1, 2, 3, …). |
1-based, increments per request handled by the worker. |
memoryUsage() |
Live PHP memory at the moment of the call. | Live PHP memory at the moment of the call. |
rss() |
Live process RSS. | Live process RSS. |
maxMemoryBytes() |
0 (no recycle cap applies). |
Value of WORKER_MAX_MEMORY_MIB × 1 MiB, or 0 if unset. |
scheduleExit() |
No-op (the script is exiting anyway). | Sets the exit flag; the request loop stops after the current handler returns. |
isExitScheduled() |
Always false. |
true after scheduleExit() has been called on this thread or the worker has retired itself. The 'max_memory' and 'error' exits do not set it. |
exitReason() |
Always null. |
null until an exit is pending; then one of 'scheduled', 'max_memory', 'error'. |
serve(callable) |
Throws OxPHP\Server\Exception\InvalidServeContextException. |
Enters the request loop. |
Examples
Per-worker logging context
Tag every log line with the worker id and per-thread request counter so you can correlate request traffic with a specific worker.
<?php
$worker = OxPHP\Server\Worker::current();
$logger->info('handling request', [
'worker_id' => $worker->id(),
'request_number' => $worker->requestCount(),
]);Bootstrap once per OS thread
requestCount() is 1-based, so the first request handled by any thread sees the value 1. This is a portable place to run lazy, per-thread initialization that should happen exactly once.
<?php
$worker = OxPHP\Server\Worker::current();
if ($worker->requestCount() === 1) {
bootstrap();
}scheduleExit
Application-driven worker recycling. The current request completes normally; the loop checks isExitScheduled() afterwards and breaks out. The supervisor brings a fresh worker up in its place, re-running the outer scope of the worker file. A static pool does this on its next scan; a dynamic pool does it only while the pool is at or below its minimum. See Recycling.
<?php
$worker = OxPHP\Server\Worker::current();
handleRequest();
// Reload bootstrap on every request when developing locally.
if (getenv('OXPHP_DEV') === '1') {
$worker->scheduleExit();
}scheduleExit() is idempotent and a no-op outside worker mode. Use cases:
-
For hot reload during development, exit after every request so the outer-scope bootstrap re-runs.
-
WORKER_MAX_MEMORY_MIBmeasures only the Zend allocator, so with extension-heavy stacks (curl, mysqli) you can add RSS-based recycling and exit when the process RSS crosses your own threshold:if ($worker->rss() > 256 * 1024 * 1024) { $worker->scheduleExit(); } -
For coordinated rolling restarts, gate the call on a sentinel file or signal so an external orchestrator can drain workers cleanly.
Worker entry point
In a worker bootstrap script, call serve() to enter the request loop.
<?php
require __DIR__ . '/../vendor/autoload.php';
OxPHP\Server\Worker::current()->serve(function () {
handleRequest();
});RSS observability
rss() returns the live process resident set size in bytes. The call is a real syscall: cheap, but not free. Read it at most once per request.
<?php
$worker = OxPHP\Server\Worker::current();
$rss = $worker->rss();
$metrics->gauge('php_worker_rss_bytes', $rss, [
'worker_id' => (string) $worker->id(),
]);Migration from oxphp_* functions
The legacy free functions remain available and route through the same internal state. They are not deprecated. New code should prefer the class API for discoverability and consistency.
| Legacy function | Class API |
|---|---|
oxphp_is_worker() |
OxPHP\Server\Worker::isWorkerMode() |
oxphp_worker_id() |
OxPHP\Server\Worker::current()->id() |
oxphp_worker(callable) |
OxPHP\Server\Worker::current()->serve(callable) |
Caveats
rss()is uncached. Each call performs a syscall (/proc/self/statmread on Linux,getrusage(RUSAGE_SELF)on macOS). It is cheap but not free, so call it at most once per request, typically inside a metrics handler rather than on every log line.- Cloning is forbidden.
clone $workerthrows\Error("Cloning OxPHP\\Server\\Worker is not allowed"). The worker handle represents OS-thread identity; cloning it would create the misleading impression of a second handle for the same thread. - Outside an OxPHP host (e.g. when an extension that links the SAPI is loaded into PHP CLI),
Worker::current()still returns an instance, but every accessor returns its zero-state value:id()is0,startTime()is the process start time,requestCount()is0,rss()is the live RSS, andserve()throwsInvalidServeContextException.
Prompts for your agent
These prompts assume an agent with the OxPHP documentation MCP server connected. The agent reads the pages it needs itself.
Add worker introspection to my application using `OxPHP\Server\Worker`. Read the `php/worker-class` page through the OxPHP documentation MCP server.
Find where my code logs each request or does one-time bootstrap work, and wire in `Worker::current()->id()` and `requestCount()` for the log lines and a `requestCount() === 1` guard for the bootstrap.See also
- Worker Mode: overview of persistent PHP workers and the bootstrap-once pattern
- PHP Functions: reference for the legacy
oxphp_*free functions - Request API:
OxPHP\Http\RequestInterface::startTime()for per-request timing