Search by

phpsoftbox / http-emitter

inspector-who

HTTP response emitter for the PhpSoftBox framework

Package info

github.com/phpsoftbox/http-emitter

pkg:composer/phpsoftbox/http-emitter

Statistics

Installs: 244

Dependents: 2

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-09-29 10:24 UTC

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 должно передавать этот флаг при эмиссии.