Headers sent: примеры (PHP)
headers_sent(&$filename = null, &$line = null): boolФункция headers_sent в PHP
Функция headers_sent() в языке PHP проверяет, были ли уже отправлены HTTP-заголовки клиенту. Её основное применение - предотвращение ошибок, связанных с попыткой изменить заголовки после их отправки, что в протоколе HTTP не допускается. Эта функция часто используется перед вызовом header(), setcookie() или session_start().
Функция поддерживает два необязательных параметра:
- $filename (string) - если заголовки уже отправлены, в эту переменную будет записано имя файла скрипта, в котором это произошло.
- $line (int) - если заголовки отправлены, в эту переменную будет записан номер строки в файле, на которой это произошло.
Возвращаемое значение: bool - true, если заголовки отправлены, false в противном случае.
Проверка возможности отправить заголовок:
<?php
if (!headers_sent()) {
header('Location: /newpage.php');
exit;
} else {
echo 'Заголовки уже отправлены. Перенаправление невозможно.';
}
?>Заголовки уже отправлены. Перенаправление невозможно.
<?php
// Попытка отправить cookie после вывода
echo 'Некоторый вывод';
if (headers_sent($file, $line)) {
echo "Заголовки отправлены в файле $file на строке $line";
} else {
setcookie('test', 'value');
}
?>Некоторый вывод Заголовки отправлены в файле /var/www/script.php на строке 3
- header() - непосредственно отправляет HTTP-заголовок.
headers_sent()часто используется как условие перед вызовом этой функции. - ob_start() - включает буферизацию вывода, что позволяет откладывать отправку заголовков до момента вывода всего контента. Предпочтительнее для сложных сценариев, где возможен ранний вывод.
- session_start() - также отправляет заголовки (cookie сессии). Перед её вызовом в существующем коде иногда проверяют
headers_sent().
Функцию headers_sent() используют, когда нужно выполнить простое условие. Буферизацию вывода применяют для более полного контроля над потоком вывода.
В веб-фреймворках Python заголовки обычно устанавливаются до начала тела ответа, но явной проверки нет. Фреймворк контролирует этот процесс.
# Flask пример
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/')
def index():
response = make_response('Hello')
response.headers['X-Custom'] = 'Value' # Заголовки можно менять до возврата
return response// Express.js пример
app.get('/', (req, res) => {
// Проверка, отправлены ли заголовки
if (res.headersSent) {
console.log('Заголовки уже отправлены');
} else {
res.setHeader('Content-Type', 'text/html');
res.send('Hello');
}
});В отличие от PHP, где проверка глобальная, в Node.js свойство headersSent относится к конкретному объекту ответа. В Python типичный паттерн предполагает формирование объекта ответа с заголовками до отправки.
<?php
// ОШИБКА: Логическая ошибка в условии
if (headers_sent()) {
header('Content-Type: application/json'); // Попытка отправить после отправки
}
?>Warning: Cannot modify header information - headers already sent
<?php
echo 'Старт';
// Любой вывод, включая пробелы до <?php или после ?> в included файлах,
// может вызвать отправку заголовков.
if (!headers_sent()) { // Условие уже не выполнится
setcookie('name', 'value');
}
?>Старт Warning: Cannot modify header information - headers already sent
В PHP 8 не было внесено значительных изменений в поведение или сигнатуру функции headers_sent(). Функция сохраняет обратную совместимость с предыдущими версиями языка. Однако, в PHP 8.1 были улучшены некоторые внутренние механизмы обработки заголовков, но это не повлияло на внешний API функции.
<?php
ob_start(); // Включаем буферизацию
// ... сложная логика с потенциальным выводом ...
echo 'Потенциально ранний вывод';
// Проверяем, не было ли вывода до ob_start()
if (headers_sent()) {
echo '<div class="error">Критическая ошибка конфигурации</div>';
} else {
// Безопасно отправляем заголовки, так как вывод в буфере
header('Cache-Control: no-cache');
setcookie('session', 'token');
ob_end_flush(); // Отправляем вывод
}
?><?php
function safe_header($header, $value) {
if (!headers_sent($file, $line)) {
header("$header: $value");
return true;
} else {
error_log("Попытка отправить заголовок $header после отправки в $file:$line");
return false;
}
}
// Использование
safe_header('Content-Type', 'text/html');
?><?php
class HeadersSentException extends Exception {}
function require_headers_not_sent() {
if (headers_sent($file, $line)) {
throw new HeadersSentException("Заголовки отправлены в $file:$line", $line);
}
}
try {
require_headers_not_sent();
header('Location: /secure');
} catch (HeadersSentException $e) {
// Альтернативная логика без перенаправления
echo '<meta http-equiv="refresh" content="0;url=/secure">';
}
?>