Ранний ответ
oxphp_finish_request() немедленно отправляет клиенту полный HTTP-ответ и позволяет вашему PHP-скрипту продолжить работу для выполнения фоновых задач. Это аналог fastcgi_finish_request() из PHP-FPM в OxPHP.
Как это работает
- Формирование ответа. Ваш скрипт как обычно устанавливает заголовки, код состояния и выводит тело ответа через echo.
- Завершение запроса. Вызовите
oxphp_finish_request(). OxPHP сбрасывает все буферы вывода, помечает запрос как завершённый и доставляет клиенту полный HTTP-ответ. - Выполнение фоновой работы. Скрипт продолжает выполняться для фоновых задач, таких как отправка писем, запись записей в кэш или рассылка вебхуков.
- Последующий вывод отбрасывается. Любой вывод, сформированный после вызова (
echo,print,var_dump), молча отбрасывается. - Вызов идемпотентен.
oxphp_finish_request()возвращаетtrueпри первом вызове иfalseпри любом последующем вызове в рамках того же запроса.
Сценарии использования
Ранний ответ полезен всякий раз, когда нужно немедленно подтвердить приём запроса, отложив некритичную работу:
- Отправка писем — сразу верните «принято» и отправьте письмо в фоне
- Прогрев кэша — ответьте закэшированными данными, а затем перегенерируйте запись кэша
- Аналитика и логирование — подтвердите запрос, а затем запишите подробные аналитические записи
- Рассылка вебхуков — подтвердите получение вызывающей стороне, а затем разошлите доставки вебхуков
- Обработка изображений — сразу верните URL, а затем обработайте полноразмерное изображение
Примеры на PHP
Базовое использование
<?php
header('Content-Type: application/json');
echo json_encode(['status' => 'accepted', 'id' => uniqid()]);
// Send response now — client receives the full response at this point
oxphp_finish_request();
// Background work runs here; client is no longer waiting
file_put_contents('/tmp/audit.log', date('c') . " request processed\n", FILE_APPEND);
send_notification_email($user);Защита от двойного вызова
oxphp_finish_request() возвращает false при втором и последующих вызовах. Проверяйте возвращаемое значение в приложениях с большим количеством middleware, где несколько слоёв могут вызвать эту функцию:
<?php
function finish_and_cleanup(): void
{
if (!oxphp_finish_request()) {
// Already finished — background work was already scheduled
return;
}
// First call — safe to run cleanup
flush_metrics_buffer();
close_external_connections();
}Условная фоновая работа
<?php
header('Content-Type: application/json');
$payload = json_decode(file_get_contents('php://input'), true);
$result = handle_request($payload);
echo json_encode($result);
if ($result['needs_sync']) {
oxphp_finish_request();
sync_to_external_service($result);
}
// No early finish if sync is not needed — script exits normallyРежим воркеров
В режиме воркеров PHP-воркер остаётся занятым до тех пор, пока не завершится весь скрипт (включая всю фоновую работу). Воркер не принимает новый запрос, пока не вернётся колбэк.
<?php
oxphp_worker(function () {
$order = json_decode(file_get_contents('php://input'), true);
$result = process_order($order);
header('Content-Type: application/json');
echo json_encode(['order_id' => $result['id'], 'status' => 'accepted']);
oxphp_finish_request();
// Worker is still occupied during this background work
send_confirmation_email($result);
update_inventory($result);
notify_warehouse($result);
// Worker becomes available after this point
});Учитывайте время фоновой обработки при определении размера пула воркеров. Воркер, тратящий 3 секунды на работу после ответа после каждого запроса, фактически обслуживает меньше конкурентных запросов.
Устранение неполадок
Фоновая работа не завершается
PHP-настройка max_execution_time продолжает действовать и после вызова oxphp_finish_request(). Если суммарное время выполнения скрипта, включая фоновую работу, превышает лимит, запрос отменяется с фатальной ошибкой Request cancelled (timeout).
Решение: увеличьте max_execution_time (в php.ini или через set_time_limit() из скрипта) либо переместите длительные фоновые задачи в очередь сообщений:
set_time_limit(300);
oxphp_finish_request();
// ... long-running work ...Для работы, которая регулярно занимает больше нескольких секунд, публикуйте сообщение в Redis, RabbitMQ или похожую очередь и позволяйте выделенному потребителю обрабатывать его асинхронно.
Сессию нельзя запустить после того, как ответ ушёл
oxphp_finish_request() отправляет ответ в том виде, в каком он есть (код состояния, заголовки и тело), и всё, что добавлено в него позже, уже не отправляется. Отправка ответа означает и то, что заголовки уже отправлены, а после этого PHP отказывается запускать сессию: session_start() выдаёт Session cannot be started after headers have already been sent и возвращает false, и session_regenerate_id() отказывает точно так же. Сессия не запускается вовсе, поэтому записи в $_SESSION после этого момента попадают в обычный массив, который никто не сохраняет. Во фреймворке, превращающем предупреждения в исключения, этот отказ становится исключением, выброшенным внутри запроса, на который уже ответили.
Решение: запускайте сессию до вызова и закрывайте её тоже до вызова, чтобы запись и снятие блокировки происходили в момент, который вы контролируете:
<?php
session_start(); // Before the response goes out, not after
$_SESSION['last_seen'] = time();
session_write_close(); // Persist the session and release the save handler's lock
oxphp_finish_request(); // Send response
// ... work that no longer has a session ...В режиме воркеров так стоит делать независимо от того, завершается ли запрос досрочно. Воркер закрывает сессию в конце запроса, только если ни один другой обслуживаемый им запрос не находится в этой сессии; общая сессия закрывается лишь тогда, когда у воркера в следующий раз появится свободная минута, чтобы её вернуть. До тех пор обработчик сохранения продолжает удерживать то, что заблокировал, а у обработчика по умолчанию это эксклюзивная блокировка, из-за которой никто другой не может прочитать эту сессию. Полное описание этой границы — в разделе Режим воркеров.
Тело ответа пустое после вызова oxphp_finish_request()
Если вы вызываете oxphp_finish_request() до какого-либо вывода через echo, клиент получает пустое тело. Сначала сформируйте и выведите ответ, а затем вызывайте функцию.
Примечания
oxphp_finish_request()возвращаетtrueпри первом вызове иfalseпри последующих вызовах в рамках того же запроса.- Весь вывод (
echo,print,var_dump) после первого вызова молча отбрасывается. - В режиме воркеров воркер остаётся занятым до полного завершения колбэка, включая весь код после ответа.
- Таймаут запроса продолжает применяться к фоновому коду, выполняемому после
oxphp_finish_request(). oxphp_finish_request()иoxphp_stream_flush()взаимоисключающи: вызовoxphp_finish_request()до начала стрима не даёт запустить стриминг, а вызов послеoxphp_stream_flush()закрывает стрим.
Промпты для вашего агента
Эти промпты рассчитаны на агента с подключённым MCP-сервером документации OxPHP — нужные страницы он прочитает сам.
Убери работу, которая идёт после ответа, за `oxphp_finish_request()`. Прочитай страницу `features/early-response` через MCP-сервер документации OxPHP.
Найди в этом репозитории работу, которую клиент ждёт, но которая ему не нужна (письма, рассылку вебхуков, запись аналитики), и покажи эти обработчики переписанными так, чтобы ответ уходил первым, а любая сессия запускалась и закрывалась до вызова.См. также
- Режим воркеров — постоянные PHP-воркеры и то, как ранний ответ взаимодействует с циклом обработки запросов
- Таймауты — как таймаут запроса применяется к фоновой работе
- PHP-функции — полный справочник по
oxphp_finish_request()и другим встроенным функциям