phpsoftbox / http-emitter
HTTP response emitter for the PhpSoftBox framework
Requires
- php: ^8.5
- psr/http-message: ^2.0
Requires (Dev)
- phpsoftbox/cs-fixer: ^1.2.2
- phpsoftbox/http-message: ^1.0
- phpunit/phpunit: ^13.3
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-29 11:53:23 UTC
README
Компонент выводит готовый PSR-7 response в PHP SAPI. Он устанавливает status line и заголовки, после чего потоково передаёт body клиенту.
Использование
use PhpSoftBox\Http\Emitter\SapiEmitter; $emitter = new SapiEmitter(); $emitter->emit($response);
Вызов emit() должен быть последним этапом обработки запроса. До него приложение не
должно отправлять данные клиенту.
Статус и заголовки
Статус берётся из переданного response и сохраняется при отправке каждого заголовка.
Это важно, например, для Inertia-ответа 409 Conflict с заголовком Location: PHP не
заменит его автоматически на 302 Found.
Каждое значение PSR-7 заголовка отправляется отдельно. Поэтому несколько значений
Set-Cookie не объединяются через запятую.
Первое значение каждого заголовка заменяет одноимённый заголовок, который PHP выставил до
emit() (например, Content-Type по default_mimetype или header() в коде), остальные
значения добавляются. Исключение — Set-Cookie: его значения всегда добавляются, поэтому
cookie от setcookie() и session_start() не теряются.
Если PHP уже отправил заголовки, emitter выбросит
HeadersAlreadySentException с доступным местом первого вывода. Продолжать эмиссию в
такой ситуации небезопасно: status и заголовки response уже нельзя доставить клиенту.
Буфер вывода
Если перед emit() в буфере вывода (ob_start()) уже есть данные — echo, var_dump,
выведенный notice, — emitter выбрасывает OutputAlreadySentException и ничего не
отправляет. Иначе эти данные оказались бы в начале body и испортили бы ответ (например,
JSON). Буфер emitter не очищает: вывод остаётся доступен для диагностики. Уберите лишний
вывод или очистите буфер (ob_end_clean()) до вызова emit().
Body
Body читается из StreamInterface частями, поэтому файл или большой экспорт не
загружается в память целиком. Seekable stream перед чтением перематывается в начало;
non-seekable stream читается с текущей позиции.
Emitter не выводит body для статусов, которые его не допускают:
- информационные
1xx; 204 No Content;205 Reset Content;304 Not Modified.
HEAD
Emitter не знает метод исходного запроса, поэтому для ответа на HEAD вызывающий код
передаёт withoutBody: true:
$emitter->emit($response, withoutBody: $request->getMethod() === 'HEAD');
В этом режиме отправляются status line и все заголовки response как есть (включая
Content-Length, посчитанный для GET), а body не выводится. Router обслуживает HEAD
GET-маршрутом и тело не очищает, поэтому HTTP application должно передавать этот флаг
при эмиссии.