Ответ 1
Нет вывода перед отправкой заголовков!
Функции, отправляющие/изменяющие HTTP-заголовки, должны быть вызваны до того, как будет выполнен какой-либо вывод. резюме < В противном случае вызов завершается с ошибкой:
Предупреждение: невозможно изменить информацию заголовка - уже отправленные заголовки (вывод начался с script: строка)
Некоторые функции, изменяющие HTTP-заголовок:
Вывод может быть:
-
Unintentional:
- Пробелы перед
<?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/usr 2345/htdocs/index.php в строке 100
Здесь "строка 100" относится к script, где не удалось выполнить вызов 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
также являются прямым выходом. Script должны быть отмечены условия, вызывающие вызовheader()
перед любыми необработанными блоками<html>
.<!DOCTYPE html> <?php // Too late for headers already.
Используйте схему шаблонов, чтобы отделить обработку от логики вывода.
- Обработать код обработки формы на скриптах поверх.
- Используйте временные строковые переменные для отсрочки сообщений.
- Фактическая логика вывода и смешанный вывод HTML должны соответствовать последним.
-
Пробелы перед
<?php
для "script.php строка 1" предупрежденияЕсли предупреждение относится к выходу в строке
1
, то это в основном пробелы, текст или HTML перед открывающим токеном<?php
.<?php # There a SINGLE space/newline before <? - Which already seals it.
Аналогично это может произойти для добавленных скриптов или script разделов:
?> <?php
PHP на самом деле поглощает одну строку после тегов. Но это не будет компенсировать несколько новых строк или вкладок или пробелов, сдвинутых в такие промежутки.
-
Спецификация UTF-8
Одной из проблем может быть только односторонняя линия и пространство. Но есть и "невидимые" последовательности символов, которые могут вызвать это. Самый классный Спецификация UTF-8 (байт-заказ-марка)который не отображается большинством текстовых редакторов. Это байтовая последовательность
EF BB BF
, которая является необязательным и избыточным для кодированных документов UTF-8. Однако PHP должен лечить это как сырой выход. Он может отображаться как символы
на выходе (если клиент интерпретирует документ как латинский-1) или аналогичный "мусор".В частности, графические редакторы и Java-IDE не обращают внимания на свои присутствие. Они не визуализируют его (обязаны стандартом Unicode). Однако большинство программистов и консольных редакторов:
Там легко понять проблему на ранней стадии. Другие редакторы могут идентифицировать его присутствие в меню файлов/настроек (Notepad ++ в Windows может идентифицировать и устранить проблему), Другой способ проверки присутствия BOM прибегает к hexeditor. В системах * nix
hexdump
обычно доступен, если не графический вариант, который упрощает проверку этих и других проблем:Легкое исправление заключается в том, чтобы установить текстовый редактор для сохранения файлов как "UTF-8 (no BOM)" или подобная такая номенклатура. Зачастую новички прибегают к созданию новых файлы и просто скопировать и снова вставить предыдущий код.
Утилиты исправления
Существуют также автоматизированные инструменты для проверки и перезаписи текстовых файлов (
sed
/awk
илиrecode
). Для PHP специально существуетphptags
tag tidier. Он переписывает закрытые и открытые теги в длинные и короткие формы, но также легко исправляет начальные и конечные пробелы, проблемы с Unicode и UTF-x:phptags --whitespace *.php
Это разумно использовать в целом include или каталог проекта.
-
Пробелы после
?>
Если источник ошибки упоминается как за закрытие
?>
то здесь выписывается какой-то пропущенный или необработанный текст. Маркер конца PHP не завершает выполнение script при этом точка. Любые символы текста/пробела после того, как они будут выписаны как содержимое страницы до сих пор.Общепринято, в частности, новичкам, что trailing
?>
PHP теги close должны быть опущены. Это предотвращает небольшую часть этих случаев. (Чаще всего сценарииinclude()d
являются виновниками.) -
Источник ошибки, указанный как "Неизвестно в строке 0"
Обычно это расширение PHP или php.ini, если источник ошибок конкретизируется.
- Иногда параметр настройки потока
gzip
илиob_gzhandler
. - Но это может быть и любой загруженный в два раза модуль
extension=
генерирование неявного сообщения о запуске/предупреждении PHP.
- Иногда параметр настройки потока
-
Предыдущие сообщения об ошибках
Если другой оператор или выражение PHP вызывает предупреждение или уведомление распечатывается, что также считается преждевременным выходом.
В этом случае вам нужно избежать ошибки, задержать выполнение оператора или подавить сообщение, например.
isset()
или@()
- когда либо не препятствует отладке позже.
Нет сообщения об ошибке
Если у вас error_reporting
или display_errors
отключено на php.ini
,
то предупреждение не появится. Но игнорирование ошибок не вызовет проблемы
далеко. Заголовки по-прежнему не могут быть отправлены после преждевременного выхода.
Поэтому, когда header("Location: ...")
перенаправляет молча,
рекомендуется проконтролировать предупреждения. Обозначить их двумя простыми командами
на вызов script:
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 on современные установки FPM/FastCGI.
Включение этого режима позволит PHP выводить выходные данные вместо того, чтобы передавать его на веб-сервер мгновенно. Таким образом, PHP может собирать заголовки HTTP. -
Он также может быть связан с вызовом
ob_start();
на вершине вызова script. Это, однако, менее надежно по нескольким причинам:-
Даже если
<?php ob_start(); ?>
запускает первый script, пробел или Спецификация может быть перетасована раньше, делает ее неэффективной. -
Он может скрывать пробелы для вывода HTML. Но как только приложение логические попытки отправить двоичный контент (например, сгенерированное изображение), буферизованный посторонний выход становится проблемой. (Необходимость
ob_clean()
как предыдущий вариант.) -
Буфер ограничен по размеру и может быть легко переполнен, если оставить его по умолчанию. И это не редкость, трудно отследить когда это произойдет.
-
Оба подхода могут стать ненадежными - в частности, при переключении между разработки и/или производственные серверы. Именно поэтому буферизация вывода широко считается просто костылем/строго обходным путем.
См. также базовый пример использования в руководстве, и для большего количества плюсов и минусов:
- Что такое буферизация вывода?
- Зачем использовать буферизацию вывода в PHP?
- Является ли использование буферизации вывода считающейся плохой практикой?
- Использовать регистр для буферизации вывода как правильное решение для уже отправленных заголовков
Но он работал на другом сервере!?
Если вы не получили предупреждения заголовков раньше, то выходная буферизация Настройка 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-заголовок() вызовы не выполняются. В идеале вы всегда должны сочетать это с удобным для пользователя сообщением и в качестве последней инстанции. (Например, это http_redirect() Расширение PECL делает.)
Почему setcookie()
и session_start()
также затронуты
Оба setcookie()
и session_start()
должны отправить HTTP-заголовок Set-Cookie:
.
Таким образом, применяются те же условия, и аналогичные сообщения об ошибках будут сгенерированы
для случаев преждевременного выхода.
(Конечно, в браузере, кроме того, они подвержены воздействию файлов cookie, или даже проблемы с прокси. Функциональность сеанса, очевидно, также зависит от дисковое пространство и другие настройки php.ini и т.д.)
Дальнейшие ссылки
- Google предоставляет длинный список подобных обсуждений.
- И, конечно, многие конкретные случаи были также рассмотрены в переполнении стека.
- В Wordpress часто задаются вопросы Как решить проблему, уже отправленную предупреждением? в общем виде.
- Сообщество Adobe: PHP-разработка: почему перенаправления не работают (уже отправленные заголовки)
- Часто задаваемые вопросы о нуклеусе: Что означает "заголовки страниц уже отправлены" ?
- Одним из более подробных объяснений является HTTP-заголовки и функция заголовка PHP() - учебник NicholasSolutions (ссылка на интернет-архив). Он подробно описывает HTTP и дает несколько рекомендаций по переписыванию скриптов.