Учусь писать плагины. При активации на Вордпресс вылезает сообщение
Плагин произвёл при активации 1444 символов неожиданного вывода. Если вы заметите ошибку «headers already sent», проблемы с RSS-лентами или другие неполадки, попробуйте деактивировать или удалить этот плагин.
Вот код плагина:
<?php
/*
Plugin Name: colors-woobler
Plugin URI: http://www.woobler.ru
Description: Оформление текста
Version: 0.1
License: GPLv2
Author: Татьяна
Author URI: http://www.woobler.ru
*/
function podpis() {
return '<div class="podpis"><p>С уважением</p><p>Команда woobler.ru</p></div>';
}
add_shortcode('podpis', 'podpis');
function green1() {
return '<div id="green">';
}
add_shortcode('green1', 'green1');
function green0() {
return '</div>';
}
add_shortcode('green0', 'green0');
function blue1() {
return '<div id="blue">';
}
add_shortcode('blue1', 'blue1');
function blue0() {
return '</div>';
}
add_shortcode('blue0', 'blue0');
function yellow1() {
return '<div id="yellow">';
}
add_shortcode('yellow1', 'yellow1');
function yellow0() {
return '</div>';
}
add_shortcode('yellow0', 'yellow0');
function red1() {
return '<div id="red">';
}
add_shortcode('red1', 'red1');
function red0() {
return '</div>';
}
add_shortcode('red0', 'red0');
function magenta1() {
return '<div id="magenta">';
}
add_shortcode('magenta1', 'magenta1');
function magenta0() {
return '</div>';
}
add_shortcode('magenta0', 'magenta0');
function silver1() {
return '<div id="silver">';
}
add_shortcode('silver1', 'silver1');
function silver0() {
return '</div>';
}
add_shortcode('silver0', 'silver0');
?>
И там же стили (все в одном файле)
<style>
.podpis {
text-align: right;
padding-right: 20px;
border-radius: 11px;
font-family: cursive;
}
#blue
{
background: linear-gradient(0deg, #E8FFFE, #E8FFEB);
border-top: 1px solid #B3DCB1;
border-bottom: 1px solid #B7D5D8;
margin-top: 15px;
padding: 10px;
margin-bottom: 15px;
}
#yellow
{
background: linear-gradient(0deg, #FFFECD, #FFFDE2);
border-top: 1px solid #FBE98B;
border-bottom: 1px solid #FFDA72;
margin-top: 15px;
padding: 10px;
margin-bottom: 15px;
}
#red
{
background: linear-gradient(0deg, rgba(230, 0, 29, 0.2), rgba(253, 0, 0, 0.09));
border-top: 1px solid rgba(255, 27, 2, 0.33);
border-bottom: 1px solid rgba(255, 23, 16, 0.64);
margin-top: 15px;
padding: 10px;
margin-bottom: 15px;
}
#magenta
{
background: linear-gradient(0deg, rgba(209, 193, 255, 0.77), rgba(184, 69, 255, 0.16));
border-top: 1px solid rgba(209, 139, 251, 0.89);
border-bottom: 1px solid rgba(192, 92, 255, 0.84);
margin-top: 15px;
padding: 10px;
margin-bottom: 15px;
}
#silver
{
background: linear-gradient(0deg, rgba(224, 224, 224, 0.77), rgba(218, 218, 218, 0.16));
border-top: 1px solid rgba(142, 142, 142, 0.36);
border-bottom: 1px solid rgba(136, 136, 136, 0.6);
margin-top: 15px;
padding: 10px;
margin-bottom: 15px;
}
</style>
Подскажите, в чем ошибка, и куда надо смотреть чтобы разобраться?
Функционально все работает.
От автора: в этом уроке мы рассмотрим, пожалуй, одну из наиболее распространенных ошибок, с которой сталкиваются при программировании на PHP. Эта ошибка возникает при работе с функциями, связанными с отправкой заголовков. При этом мы можем столкнуться с несколькими вариантами сообщений об ошибке, но причина у них будет одинакова – заголовки уже отправлены (headers already sent).
В видео версии урока вы найдете подробное описание вариантов возникновения данной ошибки. Здесь же мы сформулируем вкратце причины и способы решения данной ошибки.
Итак, ошибка эта может возникнуть в случаях, когда в коде вы используете одну из функций, связанных с отправкой заголовков: header(), session_start(), setcookie().
Профессия PHP-разработчик с нуля до PRO
Готовим PHP-разработчиков с нуля
Вы с нуля научитесь программировать сайты и веб-приложения на PHP, освоите фреймворк
Laravel, напишете облачное хранилище и поработаете над интернет-магазином в команде.
Сможете устроиться на позицию Junior-разработчика.
Узнать подробнее
Командная стажировка под руководством тимлида
90 000 рублей средняя зарплата PHP-разработчика
3 проекта в портфолио для старта карьеры
Причина ошибки заключается в том, что перед данными функциями в коде где-то есть вывод в браузер. При этом выводом в браузер считается любой символ, даже пробел или перенос строки, которые можно увидеть в исходном коде.
Соответственно, решение проблемы заключается в том, что этот вывод нужно убрать. Варианты вывода могут быть следующими:
собственно вывод, который прописан в коде: это может быть пробел, перенос строки, HTML-код и т.д.
вывод в подключаемых файлах
сохранение документа в кодировке UTF-8 с BOM. Это наиболее неочевидная проблема и здесь стоит помнить, что при использовании юникода файл должен быть сохранен в кодировке UTF-8 без BOM.
На этом мы завершим текущий урок. Удачи и до новых встреч!
No output before sending headers!
Functions that send/modify HTTP headers must be invoked before any output is made.
summary ⇊
Otherwise the call fails:
Warning: Cannot modify header information — headers already sent (output started at script:line)
Some functions modifying the HTTP header are:
header
/header_remove
session_start
/session_regenerate_id
setcookie
/setrawcookie
Output can be:
-
Unintentional:
- Whitespace before
<?php
or after?>
- The UTF-8 Byte Order Mark specifically
- Previous error messages or notices
- Whitespace before
-
Intentional:
print
,echo
and other functions producing output- Raw
<html>
sections prior<?php
code.
Why does it happen?
To understand why headers must be sent before output it’s necessary
to look at a typical HTTP
response. PHP scripts mainly generate HTML content, but also pass a
set of HTTP/CGI headers to the webserver:
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>
The page/output always follows the headers. PHP has to pass the
headers to the webserver first. It can only do that once.
After the double linebreak it can nevermore amend them.
When PHP receives the first output (print
, echo
, <html>
) it will
flush all collected headers. Afterward it can send all the output
it wants. But sending further HTTP headers is impossible then.
How can you find out where the premature output occurred?
The header()
warning contains all relevant information to
locate the problem cause:
Warning: Cannot modify header information — headers already sent by
(output started at /www/usr2345/htdocs/auth.php:52) in
/www/usr2345/htdocs/index.php on line 100
Here «line 100» refers to the script where the header()
invocation failed.
The «output started at» note within the parenthesis is more significant.
It denominates the source of previous output. In this example, it’s auth.php
and line 52
. That’s where you had to look for premature output.
Typical causes:
-
Print, echo
Intentional output from
print
andecho
statements will terminate the opportunity to send HTTP headers. The application flow must be restructured to avoid that. Use functions
and templating schemes. Ensureheader()
calls occur before messages
are written out.Functions that produce output include
print
,echo
,printf
,vprintf
trigger_error
,ob_flush
,ob_end_flush
,var_dump
,print_r
readfile
,passthru
,flush
,imagepng
,imagejpeg
among others and user-defined functions.
-
Raw HTML areas
Unparsed HTML sections in a
.php
file are direct output as well.
Script conditions that will trigger aheader()
call must be noted
before any raw<html>
blocks.<!DOCTYPE html> <?php // Too late for headers already.
Use a templating scheme to separate processing from output logic.
- Place form processing code atop scripts.
- Use temporary string variables to defer messages.
- The actual output logic and intermixed HTML output should follow last.
-
Whitespace before
<?php
for «script.php line 1» warningsIf the warning refers to output inline
1
, then it’s mostly
leading whitespace, text or HTML before the opening<?php
token.<?php # There's a SINGLE space/newline before <? - Which already seals it.
Similarly it can occur for appended scripts or script sections:
?> <?php
PHP actually eats up a single linebreak after close tags. But it won’t
compensate multiple newlines or tabs or spaces shifted into such gaps. -
UTF-8 BOM
Linebreaks and spaces alone can be a problem. But there are also «invisible»
character sequences that can cause this. Most famously the
UTF-8 BOM (Byte-Order-Mark)
which isn’t displayed by most text editors. It’s the byte sequenceEF BB BF
, which is optional and redundant for UTF-8 encoded documents. PHP however has to treat it as raw output. It may show up as the characters
in the output (if the client interprets the document as Latin-1) or similar «garbage».In particular graphical editors and Java-based IDEs are oblivious to its
presence. They don’t visualize it (obliged by the Unicode standard).
Most programmer and console editors however do:There it’s easy to recognize the problem early on. Other editors may identify
its presence in a file/settings menu (Notepad++ on Windows can identify and
remedy the problem),
Another option to inspect the BOMs presence is resorting to an hexeditor.
On *nix systemshexdump
is usually available,
if not a graphical variant which simplifies auditing these and other issues:An easy fix is to set the text editor to save files as «UTF-8 (no BOM)»
or similar to such nomenclature. Often newcomers otherwise resort to creating new files and just copy&pasting the previous code back in.Correction utilities
There are also automated tools to examine and rewrite text files
(sed
/awk
orrecode
).
For PHP specifically there’s thephptags
tag tidier.
It rewrites close and open tags into long and short forms, but also easily
fixes leading and trailing whitespace, Unicode and UTF-x BOM issues:phptags --whitespace *.php
It’s safe to use on a whole include or project directory.
-
Whitespace after
?>
If the error source is mentioned as behind the
closing?>
then this is where some whitespace or the raw text got written out.
The PHP end marker does not terminate script execution at this point. Any text/space characters after it will be written out as page content
still.It’s commonly advised, in particular to newcomers, that trailing
?>
PHP
close tags should be omitted. This eschews a small portion of these cases.
(Quite commonlyinclude()d
scripts are the culprit.) -
Error source mentioned as «Unknown on line 0»
It’s typically a PHP extension or php.ini setting if no error source
is concretized.- It’s occasionally the
gzip
stream encoding setting
or theob_gzhandler
. - But it could also be any doubly loaded
extension=
module
generating an implicit PHP startup/warning message.
- It’s occasionally the
-
Preceding error messages
If another PHP statement or expression causes a warning message or
notice being printed out, that also counts as premature output.In this case you need to eschew the error,
delay the statement execution, or suppress the message with e.g.
isset()
or@()
—
when either doesn’t obstruct debugging later on.
No error message
If you have error_reporting
or display_errors
disabled per php.ini
,
then no warning will show up. But ignoring errors won’t make the problem go
away. Headers still can’t be sent after premature output.
So when header("Location: ...")
redirects silently fail it’s very
advisable to probe for warnings. Reenable them with two simple commands
atop the invocation script:
error_reporting(E_ALL);
ini_set("display_errors", 1);
Or set_error_handler("var_dump");
if all else fails.
Speaking of redirect headers, you should often use an idiom like
this for final code paths:
exit(header("Location: /finished.html"));
Preferably even a utility function, which prints a user message
in case of header()
failures.
Output buffering as a workaround
PHPs output buffering
is a workaround to alleviate this issue. It often works reliably, but shouldn’t
substitute for proper application structuring and separating output from control
logic. Its actual purpose is minimizing chunked transfers to the webserver.
-
The
output_buffering=
setting nevertheless can help.
Configure it in the php.ini
or via .htaccess
or even .user.ini on
modern FPM/FastCGI setups.
Enabling it will allow PHP to buffer output instead of passing it to the webserver instantly. PHP thus can aggregate HTTP headers. -
It can likewise be engaged with a call to
ob_start();
atop the invocation script. Which however is less reliable for multiple reasons:-
Even if
<?php ob_start(); ?>
starts the first script, whitespace or a
BOM might get shuffled before, rendering it ineffective. -
It can conceal whitespace for HTML output. But as soon as the application logic attempts to send binary content (a generated image for example),
the buffered extraneous output becomes a problem. (Necessitatingob_clean()
as a further workaround.) -
The buffer is limited in size, and can easily overrun when left to defaults.
And that’s not a rare occurrence either, difficult to track down
when it happens.
-
Both approaches therefore may become unreliable — in particular when switching between
development setups and/or production servers. This is why output buffering is
widely considered just a crutch / strictly a workaround.
See also the basic usage example
in the manual, and for more pros and cons:
- What is output buffering?
- Why use output buffering in PHP?
- Is using output buffering considered a bad practice?
- Use case for output buffering as the correct solution to «headers already sent»
But it worked on the other server!?
If you didn’t get the headers warning before, then the output buffering
php.ini setting
has changed. It’s likely unconfigured on the current/new server.
Checking with headers_sent()
You can always use headers_sent()
to probe if
it’s still possible to… send headers. Which is useful to conditionally print
info or apply other fallback logic.
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
Useful fallback workarounds are:
-
HTML
<meta>
tagIf your application is structurally hard to fix, then an easy (but
somewhat unprofessional) way to allow redirects is injecting a HTML
<meta>
tag. A redirect can be achieved with:<meta http-equiv="Location" content="http://example.com/">
Or with a short delay:
<meta http-equiv="Refresh" content="2; url=../target.html">
This leads to non-valid HTML when utilized past the
<head>
section.
Most browsers still accept it. -
JavaScript redirect
As alternative a JavaScript redirect
can be used for page redirects:<script> location.replace("target.html"); </script>
While this is often more HTML compliant than the
<meta>
workaround,
it incurs a reliance on JavaScript-capable clients.
Both approaches however make acceptable fallbacks when genuine HTTP header()
calls fail. Ideally you’d always combine this with a user-friendly message and
clickable link as last resort. (Which for instance is what the http_redirect()
PECL extension does.)
Why setcookie()
and session_start()
are also affected
Both setcookie()
and session_start()
need to send a Set-Cookie:
HTTP header.
The same conditions therefore apply, and similar error messages will be generated
for premature output situations.
(Of course, they’re furthermore affected by disabled cookies in the browser
or even proxy issues. The session functionality obviously also depends on free
disk space and other php.ini settings, etc.)
Further links
- Google provides a lengthy list of similar discussions.
- And of course many specific cases have been covered on Stack Overflow as well.
- The WordPress FAQ explains How do I solve the Headers already sent warning problem? in a generic manner.
- Adobe Community: PHP development: why redirects don’t work (headers already sent)
- Nucleus FAQ: What does «page headers already sent» mean?
- One of the more thorough explanations is HTTP Headers and the PHP header() Function — A tutorial by NicholasSolutions (Internet Archive link).
It covers HTTP in detail and gives a few guidelines for rewriting scripts.
09.08.2011, 11:14. Показов 99601. Ответов 76
На форуме часто бывают вопросы, что при использовании session_start(), header() и setcookie() и некоторых других функций, появляется сообщения типа
Код
Warning: session_start(): Cannot send session cookie - headers already sent in D:HTTPa.php on line 5 Warning: session_start(): Cannot send session cache limiter - headers already sent in D:HTTPa.php on line 5
или
Код
Warning: Cannot modify header information - headers already sent in D:HTTPa.php on line 5
Это связано с тем, что браузеру уже были отправлены данные перед тем, как была вызвана одна из этих функций.
Если кому интересны подробности, то вот
немного теории
Давайте разберёмся, что происходит, когда мы заходим на сайт. Например, когда мы в браузере открываем страницу http://www.example.ru/file.php, то браузер запрашивает у DNS сервера, какой IP адрес принадлежит серверу www.example.ru. После этого он подключается по протоколу TCP к этому IP адресу на порт 80 (этот порт используется по умолчанию для HTTP) и отправляет текстовый запрос наподобие такого:
Код
GET /file.php HTTP/1.1 Host: www.example.ru User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: ru,chrome://global/locale/intl.properties;q=0.5 Accept-Encoding: gzip, deflate Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7 Connection: keep-alive Cookie: user=2; var=blablabla
Этим запросом в первой строке браузер указывает, что хочет просто получить страницу (GET), указывает адрес страницы и версию протокола (сейчас используются только две версии — 1.0 и 1.1… версия 0.9 устарела и сейчас не используется).
Так же обязательным является параметр Host. Т.к. на каждом сервере может быть несколько разных сайтов, то мы должны указать, к какому именно сайту мы хотим обратиться.
Остальные параметры необязательны… ими браузер просто рассказывает о себе (User-Agent) и рассказывает серверу, как он хочет общаться. Но сейчас речь не об этом…
В ответ на этот запрос веб-сервер запускает выполнение нашего php-скрипта, к которому обращается браузер. И веб-сервер отправляет браузеру результат работы скрипта… но перед этим он отправит свои заголовки ответа.
Код
HTTP/1.1 200 OK Date: Tue, 09 Aug 2011 06:59:22 GMT Server: Apache X-Powered-By: PHP/5.3.5 Content-Length: 1234 Content-Type: text/html; charset=windows-1251 Set-Cookie: PHPSESSID=3ms50sjl2pt2s19sns5udiryn6; path=/ <!DOCTYPE html> <html> ......
Как вы видите, заголовки идут вначале, а после одной пустой строки начинается тело самого html, который мы генерируем с помощью php-скрипта.
И вот мы подошли к основной сути… когда PHP приказывает веб-серверу начать отправлять само содержимое брузеру, то веб-серверу не остаётся ничего, кроме как отправить вначале все заголовки, которые имеются у него на текущий момент. Поэтому после этого PHP уже не сможет изменить эти заголовки.
Функция header() изменяет любой заголовок. Функция setcookie() добавляет куки к заголовку Set-Cookie, поэтому тоже не может работать после отправки заголовков. session_start() добавляет куки с идентификатором сессии (как в примере выше).
А пока найдём пути решения этой проблемы. Чаще всего такие ситуации происходят из-за использования BOM. BOM — это 2 или 3 невидимых символа в начале файла. Эти символы указывают на то, какая используется кодировка. Многие текстовые редакторы по умолчанию добавляют BOM ко всем файлам юникодовской кодировкой (например UTF-8). Например, создадим в блокноте файл
И сохраним его в кодировке UTF-8. Но если мы теперь просмотрим файл в кодировке cp1251, то увидим, что вначале файла добавлены символы:
Именно эти символы и создают проблему. Ведь PHP исполняет свой код только внутри тегов <?php ?>… всё что снаружи отправляется браузеру как есть. Получается, что эти 3 байта сразу же отправятся к браузеру ещё до начала интерпретации PHP-кода. Хотя, по умолчанию в php.ini включен output_buffering и установлен в значение 4096… поэтому проблемы с BOM не должно быть… однако, у многих пользователей она почему-то есть. Подозреваю, что это следствие использования разных кривых сборок.
Я вижу 2 пути решения это проблемы:
1. Включить в php.ini
На вашем компьютере это должно помочь. Но если вы позже закачаете свой сайт на хостинг, то у вас не будет уверенности, что там включен этот буфер.
2. Запретить текстовому редактору сохранять BOM. Но тут уже всё зависит от возможностей текстового редактора, который вы используете. Например, я пользуюсь Notepad++… там можно открыть меню «Кодировки» и выбрать «Преобразовать в UTF-8 без BOM».
Если вы уверены, что BOM не используется, значит где-то перед функциями header(), setcookie() и т.п. вы сами отправляете данные пользователю. Возможно, перед открытием тега <?php есть пустая строка… её тоже нужно убрать.
Если вначале вам необходимо отправлять данные, то можно включить использование буфера, что бы данные не отправлялись сразу браузеру:
PHP | ||
|
В этом случае никаких проблем не будет, т.к. ob_start() указывает, что данные вначале надо накапливать в буфере и отправлять браузеру только когда выполнение скрипта будет завершено.
57
Здравствуйте, друзья!
Сегодня я хочу рассказать вам об одной очень популярной и загадочной ошибке. Наверняка, многие из вас во время отладки своих программ сталкивались со следующим сообщением: headers already sent. Переводится на русский язык данная запись как «заголовки уже отправлены».
Чтобы понять суть этой ошибки, давайте вспомним структуру http-пакетов:
- Стартовая строка — определяет тип сообщения;
- Заголовки — характеризуют тело сообщения, параметры передачи и прочие сведения;
- Тело сообщения — непосредственно данные сообщения.
Т.е. говоря headers already sent программа ругает нас, за то, что мы уже где-то начали формировать тело сообщения (3-ю часть http-пакета), но снова хотим отправить заголовки (2-ую часть).
Приведу пару примеров: лёгкий и адский – с точки зрения возможности заметить ошибку.
Пример 1 – лёгкий.
//Получаем что-то из массива $_GET
$id = $_GET[‘id’];
//Делаем отладочный вывод
echo $id;
// Стартуем сессию
session_start();
//И дальше много php и html, работающего правильно.
Будет выведена ошибка:
Warning: session_start() [function.session-start]: Cannot send session cache limiter – headers already sent
Мы не имели права вызывать функцию session_start(), после использования оператора echo. Такую ошибку исправить несложно – уберите отладочное echo.
Пример 2 – жуткий.
Представим, что у нас есть абстрактный файл модели model.php, который мы подключаем на нужную нам страничку a.php.
//Подключаем какой-то модуль
include_once('model.php');
// Стартуем сессию
session_start();
//И дальше много php и html, работающего правильно.
Но неожиданно денвер снова говорит:
Warning: session_start() [function.session-start]: Cannot send session cache limiter – headers already sent
Как? Почему? Ведь мы ещё не начинали формирования тела сообщения! И вот здесь, прежде чем удастся найти ошибку, можно расколотить компьютер или удалить локальный сервер. А всё приключилось потому, что мы нарушили золотое правило – никогда не закрывайте блок php, если в конкретном файле после него не идёт html.
Давайте взглянем на файл модели:
<?php
function sum($a, $b){
return $a + $b;
}
?>
И оказывается, что мы всего-то навсего случайно поставили пробел после закрывающего ?>… Ненаходимая ошибка, этот пробел невозможно было и представить себе. Как же с этим бороться? А очень просто – не пишите закрывающее ?> в файлах с чистым php-кодом, и всё будет в порядке.
Также важно понимать, что речь идёт не только о конкретной функции session_start, но и обо всех остальных, которые выполняют отправку заголовков, например: setcookie, header итд.
Резюме:
- Не забывайте о том, что нельзя отправлять заголовки после того, как началось формирование тела сообщения
- Не пишите закрывающее ?> в файлах с чистым php
И об ошибке headers already sent Вы забудете!
15.02.2015
Понравился материал? Поделись с друзьями!