Немає результатів перед надсиланням заголовків!
Функції, що надсилають / змінюють заголовки HTTP, потрібно викликати перед тим, як буде зроблено будь-який вихід .
summary ⇊
Інакше виклик не вдасться:
Попередження: Неможливо змінити інформацію заголовка - заголовки вже надіслані (вихід запущений у скрипті: рядок )
Деякі функції, що змінюють заголовок 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 має сприймати це як вихідний вихід. Він може відображатися як символи 
у висновку (якщо клієнт інтерпретує документ як латинське-1) або подібний "сміття".
Зокрема, графічні редактори та IDE на базі Java не враховують його наявність. Вони не візуалізують його (зобов'язаний стандартом Unicode). Однак більшість редакторів і консольних програм:
Там легко розпізнати проблему на початку. Інші редактори можуть визначити його присутність у меню файлів / налаштувань (Блокнот ++ у Windows може визначити та
усунути проблему ). Інший варіант перевірки присутності BOM - це звернення до гексексидатора . У системах * nix hexdump
зазвичай доступний, якщо не графічний варіант, який спрощує аудит цих та інших питань:
Просте виправлення - встановити текстовий редактор для збереження файлів як "UTF-8 (без BOM)" або подібної номенклатури. Часто новачки в іншому випадку вдаються до створення нових файлів і просто копіюють і вставляють попередній код назад.
Корекційні утиліти
Існують також автоматизовані інструменти для вивчення та переписування текстових файлів ( sed
/awk
або recode
). Спеціально для PHP є phptags
тег tidier . Він переписує тісні та відкриті теги у довгі та короткі форми, але також легко виправляє провідні та кінцеві пробіли, проблеми Unicode та UTF-x BOM:
phptags --whitespace *.php
Це розумно використовувати в цілому, включаючи каталог або проект.
Пробіл після ?>
Якщо джерело помилок згадується як за
закриттям,?>
то тут виписано деякий пробіл або неочищений текст. Кінцевий маркер 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(); ?>
запускається перший сценарій, пробіли або BOM можуть раніше переміщуватися, що робить його неефективним .
Він може приховати пробіли для виводу 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"));
}
Корисними шляхами резервного вирішення є:
<meta>
Тег HTML
Якщо ваш додаток структурно важко виправити, то простим (але дещо непрофесійним) способом дозволити переадресацію є введення <meta>
тегу HTML
. Перенаправлення можна досягти за допомогою:
<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-заголовка () не вдається. В ідеалі ви завжди поєднуєте це із зручним для користувача повідомленням та посиланням, яке можна натиснути, як останню інстанцію. (Що, наприклад,
робить розширення PECL http_redirect () .)
Чому setcookie()
і session_start()
теж впливають
І те setcookie()
й session_start()
інше потрібно надіслати Set-Cookie:
заголовок HTTP. Тому застосовуються ті самі умови, і подібні повідомлення про помилки будуть генеруватися для передчасних вихідних ситуацій.
(Звичайно, на них також впливають відключені файли cookie у браузері або навіть проблеми з проксі-серверами. Функціонал сеансу, очевидно, також залежить від вільного місця на диску та інших параметрів php.ini тощо).
Подальші посилання