PHP-функции
OxPHP регистрирует свои функции через расширение oxphp_sapi, которое автоматически загружается для каждого PHP-скрипта, выполняемого сервером. Директива extension= и ручная загрузка не требуются. Все перечисленные здесь функции доступны с первой строки вашего PHP-кода.
Оглавление
- oxphp_http_request()
- oxphp_superglobals_enabled()
- oxphp_request_id()
- oxphp_worker_id()
- oxphp_server_info()
- oxphp_finish_request()
- oxphp_is_worker()
- oxphp_worker()
- oxphp_is_streaming()
- oxphp_stream_flush()
- oxphp_sleep()
- oxphp_usleep()
- oxphp_async()
- oxphp_async_await()
- oxphp_async_await_all()
- oxphp_async_await_race()
- oxphp_async_await_any()
- oxphp_register_decorator()
- oxphp_apm_trace()
- oxphp_apm_start()
- oxphp_apm_end()
- oxphp_apm_attribute()
- oxphp_apm_event()
- oxphp_apm_error()
- oxphp_apm_status()
- oxphp_apm_trace_id()
- oxphp_apm_span_id()
- oxphp_apm_header()
- OxPHP\Profile\is_active()
- OxPHP\Profile\start()
- OxPHP\Profile\stop()
- OxPHP\Profile\pause()
- OxPHP\Profile\resume()
- OxPHP\Profile\mark()
- OxPHP\Profile\metric()
- Классы и интерфейсы
- Исключения
oxphp_http_request()
oxphp_http_request(): \OxPHP\Http\RequestВозвращает объект запроса для текущего HTTP-запроса. Каждый вызов возвращает новый объект. Объект предоставляет типизированный доступ к HTTP-методу, URI, параметрам строки запроса, разобранному телу, заголовкам, cookie, загруженным файлам, IP клиента и таймингам запроса.
Возвращает: экземпляр \OxPHP\Http\Request, опирающийся на данные запроса в текущем PHP-потоке воркера.
Выбрасывает: исключение из пространства имён OxPHP\Http\Exception, если вызвана вне активного запроса:
| Исключение | Ситуация |
|---|---|
\OxPHP\Http\Exception\WorkerIdleException |
Режим воркеров, между запросами |
\OxPHP\Http\Exception\AsyncContextException |
Внутри колбэка oxphp_async() |
\OxPHP\Http\Exception\NoActiveRequestException |
Любой другой контекст без активного запроса |
В обычном коде обработки запроса обработка исключений не требуется.
Пример:
<?php
$request = oxphp_http_request();
$method = $request->method(); // "POST"
$path = $request->path(); // "/api/users"
$email = $request->payload('email'); // from JSON or form body
$token = $request->header('Authorization');
$theme = $request->cookie('theme', 'light');Полный справочник по интерфейсу см. в документации HTTP Request API.
oxphp_superglobals_enabled()
oxphp_superglobals_enabled(): boolВозвращает, включено ли заполнение суперглобальных переменных для этого экземпляра сервера. Значение отражает переменную окружения SUPERGLOBALS_ENABLED и не меняется в течение всего времени работы сервера.
Когда значение false, запрос выполняется с пустым $_GET, а $_SERVER содержит только четыре ключа, которые PHP регистрирует сам: REQUEST_TIME, REQUEST_TIME_FLOAT, argc и argv. $_POST, $_FILES и $_COOKIE формируются как обычно, потому что они берутся из тела запроса и заголовка Cookie, которых эта настройка не касается. Это не влияет на HTTP Object API (oxphp_http_request()), php://input и функции сессий PHP. Полная таблица приведена в разделе SUPERGLOBALS_ENABLED.
Возвращает: true, когда SUPERGLOBALS_ENABLED равно true (значение по умолчанию), иначе false.
Пример:
<?php
if (oxphp_superglobals_enabled()) {
$query = $_GET['page'] ?? 1;
} else {
$query = oxphp_http_request()->query('page', 1);
}oxphp_request_id()
oxphp_request_id(): stringВозвращает уникальный идентификатор текущего запроса. Это то же значение, что отправляется в заголовке ответа X-Request-ID. Если клиент присылает заголовок X-Request-ID, OxPHP пропускает его без изменений вместо генерации нового.
Возвращает: 20-символьную шестнадцатеричную строку, когда идентификатор генерирует OxPHP (например, "67890abc12341a2b0042"). Когда клиент присылает заголовок X-Request-ID, это значение возвращается как есть (1–64 символа, буквы и цифры плюс -, _, .).
Пример:
<?php
$id = oxphp_request_id();
error_log("[$id] Processing order #1234");
// Propagate the ID to downstream services
header("X-Correlation-ID: $id");oxphp_worker_id()
oxphp_worker_id(): intВозвращает индекс (отсчёт с нуля) PHP-потока воркера, обрабатывающего текущий запрос. Индексы воркеров находятся в диапазоне от 0 до PHP_WORKERS - 1.
Возвращает: целое число, идентифицирующее текущий поток воркера.
Пример:
<?php
$workerId = oxphp_worker_id();
// Use per-worker temp files to avoid collisions
$tmp = "/tmp/worker_{$workerId}_buffer.dat";
error_log("Worker $workerId handling request");oxphp_server_info()
oxphp_server_info(): arrayВозвращает ассоциативный массив с метаданными сервера и запроса.
Возвращает: массив со следующими ключами:
| Ключ | Тип | Описание |
|---|---|---|
version |
string |
Версия сервера (например, "0.13.0") |
worker_id |
int |
То же значение, что и oxphp_worker_id() |
request_time |
float |
Unix-метка времени с микросекундной точностью, когда начался запрос |
worker_mode |
bool |
Работает ли текущий процесс в режиме воркеров |
runtime_hooks |
array |
Категории хуков времени выполнения, которые этот процесс установил при запуске, в виде списка имён категорий (["sleep", "streams", "net"]). Пуст, если RUNTIME_HOOKS ничего не включила; см. Хуки времени выполнения |
Пример:
<?php
$info = oxphp_server_info();
// [
// "version" => "0.13.0",
// "worker_id" => 3,
// "request_time" => 1738800000.123456,
// "worker_mode" => true,
// "runtime_hooks" => ["sleep", "streams", "net"],
// ]
$elapsed = microtime(true) - $info['request_time'];
echo "Processing took {$elapsed}s so far";oxphp_finish_request()
oxphp_finish_request(): boolСбрасывает ответ клиенту и продолжает выполнение PHP в фоне. Клиент немедленно получает полный HTTP-ответ; скрипт продолжает работать, пока не завершится естественным образом. Это аналог fastcgi_finish_request() из PHP-FPM в OxPHP.
Возвращает: true при успехе, false, если функция уже была вызвана для этого запроса.
PHP-поток воркера остаётся занятым, пока скрипт не завершится. Держите фоновую работу короткой или выносите тяжёлую обработку в очередь.
Пример:
<?php
http_response_code(202);
echo json_encode(['status' => 'accepted']);
oxphp_finish_request();
// The client already has its 202 response; continue working
send_notification_email($user);
update_analytics($event);oxphp_is_worker()
oxphp_is_worker(): boolВозвращает, работает ли сервер в режиме воркеров. Режим воркеров активируется, когда WORKER_MODE_ENABLED=true.
Возвращает: true, если сервер работает в режиме воркеров, false — в традиционном режиме.
Пример:
<?php
if (oxphp_is_worker()) {
// Reuse persistent connections across requests
$db = $GLOBALS['db'] ??= new PDO($dsn);
} else {
// Traditional mode: create a new connection per request
$db = new PDO($dsn);
}oxphp_worker()
oxphp_worker(callable $handler): boolВходит в цикл постоянного режима воркеров. OxPHP вызывает $handler по одному разу для каждого входящего HTTP-запроса. Между запросами мягкий сброс очищает состояние на уровне запроса (буферы вывода, заголовки и суперглобальные переменные), не разрушая кучу PHP, поэтому любые переменные, объявленные вне обработчика, сохраняются между запросами. $_ENV намеренно исключён из этого сброса: при стандартном для PHP auto_globals_jit=1 он живёт всё время жизни воркера, поэтому значение, которое обработчик туда записал, прочитает каждый последующий запрос на этом воркере и любой запрос, мультиплексированный рядом с ним. См. Суперглобальные переменные.
Параметры:
$handler— вызывается по одному разу на запрос. Обработчик не получает аргументов. Используйте суперглобальные переменные ($_SERVER,$_GET,$_POSTи т. д.) илиoxphp_http_request()внутри обработчика для доступа к данным запроса.
Возвращает: true, когда цикл завершился, false, если сервер не в режиме воркеров. Каждый из перечисленных ниже выходов возвращает true, поэтому по значению нельзя понять, почему цикл завершился.
Цикл воркера завершается при выполнении любого из следующих условий:
- Сервер корректно завершает работу
- 3 запроса подряд завершаются аварийно. Обычная причина — фатальная ошибка в обработчике; учитываются также два вида сбоев вокруг файбера запроса. Необработанное исключение получает ответ и не считается, как и запрос, отменённый сервером, если только на нём не была зафиксирована ещё и фатальная ошибка (см. Режим воркеров → Перезапуск)
- Воркер превышает
WORKER_MAX_MEMORY_MIB - Запрос был завершён с вызовом внутренней функции на стеке, и после этого память воркера выросла сверх порога. Замер делается между двумя моментами, когда в обработке нет ни одного запроса, и не учитывает файберы, созданные воркером (см. Режим воркеров → Перезапуск)
- Приложение вызывает
Worker::scheduleExit() - Динамический пул (
PHP_WORKERS=MIN:MAX) выводит этот воркер из работы, потому что тот простаивал дольшеPHP_WORKERS_IDLE_SECONDS(по умолчанию 30), ничего не удерживая. Сервер продолжает обслуживать запросы: пул просто сокращается (см. Динамический пул)
Код после oxphp_worker() выполняется при каждом из этих выходов, а не только при первом. В динамическом пуле это регулярное событие на исправном сервере: простаивающий воркер выводится из работы, выполняется его код завершения, а остальной пул продолжает обслуживать запросы. Пишите этот блок как завершение воркера, а не остановку сервера. Строка, которая пишет в лог «server stopping», ошибочна при каждом выходе, кроме последнего, а ресурс, общий не только для этого воркера, будет освобождён, пока сервер ещё работает.
oxphp_worker() работает только в режиме воркеров (WORKER_MODE_ENABLED=true). В традиционном режиме она пишет предупреждение в лог и возвращает false.
Пример:
<?php
// worker.php — runs once per worker lifetime
// Bootstrap: executed once on startup
require __DIR__ . '/vendor/autoload.php';
$app = new App();
// Handle requests in a loop
oxphp_worker(function () use ($app) {
$app->handle();
});
// Reached when this worker's loop ends: server shutdown, recycling,
// or a dynamic pool retiring this worker while the server keeps running
$app->terminate();oxphp_is_streaming()
oxphp_is_streaming(): boolВозвращает, находится ли текущий запрос в потоковом режиме. Потоковый режим активируется при первом вызове oxphp_stream_flush() или автоматически, когда PHP устанавливает Content-Type: text/event-stream.
Возвращает: true, если потоковый режим активен, иначе false.
Пример:
<?php
if (oxphp_is_streaming()) {
echo "data: " . json_encode($event) . "\n\n";
oxphp_stream_flush();
} else {
echo json_encode($allData);
}oxphp_stream_flush()
oxphp_stream_flush(): boolАктивирует потоковый режим и сбрасывает любой буферизованный вывод клиенту в виде HTTP-чанка. При первом вызове HTTP-заголовки отправляются немедленно и начинается потоковая передача. Каждый последующий вызов сбрасывает вывод, записанный со времени предыдущего сброса.
Возвращает: true при успехе, false, если oxphp_finish_request() уже была вызвана.
Потоковый режим также активируется автоматически, когда PHP устанавливает Content-Type: text/event-stream. В этом случае можно использовать встроенную функцию PHP flush(), но сначала вызовите ob_end_flush(), чтобы обойти слой буферизации вывода PHP.
Пример:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
for ($i = 0; $i < 10; $i++) {
echo "id: $i\n";
echo "data: " . json_encode(['counter' => $i]) . "\n\n";
oxphp_stream_flush();
oxphp_sleep(1.0); // use oxphp_sleep instead of sleep — cooperative in fiber mode, where a switch is possible
}oxphp_sleep()
oxphp_sleep(float $seconds): voidПриостанавливает выполнение на указанную длительность. Внутри обработчика режима воркеров, выполняющегося в файбере, этот вызов кооперативен — он приостанавливает текущий файбер, чтобы во время ожидания могли обрабатываться другие запросы. Вне файбера он откатывается к стандартному блокирующему usleep().
Он также блокирует там, где переключение файбера запрещено, то есть везде, где выход из текущего фрейма был бы небезопасен:
- внутри обработчика
declare(ticks) - внутри диспетчеризации сигналов pcntl
- пока формируются входные данные запроса
- внутри
FILTER_CALLBACK, вызванного изfilter_input_array()с массивом определений - внутри обходов при завершении, которые запускают деструкторы после аварийного завершения запроса
Он также блокирует при вызове внутри файбера, запущенного пользовательским планировщиком (AMPHP, Revolt, голый new Fiber), потому что планировщик OxPHP не может возобновить такой контекст. В разделе Хуки времени выполнения описана та же граница для перехваченных нативных встроенных функций.
oxphp_sleep() кооперативна в режиме воркеров без всякой конфигурации и принимает дробные секунды. RUNTIME_HOOKS=sleep — отдельная, включаемая по желанию модернизация, которая заставляет кооперировать и нативные вызовы sleep()/usleep() (например, внутри стороннего кода), в тех же пределах. Она не отменяет необходимости в oxphp_sleep() в коде, который вы контролируете. См. Хуки времени выполнения.
Параметры:
$seconds— длительность приостановки в секундах. Допускаются дробные значения (например,0.5для 500 миллисекунд). Значения0или меньше возвращают управление немедленно.
Возвращает: void
Пример:
<?php
oxphp_worker(function () {
// In worker mode with fiber multiplexing:
// this suspends the fiber rather than blocking the thread
oxphp_sleep(1.0);
echo json_encode(['done' => true]);
});oxphp_usleep()
oxphp_usleep(int $microseconds): voidПриостанавливает выполнение на указанное число микросекунд. Как и oxphp_sleep(), этот вызов кооперативен внутри файбера и в противном случае откатывается к блокирующему usleep(), в том числе в контекстах, перечисленных в разделе oxphp_sleep(), где переключение файбера запрещено или контекст не принадлежит OxPHP.
Параметры:
$microseconds— длительность приостановки в микросекундах. Значения0или меньше возвращают управление немедленно.
Возвращает: void
Пример:
<?php
oxphp_worker(function () {
// Poll for a condition every 100ms without blocking other requests
while (!$condition_met()) {
oxphp_usleep(100_000);
}
echo "ready";
});oxphp_async()
oxphp_async(Closure $closure, mixed ...$args): intОтправляет замыкание на выполнение в выделенном асинхронном потоке воркера и немедленно возвращает идентификатор промиса. Вызывающий код продолжает выполнение, не дожидаясь завершения замыкания. Используйте oxphp_async_await() для получения результата.
Параметры:
$closure— пользовательскоеClosureдля выполнения в асинхронном потоке воркера...$args— аргументы, передаваемые замыканию. Принимаются скалярные значения (null,bool,int,float,string), массивы из них и экземплярыOxPHP\Shared\*(единственные объекты, которые могут пересекать границу потока). Ресурсы и любые объекты, не являющиесяShared, отклоняются.
Возвращает: целочисленный идентификатор промиса. Передайте его в oxphp_async_await(), oxphp_async_await_all(), oxphp_async_await_race() или oxphp_async_await_any().
Выбрасывает: OxPHP\Async\AsyncException в следующих случаях:
- Асинхронный пул отключён (
ASYNC_WORKERS=0) — сообщение: "Async pool is disabled. Set ASYNC_WORKERS > 0 to enable." - Замыкание не является пользовательским
- Асинхронный пул заполнен (все слоты очереди заняты)
- Аргументы или переменные
useсодержат объекты или ресурсы
Переменные, захваченные через use в замыкании, подчиняются тем же ограничениям — объекты и ресурсы отклоняются.
Пример:
<?php
// Dispatch two independent tasks concurrently
$p1 = oxphp_async(function () {
return fetch_from_api('/users');
});
$p2 = oxphp_async(function () {
return fetch_from_api('/posts');
});
// Retrieve both results
$users = oxphp_async_await($p1);
$posts = oxphp_async_await($p2);oxphp_async_await()
oxphp_async_await(int $promise_id, float $timeout = 0.0): mixedБлокирует выполнение, пока указанный асинхронный промис не завершится, и возвращает его результат. Внутри файбера режима воркеров этот вызов кооперативно приостанавливает текущий файбер, а не блокирует поток.
Параметры:
$promise_id— идентификатор промиса, возвращённыйoxphp_async()$timeout— максимальное время ожидания в секундах.0.0означает ждать бесконечно. По умолчанию:0.0
Возвращает: возвращаемое значение асинхронного замыкания.
Выбрасывает:
OxPHP\Async\AsyncException, если асинхронный пул отключён (ASYNC_WORKERS=0) или если асинхронная задача выбросила исключениеOxPHP\Async\TimeoutException, если превышен$timeout
Пример:
<?php
$promise = oxphp_async(function (int $n) {
return array_sum(range(1, $n));
}, 1_000_000);
$result = oxphp_async_await($promise);
echo $result; // 500000500000
// With timeout
try {
$result = oxphp_async_await($promise, 5.0);
} catch (\OxPHP\Async\TimeoutException $e) {
echo "Task took too long";
}oxphp_async_await_all()
oxphp_async_await_all(array $promise_ids, float $timeout = 0.0): arrayОжидает все промисы в массиве и возвращает ассоциативный массив, сопоставляющий каждый идентификатор промиса с его результатом. Промисы ожидаются в порядке следования в массиве.
Параметры:
$promise_ids— массив целочисленных идентификаторов промисов, возвращённыхoxphp_async()$timeout— максимальное время ожидания на каждый промис в секундах.0.0означает ждать бесконечно. По умолчанию:0.0
Возвращает: ассоциативный массив, где каждый ключ — это идентификатор промиса (целое число), а каждое значение — результат этого промиса.
Выбрасывает:
OxPHP\Async\AsyncException, если асинхронный пул отключён (ASYNC_WORKERS=0) или если любой промис завершается с ошибкойOxPHP\Async\TimeoutException, если любой промис превышает$timeout
Пример:
<?php
$promises = [
oxphp_async(fn() => slow_query('users')),
oxphp_async(fn() => slow_query('orders')),
oxphp_async(fn() => slow_query('products')),
];
$results = oxphp_async_await_all($promises);
foreach ($results as $promiseId => $result) {
// process $result
}oxphp_async_await_race()
oxphp_async_await_race(array $promise_ids, float $timeout = 0.0): arrayУстраивает гонку между несколькими промисами и возвращает первый разрешившийся, независимо от того, выполнился он или был отклонён. Остальные промисы не отменяются — они продолжают выполняться и остаются доступными для ожидания через oxphp_async_await(). Это аналог JavaScript Promise.race.
Параметры:
$promise_ids— массив как минимум из одного целочисленного идентификатора промиса, возвращённогоoxphp_async(). Не должен быть пустым.$timeout— максимальное время ожидания разрешения любого промиса в секундах.0.0означает ждать бесконечно. По умолчанию:0.0
Возвращает: ассоциативный массив с двумя ключами:
id(int) — идентификатор промиса-победителяvalue(mixed) — возвращаемое значение победившего промиса
Выбрасывает:
OxPHP\Async\AsyncException, если асинхронный пул отключён (ASYNC_WORKERS=0) или если победивший промис был отклонёнOxPHP\Async\TimeoutException, если ни один промис не разрешился в течение$timeout
Пример:
<?php
// Try two mirror endpoints; use whichever responds first
$p1 = oxphp_async(fn() => fetch('https://mirror-1.example.com/data'));
$p2 = oxphp_async(fn() => fetch('https://mirror-2.example.com/data'));
$winner = oxphp_async_await_race([$p1, $p2], timeout: 10.0);
echo "Mirror {$winner['id']} won: " . json_encode($winner['value']);oxphp_async_await_any()
oxphp_async_await_any(array $promise_ids, float $timeout = 0.0): arrayВозвращает управление, как только один промис ВЫПОЛНЯЕТСЯ. Отклонения накапливаются и становятся наблюдаемыми только в том случае, если отклоняются все промисы. Это аналог JavaScript Promise.any — полезен для паттернов резервирования / избыточности, когда вам подходит любой работающий источник.
Параметры:
$promise_ids— массив как минимум из одного целочисленного идентификатора промиса, возвращённогоoxphp_async(). Не должен быть пустым.$timeout— максимальное время ожидания первого выполнения в секундах.0.0означает ждать бесконечно. По умолчанию:0.0
Возвращает: ассоциативный массив с двумя ключами:
id(int) — идентификатор первого выполнившегося промисаvalue(mixed) — возвращаемое значение победившего промиса
Выбрасывает:
OxPHP\Async\AsyncException, если асинхронный пул отключён (ASYNC_WORKERS=0)OxPHP\Async\AggregateAsyncException, если отклонён каждый промис. Исключение переносит все ошибки черезgetErrors()(позиционные, с ключами 0..N-1),getErrorMap()(с ключами по id) иgetPromiseIds().OxPHP\Async\TimeoutException, если ни один промис не выполнился в течение$timeout.getPartialErrors()перечисляет промисы, которые уже были отклонены до дедлайна;getCancelledPromiseIds()перечисляет те, что не разрешились и поэтому были отменены. Для них устанавливается флаг отмены, а их приёмники сбрасываются — передача любого из этих идентификаторов вoxphp_async_await*()впоследствии выбрасывает"unknown or already-awaited promise id". Этот список — журнал аудита, а не очередь возобновляемой работы.
Поведение:
- Промисы, которые всё ещё были в ожидании в момент победы, остаются доступными для ожидания по отдельности через
oxphp_async_await(). - Промисы, которые уже были отклонены до победителя, — нет: их результаты были потреблены при накоплении в качестве кандидатов-ошибок.
Пример:
<?php
$mirror_a = oxphp_async(fn() => fetch('https://mirror-a.example.com/data'));
$mirror_b = oxphp_async(fn() => fetch('https://mirror-b.example.com/data'));
$mirror_c = oxphp_async(fn() => fetch('https://mirror-c.example.com/data'));
try {
$winner = oxphp_async_await_any([$mirror_a, $mirror_b, $mirror_c], 5.0);
echo "Mirror {$winner['id']} responded: " . json_encode($winner['value']);
} catch (\OxPHP\Async\AggregateAsyncException $e) {
// every mirror rejected
foreach ($e->getErrorMap() as $promise_id => $err) {
error_log("mirror {$promise_id}: " . $err->getMessage());
}
} catch (\OxPHP\Async\TimeoutException $e) {
// deadline elapsed before any mirror fulfilled
$partial = $e->getPartialErrors();
$cancelled = $e->getCancelledPromiseIds();
}oxphp_register_decorator()
oxphp_register_decorator(string $class): boolРегистрирует PHP-класс как декоратор, оборачивающий вызовы функций и методов. Класс должен реализовывать OxPHP\Decorator\AttributeInterface. После регистрации OxPHP вызывает хуки декоратора before() и after() вокруг каждого вызова функции или метода, который соответствует целям #[Attribute] декоратора.
Параметры:
$class— полное имя класса декоратора для регистрации
Возвращает: true при успехе, false, если класс не существует или не реализует OxPHP\Decorator\AttributeInterface.
Пример:
<?php
use OxPHP\Decorator\AttributeInterface;
use OxPHP\Decorator\Context;
#[\Attribute(\Attribute::TARGET_METHOD)]
class LogDecorator implements AttributeInterface
{
public function before(Context $ctx): void
{
error_log("Calling {$ctx->target} (request {$ctx->requestId})");
}
public function after(Context $ctx): void
{
error_log("Finished {$ctx->target}");
}
}
// Register once at bootstrap (or worker startup)
oxphp_register_decorator(LogDecorator::class);oxphp_apm_trace()
oxphp_apm_trace(string $name, callable $callback, ?array $attributes = null): mixedВыполняет колбэк внутри именованного спана. Спан открывается перед выполнением колбэка и закрывается после того, как колбэк вернёт управление или выбросит исключение, поэтому вызывать oxphp_apm_end() не нужно.
Колбэк получает локальный ID спана единственным аргументом. Передайте его в oxphp_apm_attribute(), oxphp_apm_event() или oxphp_apm_status(), чтобы явно адресовать этот спан, а не тот, что является текущим.
Если колбэк выбрасывает исключение, спан помечается как ошибочный и на нём записывается событие exception с exception.type и exception.message, после чего исключение пробрасывается дальше без изменений. exception.stacktrace не записывается: его сбор стоит повторного входа в PHP при каждом выбросе, а этот путь срабатывает гораздо чаще, чем oxphp_apm_error(). Если стектрейс нужен, вызовите oxphp_apm_error($e) явно.
Когда APM отключён (OTEL_APM_ENABLED не задана или имеет ложное значение; принимаются on, true, 1 и yes без учёта регистра), колбэк всё равно выполняется и его возвращаемое значение всё равно передаётся дальше. В качестве ID спана он получает 0, и ничего не записывается.
Если $callback не является вызываемым, выбрасывается TypeError. Если же при его разрешении возникает собственное исключение (автозагрузчик, который выбрасывает исключение на строке 'Class::method', или обработчик ошибок, который выбрасывает исключение на уведомление об устаревании для строки 'self::method'), это исключение пробрасывается как есть, и колбэк не выполняется.
Параметры:
$name— имя спана$callback— вызываемый объект для выполнения внутри спана; получаетint $span_id$attributes— необязательный ассоциативный массив строковых пар ключ-значение атрибутов
Возвращает: mixed — значение, которое вернул $callback.
Пример:
<?php
$user = oxphp_apm_trace('user.fetch', function (int $span) use ($id) {
oxphp_apm_attribute('user.id', (string) $id, $span);
return User::find($id);
}, ['db.system' => 'mysql']);oxphp_apm_start()
oxphp_apm_start(string $name, ?array $attributes = null): intОткрывает новый спан и возвращает локальный идентификатор для последующих ссылок. Спан становится дочерним по отношению к текущему активному спану (или к корневому спану запроса, если ни один спан не активен). Используйте oxphp_apm_end(), чтобы закрыть его.
Параметры:
$name— имя спана (например,"cache.warm","payment.process")$attributes— необязательный ассоциативный массив строковых пар ключ-значение атрибутов, устанавливаемых на спан при создании
Возвращает: целочисленный локальный идентификатор спана. Передайте его в oxphp_apm_end(), oxphp_apm_attribute() или другие функции, принимающие $span_id. Возвращает 0, когда APM отключён.
Пример:
<?php
$spanId = oxphp_apm_start('order.validate', [
'order.type' => 'subscription',
]);
validateOrder($order);
oxphp_apm_end($spanId);oxphp_apm_end()
oxphp_apm_end(int $span_id): voidЗакрывает спан, открытый функцией oxphp_apm_start(). Записывается время завершения спана, и он перемещается из активного стека в список завершённых, готовый к экспорту.
Параметры:
$span_id— локальный идентификатор спана, возвращённыйoxphp_apm_start()
Возвращает: void
Всегда закрывайте спаны в обратном порядке. Если вы открыли спан A, затем спан B, закройте B перед A. Незакрытые спаны автоматически закрываются в конце запроса и помечаются oxphp.span.leaked=true.
oxphp_apm_attribute()
oxphp_apm_attribute(string $key, mixed $value, ?int $span_id = null): voidУстанавливает атрибут ключ-значение на спане. Значения преобразуются в строки. Если $span_id не указан, атрибут добавляется к текущему активному спану.
Параметры:
$key— ключ атрибута (например,"user.id","cache.hit")$value— значение атрибута (string, int, float, bool или null -- преобразуется в строку)$span_id— необязательный локальный идентификатор спана. Если опущен, нацеливается на текущий спан
Возвращает: void
Пример:
<?php
$spanId = oxphp_apm_start('db.query');
oxphp_apm_attribute('db.system', 'mysql');
oxphp_apm_attribute('db.statement', 'SELECT * FROM users WHERE id = ?');
oxphp_apm_attribute('db.row_count', $rowCount, $spanId);
oxphp_apm_end($spanId);oxphp_apm_event()
oxphp_apm_event(string $name, ?array $attributes = null, ?int $span_id = null): voidЗаписывает событие с меткой времени на спане. События полезны для логирования отдельных происшествий в течение жизни спана (например, промах кэша, попытка повтора, проверка авторизации).
Параметры:
$name— имя события (например,"cache.miss","retry")$attributes— необязательный ассоциативный массив строковых пар ключ-значение атрибутов события$span_id— необязательный локальный идентификатор спана. Если опущен, нацеливается на текущий спан
Возвращает: void
Пример:
<?php
$spanId = oxphp_apm_start('payment.process');
oxphp_apm_event('payment.authorized', [
'provider' => 'stripe',
'amount' => '49.99',
]);
oxphp_apm_end($spanId);oxphp_apm_error()
oxphp_apm_error(mixed $exception, ?int $span_id = null): voidПомечает статус спана как ошибку (код статуса 2). Используйте это для того, чтобы отметить спаны, где произошло исключение или сбой.
Параметры:
$exception— исключение или ошибка (используется для контекста; статус устанавливается независимо от типа)$span_id— необязательный локальный идентификатор спана. Если опущен, нацеливается на текущий спан
Возвращает: void
Пример:
<?php
$spanId = oxphp_apm_start('external.api');
try {
$result = callExternalApi();
} catch (\Throwable $e) {
oxphp_apm_error($e, $spanId);
throw $e;
} finally {
oxphp_apm_end($spanId);
}oxphp_apm_status()
oxphp_apm_status(int $code, ?string $description = null, ?int $span_id = null): voidУстанавливает код статуса и необязательное описание на спане.
Параметры:
$code— код статуса:0= Unset,1= Ok,2= Error$description— необязательное человекочитаемое описание статуса$span_id— необязательный локальный идентификатор спана. Если опущен, нацеливается на текущий спан
Возвращает: void
Пример:
<?php
$spanId = oxphp_apm_start('validation');
if ($valid) {
oxphp_apm_status(1, 'Validation passed', $spanId);
} else {
oxphp_apm_status(2, 'Invalid input: missing email', $spanId);
}
oxphp_apm_end($spanId);oxphp_apm_trace_id()
oxphp_apm_trace_id(): stringВозвращает W3C trace ID (32 шестнадцатеричных символа) для контекста трассировки текущего запроса. Это то же значение, что и $_SERVER['OXPHP_TRACE_ID'], доступное без суперглобальных переменных.
Возвращает: 32-символьную шестнадцатеричную строку trace ID. Возвращает пустую строку, когда APM отключён или контекст трассировки не активен.
Пример:
<?php
$traceId = oxphp_apm_trace_id();
error_log("Processing request in trace {$traceId}");oxphp_apm_span_id()
oxphp_apm_span_id(): stringВозвращает span ID (16 шестнадцатеричных символов) текущего активного спана. Если есть вложенные спаны, возвращается идентификатор самого внутреннего открытого спана.
Возвращает: 16-символьную шестнадцатеричную строку span ID. Возвращает пустую строку, когда ни один спан не активен.
oxphp_apm_header()
oxphp_apm_header(): stringВозвращает значение W3C-заголовка traceparent для текущего контекста спана. Используйте это для передачи контекста трассировки в нижестоящие HTTP-вызовы.
Возвращает: строку в формате 00-{trace_id}-{span_id}-01. Возвращает пустую строку, когда контекст трассировки не активен.
Пример:
<?php
$spanId = oxphp_apm_start('http.call');
$traceparent = oxphp_apm_header();
$response = file_get_contents('https://api.example.com/data', false,
stream_context_create([
'http' => [
'header' => "traceparent: {$traceparent}\r\n",
],
])
);
oxphp_apm_end($spanId);OxPHP\Profile\is_active()
OxPHP\Profile\is_active(): boolВозвращает true, когда захват профиля в данный момент активен для этого запроса — то есть его включил триггер (заголовок, cookie, параметр строки запроса или частота сэмплирования) или скрипт вызвал start(), а захват не приостановлен через pause() или stop(). При выключенном профилировщике (PROFILER_ENABLED) триггеры не проверяются, а start() ничего не делает, так что ни то ни другое захват не включает.
Полезно для защиты дорогостоящей инструментации, которая должна выполняться только при включённом профилировании.
Возвращает: bool.
Пример:
<?php
if (OxPHP\Profile\is_active()) {
OxPHP\Profile\mark('checkpoint.before_query');
}OxPHP\Profile\start()
OxPHP\Profile\start(): voidПрограммно включает захват профиля до конца текущего запроса, даже если ни один триггер не сработал на RINIT. Устанавливает режим профилирования в PROFILE_ALL и снимает флаг приостановки.
Ничего не делает, пока профилировщик выключен (PROFILER_ENABLED): тогда профилировщик не записывает вызовы функций, поэтому вызов не включает захват и не заставляет is_active() возвращать true.
Если профиль уже был активен в другом режиме, этот вызов повышает его — любые спаны, уже собранные в более низком режиме, отбрасываются, чтобы захваченный профиль был внутренне согласованным. Используйте это, когда хотите включить в профилирование конкретный путь выполнения кода, не полагаясь на триггеры.
Возвращает: void.
Пример:
<?php
if ($request->header('x-debug') === 'on') {
OxPHP\Profile\start();
}OxPHP\Profile\stop()
OxPHP\Profile\stop(): voidОтключает дальнейший захват спанов для этого запроса. Уже открытые спаны закрываются естественным образом по мере того, как PHP возвращается из них, поэтому стек вызовов остаётся сбалансированным — прекращается лишь запись новых спанов.
Возвращает: void.
Пример:
<?php
OxPHP\Profile\start();
expensive_work();
OxPHP\Profile\stop();
non_profiled_work();OxPHP\Profile\pause()
OxPHP\Profile\pause(): voidМягкий вариант stop(). Эффект тот же (устанавливает флаг приостановки); различие в намерении — pause() сигнализирует, что захват возобновится позже через resume(), тогда как stop() — нет.
Возвращает: void.
OxPHP\Profile\resume()
OxPHP\Profile\resume(): voidСнимает флаг приостановки, установленный pause() или stop(). Сам режим профиля при этом не меняется — если он никогда не был включён, resume() не делает ничего наблюдаемого.
Возвращает: void.
Пример:
<?php
OxPHP\Profile\pause();
$secret = decrypt_payload($data);
OxPHP\Profile\resume();OxPHP\Profile\mark()
OxPHP\Profile\mark(string $label, ?array $attrs = null): voidПрикрепляет событие Mark к самому верхнему открытому спану, с необязательным набором атрибутов. Ничего не делает, когда нет открытого спана (например, профилирование не активно, или mark() вызвана на верхнем уровне запроса вне какого-либо инструментированного фрейма).
Ключи и значения атрибутов приводятся к строкам; нестроковые значения становятся пустой строкой.
Параметры:
$label— короткое человекочитаемое имя события (например,"cache.miss","db.slow_query")$attrs— необязательныйarray<string, scalar>пар ключ/значение, прикрепляемых к событию
Возвращает: void.
Пример:
<?php
function load_user(int $id): array {
$cached = $cache->get("user:$id");
if ($cached === null) {
OxPHP\Profile\mark('cache.miss', ['key' => "user:$id"]);
$cached = $db->fetchUser($id);
}
return $cached;
}OxPHP\Profile\metric()
OxPHP\Profile\metric(string $name, float $value): voidДобавляет атрибут metric.<name> к текущему открытому спану. Ничего не делает, когда нет открытого спана.
В отличие от mark() (которая создаёт отдельное событие), metric() записывает данные в набор атрибутов существующего спана — полезно для фиксации числовых наблюдений, привязанных к окружающей операции (число полученных строк, обработанных байтов, количество повторов).
Параметры:
$name— идентификатор метрики; будет сохранён какmetric.<name>$value— числовое значение (приводится кfloat)
Возвращает: void.
Пример:
<?php
function search(string $query): array {
$results = $index->search($query);
OxPHP\Profile\metric('result_count', count($results));
return $results;
}Классы и интерфейсы
Расширение oxphp_sapi регистрирует следующие классы:
HTTP
| Класс | Описание |
|---|---|
OxPHP\Http\Request |
Объект запроса, возвращаемый oxphp_http_request(). final — не может быть расширен. |
OxPHP\Http\Attributes |
Изменяемый контейнер атрибутов запроса (для middleware). final. |
OxPHP\Http\Session |
Объект сессии, доступный через $request->session(). final. |
OxPHP\Http\UploadedFile |
Объект загруженного файла из $request->files(). final. |
Декораторы
| Класс / интерфейс | Описание |
|---|---|
OxPHP\Decorator\AttributeInterface |
Интерфейс для декораторов. Требует методов before(Context $ctx) и after(Context $ctx). |
OxPHP\Decorator\Context |
Контекстный объект, передаваемый хукам декоратора. final. Публичные свойства: target, class, method, function, objectId, requestId, traceId. Методы: getParams(): array, getResult(): mixed, hasResult(): bool. Полный справочник см. в Декораторы. |
Трассировка
| Класс | Описание |
|---|---|
OxPHP\Apm\Trace |
Встроенный атрибут для автоматического создания спанов. Применяется к функциям или методам. |
Async
| Класс | Описание |
|---|---|
OxPHP\Async\BorrowedProxy |
Прокси-объект для одолженных значений между потоками. |
Исключения
Все исключения, регистрируемые расширением:
| Исключение | Расширяет | Когда выбрасывается |
|---|---|---|
OxPHP\Async\AsyncException |
\Exception |
Ошибка в асинхронной задаче (oxphp_async_await()) или недопустимые аргументы в oxphp_async() |
OxPHP\Async\TimeoutException |
OxPHP\Async\AsyncException |
Превышен таймаут в любой из oxphp_async_await(), oxphp_async_await_all(), oxphp_async_await_race() или oxphp_async_await_any(). Для таймаутов oxphp_async_await_any() заполняются аксессоры getPartialErrors(): array<int, \Throwable> и getCancelledPromiseIds(): list<int>; для остальных мест вызова оба возвращают []. |
OxPHP\Async\AggregateAsyncException |
OxPHP\Async\AsyncException |
Выбрасывается oxphp_async_await_any(), когда отклонён каждый промис. Методы: getErrors(): list<\Throwable> (позиционные, с ключами 0..N-1 по позиции во входных данных), getErrorMap(): array<int, \Throwable> (с ключами по id промиса), getPromiseIds(): list<int> (входные id промисов по порядку). |
OxPHP\Async\BorrowException |
\Exception |
Ошибка при одалживании значения между потоками |
OxPHP\Http\Exception\NoActiveRequestException |
\RuntimeException |
Вызов oxphp_http_request() вне активного запроса |
OxPHP\Http\Exception\AsyncContextException |
NoActiveRequestException |
Вызов oxphp_http_request() внутри колбэка oxphp_async() |
OxPHP\Http\Exception\WorkerIdleException |
NoActiveRequestException |
Вызов oxphp_http_request() в режиме воркеров между запросами |
OxPHP\Decorator\RejectedException |
\Exception |
Декоратор отклонил вызов функции/метода |
Проверка расширения
Вы можете проверить, что расширение OxPHP загружено, и просмотреть все зарегистрированные функции:
<?php
if (extension_loaded('oxphp_sapi')) {
echo "OxPHP extension is loaded\n";
}
$functions = get_extension_funcs('oxphp_sapi');
print_r($functions);
// Array
// (
// [0] => oxphp_http_request
// [1] => oxphp_superglobals_enabled
// [2] => oxphp_request_id
// [3] => oxphp_worker_id
// [4] => oxphp_server_info
// [5] => oxphp_finish_request
// [6] => oxphp_is_worker
// [7] => oxphp_is_streaming
// [8] => oxphp_stream_flush
// [9] => oxphp_sleep
// [10] => oxphp_usleep
// [11] => oxphp_worker
// [12] => oxphp_register_decorator
// [13] => oxphp_async
// [14] => oxphp_async_await
// [15] => oxphp_async_await_all
// [16] => oxphp_async_await_race
// [17] => oxphp_async_await_any
// [18] => oxphp_apm_trace
// [19] => oxphp_apm_start
// [20] => oxphp_apm_end
// [21] => oxphp_apm_attribute
// [22] => oxphp_apm_event
// [23] => oxphp_apm_error
// [24] => oxphp_apm_status
// [25] => oxphp_apm_trace_id
// [26] => oxphp_apm_span_id
// [27] => oxphp_apm_header
// )Основные функции SAPI (вплоть до oxphp_register_decorator) идут первыми; семейства oxphp_async_* и oxphp_apm_* — а также SDK OxPHP\Profile\*, когда профилировщик встроен, — добавляются их плагинами во время инициализации модуля. Считайте этот список иллюстративным: точный набор и порядок зависят от того, какие плагины скомпилированы в сборку.
Совместимость с PHP-FPM
Если ваш код должен работать и на OxPHP, и на PHP-FPM, используйте обёртки-запасные варианты:
<?php
function finish_request(): bool
{
if (function_exists('oxphp_finish_request')) {
return oxphp_finish_request();
}
if (function_exists('fastcgi_finish_request')) {
return fastcgi_finish_request();
}
return false;
}
// Worker-aware bootstrap
if (function_exists('oxphp_is_worker') && oxphp_is_worker()) {
// OxPHP worker mode
} else {
// PHP-FPM or OxPHP traditional mode
}Семейство функций oxphp_async() всегда зарегистрировано в OxPHP, поэтому function_exists('oxphp_async') возвращает true, даже когда ASYNC_WORKERS=0. Когда пул отключён, вызов любой асинхронной функции выбрасывает OxPHP\Async\AsyncException. Если ваш код должен обрабатывать обе конфигурации, перехватывайте исключение, а не проверяйте function_exists().
См. также
- HTTP Request API -- объектно-ориентированный доступ к данным запроса через
oxphp_http_request() - Режим воркеров -- постоянный цикл воркера и жизненный цикл запроса
- Server-Sent Events -- потоковая передача в реальном времени с
oxphp_stream_flush() - Ранний ответ -- фоновая обработка с
oxphp_finish_request() - Суперглобальные переменные -- как OxPHP заполняет
$_SERVER,$_GET,$_POSTи другие суперглобальные переменные - Распределённая трассировка и APM -- W3C Trace Context, экспорт OTel и SDK
oxphp_apm_*() - Справочник по конфигурации --
WORKER_MODE_ENABLED,ENTRY_FILE,PHP_WORKERSи другие переменные окружения