Нет вывода перед отправкой заголовков!
Функции, которые отправляют / изменяют заголовки HTTP, должны быть вызваны перед выполнением любого вывода .
summary ⇊
В противном случае вызов не выполняется:
Предупреждение: невозможно изменить информацию заголовка - заголовки уже отправлены (вывод начат в script: line )
Некоторые функции, изменяющие заголовок HTTP:
Выход может быть:
Непреднамеренное:
- Пробелы до
<?php
или после?>
- Порядок байтов UTF-8 специально
- Предыдущие сообщения об ошибках или уведомления
Преднамеренное:
print
, echo
И другие функции получения выходных
- Исходный код необработанных
<html>
разделов<?php
Почему это происходит?
Чтобы понять, почему заголовки должны отправляться перед выводом, необходимо взглянуть на типичный HTTP-
ответ. Сценарии PHP в основном генерируют контент HTML, но также передают набор заголовков HTTP / CGI веб-серверу:
HTTP/1.1 200 OK
Powered-By: PHP/5.3.7
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
<html><head><title>PHP page output page</title></head>
<body><h1>Content</h1> <p>Some more output follows...</p>
and <a href="/"> <img src=internal-icon-delayed> </a>
Страница / вывод всегда следует за заголовками. PHP должен сначала передать заголовки веб-серверу. Это можно сделать только один раз. После двойного разрыва он больше не сможет их исправить.
Когда PHP получает первый выход ( print
, echo
, <html>
) он будет
смывать все собранные заголовки. После этого он может отправить все выходные данные, которые он хочет. Но отправка дальнейших заголовков HTTP невозможна.
Как узнать, где произошел преждевременный выход?
header()
Предупреждение содержит всю необходимую информацию , чтобы найти причину проблемы:
Предупреждение: невозможно изменить информацию заголовка - заголовки уже отправлены
(вывод начался с / www / usr2345 / htdocs / auth.php: 52 ) в /www/usr2345/htdocs/index.php в строке 100
Здесь «строка 100» относится к сценарию, где header()
вызов не удался.
Примечание « вывод начался с » в скобках является более значимым. Он обозначает источник предыдущего вывода. В этом примере это auth.php
и строка52
. Вот где вам пришлось искать преждевременный выход.
Типичные причины:
Печать, эхо
Преднамеренное выход из print
и echo
заявления прервет возможность отправлять HTTP заголовки. Поток приложений должен быть реструктурирован, чтобы избежать этого. Используйте функции
и шаблонные схемы. Убедитесь, что header()
звонки происходят до сообщения будут записаны.
Функции, которые производят вывод, включают
print
, echo
, printf
,vprintf
trigger_error
, ob_flush
, ob_end_flush
, var_dump
,print_r
readfile
, passthru
, flush
, imagepng
,imagejpeg
среди других и пользовательских функций.
Необработанные области HTML
Непарсированные разделы HTML в .php
файле также являются прямым выводом. Условия сценария, которые инициируют header()
вызов, должны быть записаны перед любыми необработанными <html>
блоками.
<!DOCTYPE html>
<?php
// Too late for headers already.
Используйте шаблонную схему для отделения обработки от логики вывода.
- Поместите код обработки форм поверх скриптов.
- Используйте временные строковые переменные для отсрочки сообщений.
- Фактическая логика вывода и смешанный вывод HTML должны следовать последним.
Пробел перед <?php
для "script.php строки 1 предупреждением "
Если предупреждение относится к выводу в строке 1
, то перед открывающим токеном в основном идут пробелы , текст или HTML <?php
.
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
Аналогичным образом это может происходить для добавленных сценариев или разделов сценариев:
?>
<?php
PHP фактически съедает один перевод строки после закрывающих тегов. Но это не компенсирует множественные новые строки или табуляции или пробелы, сдвинутые в такие пробелы.
UTF-8 BOM
Только разрывы строк и пробелы могут быть проблемой. Но есть также «невидимые» последовательности символов, которые могут вызвать это. Наиболее известным является
UTF-8 BOM (Byte-Order-Mark),
который не отображается большинством текстовых редакторов. Это последовательность байтов EF BB BF
, которая является необязательной и избыточной для документов в кодировке UTF-8. PHP, однако, должен воспринимать это как необработанный вывод. Может отображаться как персонажи
в выходных данных (если клиент интерпретирует документ как Latin-1) или как подобный «мусор».
В частности, графические редакторы и IDE на основе Java не замечают его присутствия. Они не визуализируют это (обязано стандартом Unicode). Большинство программистов и консольных редакторов, однако, делают:
Там легко распознать проблему на ранней стадии. Другие редакторы могут идентифицировать его присутствие в файле / меню настроек (Notepad ++ в Windows может идентифицировать и
устранить проблему ). Другой вариант проверки наличия спецификаций - использование шестигранника . В системах * nix hexdump
обычно доступен, если не графический вариант, который упрощает аудит этих и других проблем:
Простое решение - настроить текстовый редактор для сохранения файлов как «UTF-8 (без спецификации)» или подобной номенклатуры. Часто новички в противном случае прибегают к созданию новых файлов и просто копируют и вставляют предыдущий код обратно.
Коррекция коммунальных услуг
Есть также автоматизированные инструменты для проверки и перезаписи текстовых файлов ( sed
/awk
или recode
). Для PHP, в частности, есть phptags
тег Tidier . Он переписывает закрытые и открытые теги в длинные и короткие формы, а также легко исправляет начальные и конечные пробелы, проблемы Unicode и UTF-x BOM:
phptags --whitespace *.php
Разумно использовать весь каталог include или проекта.
Пробел после ?>
Если источник ошибки упоминается как
закрытие?>
то это то место, где записывается какой-то пробел или необработанный текст. Маркер конца PHP не останавливает выполнение скрипта на этом этапе. Любые текстовые / пробельные символы после этого будут записаны как содержимое страницы.
Обычно советуют, в частности, новичкам, что конечные ?>
теги закрытия PHP должны быть опущены. Это избегает небольшой части этих случаев. (Довольно часто include()d
сценарии являются виновниками.)
Источник ошибки, указанный как «Неизвестный в строке 0»
Обычно это расширение PHP или параметр php.ini, если источник ошибок не конкретизирован.
- Иногда это
gzip
настройка кодирования потока
илиob_gzhandler
.
- Но это также может быть любой дважды загруженный
extension=
модуль, генерирующий неявное сообщение запуска / предупреждения PHP.
Предыдущие сообщения об ошибках
Если другой оператор или выражение PHP вызывает вывод предупреждающего сообщения или уведомления, это также считается преждевременным выводом.
В этом случае вам необходимо избежать ошибки, отложить выполнение оператора или подавить сообщение с помощью, например,
isset()
или @()
- когда один из них не препятствует отладке позже.
Нет сообщения об ошибке
Если у вас есть error_reporting
или display_errors
отключен php.ini
, то предупреждение не будет отображаться. Но игнорирование ошибок не устранит проблему. Заголовки все еще не могут быть отправлены после преждевременного вывода.
Поэтому, когда header("Location: ...")
перенаправления молча терпят неудачу, очень желательно проверять наличие предупреждений. Включите их двумя простыми командами поверх скрипта вызова:
error_reporting(E_ALL);
ini_set("display_errors", 1);
Или set_error_handler("var_dump");
если все остальное терпит неудачу.
Говоря о заголовках перенаправления, вы часто должны использовать такую идиому для окончательных путей кода:
exit(header("Location: /finished.html"));
Желательно даже служебную функцию, которая печатает пользовательское сообщение в случае header()
сбоев.
Буферизация вывода в качестве обходного пути
Буферизация вывода PHP
- это обходной путь для решения этой проблемы. Это часто работает надежно, но не должно заменять правильное структурирование приложения и отделение вывода от логики управления. Его реальная цель - минимизировать частичные переводы на веб-сервер.
output_buffering=
Параметр , тем не менее может помочь. Настройте его в php.ini
или через .htaccess
или даже .user.ini для современных установок FPM / FastCGI.
Включение этого позволит PHP буферизировать вывод вместо того, чтобы мгновенно передавать его веб-серверу. Таким образом, PHP может собирать заголовки HTTP.
Он также может быть задействован с вызовом ob_start();
поверх сценария вызова. Что, однако, менее надежно по нескольким причинам:
Даже если <?php ob_start(); ?>
первый скрипт запускается, пробелы или спецификация могут быть перетасованы ранее, что делает его неэффективным .
Он может скрывать пробелы для вывода HTML. Но как только логика приложения пытается отправить двоичный контент (например, сгенерированное изображение), буферизованный посторонний вывод становится проблемой. (Необходим в ob_clean()
качестве дальнейшего решения.)
Размер буфера ограничен, и его можно легко переполнить, если оставить значение по умолчанию. И это тоже не редкость, трудно отследить,
когда это произойдет.
Поэтому оба подхода могут стать ненадежными - в частности, при переключении между настройками разработки и / или производственными серверами. Вот почему выходная буферизация широко считается просто костыль / строго обходной путь.
См. Также базовый пример использования
в руководстве, а также дополнительные плюсы и минусы:
Но это работает на другом сервере !?
Если вы раньше не получали предупреждение о заголовках, тогда настройка выходной буферизации php.ini
изменилась . Вероятно, он не настроен на текущем / новом сервере.
Проверка с headers_sent()
Вы всегда можете использовать headers_sent()
для проверки, если все еще возможно ... отправить заголовки. Что полезно для условной печати информации или применения другой резервной логики.
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
Полезные обходные пути:
HTML <meta>
тег
Если ваше приложение структурно сложно исправить, то простым (но несколько непрофессиональным) способом разрешить перенаправления является внедрение HTML-
<meta>
тега. Перенаправление может быть достигнуто с помощью:
<meta http-equiv="Location" content="http://example.com/">
Или с небольшой задержкой:
<meta http-equiv="Refresh" content="2; url=../target.html">
Это приводит к недействительному HTML при использовании за пределами <head>
раздела. Большинство браузеров все еще принимают это.
JavaScript перенаправить
В качестве альтернативы перенаправления JavaScript
могут быть использованы для перенаправления страниц:
<script> location.replace("target.html"); </script>
Несмотря на то, что это часто более совместимо с HTML, чем <meta>
обходной путь, в нем используются клиенты с поддержкой JavaScript.
Оба подхода, однако, делают приемлемые откаты при сбое подлинных вызовов HTTP header (). В идеале вы всегда должны сочетать это с удобным сообщением и кликабельной ссылкой в качестве крайней меры. (Что, например, то, что http_redirect ()
расширение PECL .)
Почему setcookie()
и session_start()
на что тоже влияют
И то setcookie()
и другое session_start()
нужно отправить Set-Cookie:
HTTP-заголовок. Поэтому применяются те же условия, и аналогичные сообщения об ошибках будут генерироваться для преждевременных выходных ситуаций.
(Конечно, на них, кроме того, влияют отключенные файлы cookie в браузере или даже проблемы с прокси. Функциональность сеанса, очевидно, также зависит от свободного места на диске и других настроек php.ini и т. Д.)
Дальнейшие ссылки