问题

运行我的脚本时,我会收到以下错误:

Warning: Cannot modify header information - headers already sent by (output started at /some/file.php:12) in /some/file.php on line 23

错误讯息中提及的行包含 header() setcookie() 呼叫.

这可能是什么原因?如何解决这个问题?



解决方法

No output before sending headers!

发送/修改HTTP标头的函数必须在进行任何输出之前 被调用. 摘要 否则呼叫失败:

Warning: Cannot modify header information - headers already sent (output started at script:line)

修改HTTP标头的某些函数是:

输出可以是:

  • Unintentional:

    • Whitespace before <?php or after ?>
    • The UTF-8 Byte Order Mark specifically
    • Previous error messages or notices
  • Intentional:

    • print, echo and other functions producing output
    • Raw <html> sections prior <?php code.

Why does it happen?

要了解为什么必须在输出标头之前发送标头 查看典型的 HTTP 响应. PHP脚本主要生成HTML内容,但也传递一个 到Web服务器的一组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必须通过 头文件到web服务器.它只能做一次. 在双重破折后,它永远不能再修改它们.

当PHP收到第一个输出( print , echo ,&lt; html&gt; 刷新所有收集的标题.然后它可以发送所有的输出 它想要.但是发送更多的HTTP头是不可能的.

How can you find out where the premature output occured?

header()警告包含所有相关信息 找到问题原因:

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

这里"第100行"是指 header() 调用失败的脚本.

"输出开始于"括号内的注释更重要. 它指定以前输出的来源.在这个例子中,它是 auth.php 52 .这就是你不得不寻找过早的输出.

典型原因:

  1. Print, echo

    Intentional output from print and echo statements will terminate the opportunity to send HTTP headers. The application flow must be restructured to avoid that. Use functions and templating schemes. Ensure header() 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.

  2. Raw HTML areas

    Unparsed HTML sections in a .php file are direct output as well. Script conditions that will trigger a header() 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.

  3. Whitespace before <?php for "script.php line 1" warnings

    If the warning refers to output in line 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.

  4. UTF-8 BOM

    Linebreaks and spaces alone can be a problem. But there are also "invisible" character sequences which can cause this. Most famously the UTF-8 BOM (Byte-Order-Mark) which isn't displayed by most text editors. It's the byte sequence EF 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:

    joes editor showing UTF-8 BOM placeholder, and MC editor a dot

    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 systems hexdump is usually available, if not a graphical variant which simplifies auditing these and other issues:

    beav hexeditor showing utf-8 bom

    An easy fix is to set the text editor to save files as "UTF-8 (no BOM)" or similar 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 or recode). For PHP specifically there's the phptags 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 sane to use on a whole include or project directory.

  5. Whitespace after ?>

    If the error source is mentioned as behind the closing ?> then this is where some whitespace or raw text got written out. The PHP end marker does not terminate script executation 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 commonly include()d scripts are the culprit.)

  6. 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 the ob_gzhandler.
    • But it could also be any doubly loaded extension= module generating an implicit PHP startup/warning message.

  7. Preceding error messages

    If another PHP statement or expression causes a warning message or notice being printeded 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

如果您对 php.ini 禁用 error_reporting display_errors 那么不会出现警告.但忽略错误不会使问题去 远.在提前输出后仍然无法发送标头.

所以当 header("Location:...")重定向默认失败它是非常 建议探测警告.使用两个简单的命令重新启用它们 在调用脚本顶部:

error_reporting(E_ALL);
ini_set("display_errors", 1);

set_error_handler("var_dump"); 如果所有其他都失败.

说到重定向标头,您应该经常使用惯用语 这是最终代码路径:

exit(header("Location: /finished.html"));

甚至一个效用函数,打印用户消息 在 header()失败的情况下.

Output buffering as workaround

PHP 输出缓冲 是缓解这个问题的解决方法.它经常可靠地工作,但不应该 替代正确的应用程序结构并将输出与控制分离 逻辑.其实际目的是最小化到网络服务器的分块传输.

  1. 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.

  2. 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. (Necessitating ob_clean() as furher workaround.)

    • The buffer is limited in size, and can easily overrun when left to defaults. And that's not a rare occurence either, difficult to track down when it happens.

因此,这两种方法都可能变得不可靠 - 特别是在切换时 开发设置和/或生产服务器.这就是为什么输出缓冲是 广泛认为只是拐杖/严格的解决方法.

另请参阅基本用法示例 在手册中,以及更多的利弊:

But it worked on the other server!?

如果您之前未收到标头警告,则输出缓冲  php.ini设置  已经改变.它可能在当前/新服务器上未配置.

Checking with headers_sent()

您随时可以使用 headers_sent() 来探测 它仍然可能...发送标题.这对于有条件打印是有用的 信息或应用其他后备逻辑.

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> tag

    If 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.

然而,两种方法都可以接受真正的HTTP标头() 调用失败.理想情况下,您总是将这与用户友好的消息和 可点击的链接作为最后的手段. (例如,这是 http_redirect()的作用 PECL扩展名.)

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.

(当然,他们还受到浏览器中禁用的Cookie的影响, 甚至代理问题.会话功能显然也取决于自由 磁盘空间和其他php.ini设置等)

Further links




相关问题推荐