Функция headers_sent() в PHP: всё, что нужно знать
Узнайте, как использовать функцию headers_sent() в PHP, чтобы проверить, были ли уже отправлены HTTP-заголовки клиенту, и избежать распространённых ошибок.
Каждый HTTP-ответ состоит из двух частей: блока заголовков (код состояния, Content-Type, куки, редиректы) и тела ответа. Важно помнить, что заголовки должны быть отправлены до любого вывода тела. Как только PHP выводит хотя бы один байт тела, заголовки отправляются и блокируются — любой последующий вызов header(), setcookie() или session_start() вызовет печально известное предупреждение Cannot modify header information - headers already sent.
headers_sent() — это встроенная функция, которая позволяет проверить до попытки отправить заголовок, не стало ли это уже невозможным. В этом руководстве рассматриваются её синтаксис, параметры, передаваемые по ссылке, причины преждевременного вывода и способы их устранения.
Что делает headers_sent()
headers_sent() возвращает булево значение:
true— заголовки уже отправлены (вывод начался). Любой последующий вызовheader()/setcookie()завершится неудачей.false— вывод ещё не начался, поэтому заголовки можно устанавливать.
Защита логики заголовков с помощью headers_sent() позволяет избежать предупреждения и корректно обработать ситуацию (например, записать проблему в лог или выполнить JavaScript-редирект вместо HTTP) вместо того, чтобы страница «упала».
Синтаксис
headers_sent(string &$filename = null, int &$line = null): boolОба параметра необязательны и передаются по ссылке. Если заголовки уже отправлены, PHP заполняет их данными о том, где начался вывод:
$filename— имя исходного файла, в котором вывод начался впервые.$line— номер строки в этом файле.
Именно это делает headers_sent() такой полезной для отладки: она указывает прямо на источник проблемы.
Базовое использование
Проверьте возвращаемое значение перед отправкой заголовка:
<?php
if (!headers_sent()) {
header('Location: /dashboard');
exit;
}
echo 'Headers were already sent, cannot redirect via HTTP.';Если вывод ещё не начался, заголовок редиректа отправляется. В противном случае скрипт избегает фатального предупреждения и выводит резервное сообщение.
Определение места начала вывода
Передайте две переменные по ссылке, чтобы узнать точно, что отправило заголовки — это незаменимо, когда случайный пробел или echo скрыт в подключённом файле:
<?php
if (headers_sent($file, $line)) {
echo "Headers already sent in $file on line $line";
} else {
setcookie('theme', 'dark');
echo 'Cookie set successfully.';
}Если условие истинно, вы получите сообщение вида Headers already sent in /var/www/header.php on line 12, которое укажет точно, где искать проблему.
Что вызывает ошибку «Headers Already Sent»
Любой вывод тела до вызова заголовка приводит к их немедленной отправке. Распространённые причины:
- Пробел или пустая строка перед
<?php— даже один пробел или перенос строки считается выводом. - Перенос строки после закрывающего
?>подключённого файла. (Рекомендуется вообще опускать закрывающий?>в файлах, содержащих только PHP.) echo,print,printfилиvar_dump, запущенные до заголовка.- UTF-8-файл, сохранённый с BOM (метка порядка байтов) — эти невидимые ведущие байты являются выводом.
- PHP-предупреждения и уведомления, выводимые на страницу (когда включён
display_errors) до заголовков.
Исправление с помощью буферизации вывода
Если нельзя переупорядочить код так, чтобы все заголовки шли первыми, оберните скрипт в буфер вывода. ob_start() удерживает тело в памяти вместо немедленной отправки, поэтому заголовки остаются изменяемыми до сброса буфера:
<?php
ob_start(); // start buffering — nothing is sent yet
echo 'Some early output';
// Still safe: the echo above is held in the buffer, headers are not sent
setcookie('user', 'jane');
header('X-App-Version: 2.0');
ob_end_flush(); // now send headers, then the buffered bodyПоскольку вывод буферизован, headers_sent() вернёт false даже после echo, а последующие вызовы setcookie() и header() выполнятся успешно.
Связанные функции
header()— отправить сырой HTTP-заголовок.headers_list()— список заголовков, поставленных в очередь или уже отправленных.setcookie()— установить куки (отправляет заголовокSet-Cookie).ob_start()— начать буферизацию вывода, чтобы отложить отправку заголовков.- PHP Sessions —
session_start()тоже отправляет заголовки и часто становится источником проблемы.
Заключение
headers_sent() — небольшая, но важная защитная функция: вызывайте её перед любым header(), setcookie() или session_start(), чтобы проверить, не начался ли уже вывод. Когда она возвращает true, параметры по ссылке $filename и $line указывают точно на проблемный вывод, чтобы вы могли его исправить — или оберните скрипт в ob_start(), чтобы заголовки оставались изменяемыми до момента сброса буфера.