协慌网

登录 贡献 社区

如何修复 PHP 中的 “Headers already sent” 错误

运行我的脚本时,我收到几个这样的错误:

警告:不能更改头信息 - 头已经发出( 输出开始 / some/file.php:12)/some/file.php 在线 23

错误消息中提到的行包含header()setcookie()调用。

这可能是什么原因?以及如何解决它?

答案

发送标头前没有输出!

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

警告:无法修改标头信息 - 已发送的标头(输出从脚本开始:行

修改 HTTP 标头的一些功能是:

输出可以是:

  • 故意的:

    • printecho和其他产生输出的功能
    • 原始<html>部分之前的<?php代码。

为什么会这样?

要理解为什么必须在输出之前发送标头,必须查看典型的HTTP响应。 PHP 脚本主要生成 HTML 内容,但也将一组 HTTP / CGI 标头传递给 Web 服务器:

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 收到第一个输出( printecho<html> )时,它将刷新所有收集的标题。之后它可以发送它想要的所有输出。但是,发送更多 HTTP 标头是不可能的。

你怎么知道过早输出发生在哪里?

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

警告:无法修改标题信息 - 已在第 100 行的 / us/usr2345/htdocs/index.php 中发送的标题(输出从 / www / usr2345 / htdocs / auth.php:52 开始

这里 “第 100 行” 指的是header() 调用失败的脚本。

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:

  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

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);

或者set_error_handler("var_dump");如果一切都失败了。

说到重定向标题,您应该经常使用这样的成语来表示最终的代码路径:

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

甚至优选甚至是实用程序功能,其在header()失败的情况下打印用户消息。

输出缓冲作为变通方法

PHPs 输出缓冲是一种解决此问题的解决方法。它通常可靠地工作,但不应取代适当的应用程序结构并将输出与控制逻辑分开。它的实际目的是最大限度地减少到网络服务器的分块传输。

  1. 然而, output_buffering=设置可以提供帮助。在现代 FPM / FastCGI 设置中,在php.ini 中配置它,或者通过.htaccess或甚至.user.ini配置它。
    启用它将允许 PHP 缓冲输出,而不是立即将其传递给 Web 服务器。因此,PHP 可以聚合 HTTP 头。

  2. 同样可以调用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"));
}

有用的后备解决方法是:

  • HTML <meta>标记

    如果您的应用程序在结构上很难修复,那么允许重定向的一种简单(但有点不专业)的方法是注入 HTML <meta>标记。可以通过以下方式实现重定向:

    <meta http-equiv="Location" content="http://example.com/">

    或者延迟很短:

    <meta http-equiv="Refresh" content="2; url=../target.html">

    当通过<head>部分使用时,这会导致无效的 HTML。大多数浏览器仍然接受它。

  • JavaScript 重定向

    作为替代方案, JavaScript 重定向可用于页面重定向:

    <script> location.replace("target.html"); </script>

    虽然这通常比<meta>解决方法更符合 HTML,但它依赖于支持 JavaScript 的客户端。

但是,当真正的 HTTP header()调用失败时,这两种方法都会产生可接受的回退。理想情况下,您总是将此与用户友好的消息和可点击的链接结合起来作为最后的手段。 (例如, http_redirect() PECL 扩展的功能。)

为什么setcookie()session_start()也会受到影响

setcookie()session_start()需要发送Set-Cookie: HTTP 头。因此适用相同的条件,并且将针对过早输出情况生成类似的错误消息。

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

更多链接

在发送 HTTP 标头(使用setcookieheader )之前发送任何内容时会触发此错误消息。在 HTTP 标头之前输出内容的常见原因是:

  • 意外的空格,通常在文件的开头或结尾,如下所示:

    <?php
    // Note the space before "<?php"
    ?>

为了避免这种情况,只需忽略关闭?> - 无论如何都不需要它。

  • 字节顺序标记在 php 文件的开头。用十六进制编辑器检查你的 php 文件,看看是否是这种情况。它们应该从字节3F 3C 。您可以从文件的开头安全地删除 BOM EF BB BF
  • 显式输出,例如调用echoprintfreadfilepassthru ,代码<?等等
  • 如果设置了display_errors php.ini 属性,则由 php 输出警告。而不是崩溃程序员的错误,PHP 默默地修复错误并发出警告。虽然您可以修改display_errorserror_reporting配置,但您应该修复此问题。
    常见的原因是访问数组的未定义元素(例如$_POST['input']而不使用emptyisset来测试输入是否已设置),或使用未定义的常量而不是字符串文字(如$_POST[input] ,注意缺少的引号)。

打开输出缓冲应该会使问题消失; 调用ob_start之后的所有输出都缓冲在内存中,直到释放缓冲区为止,例如使用ob_end_flush

但是,虽然输出缓冲避免了这些问题,但您应该确定应用程序在 HTTP 标头之前输出 HTTP 主体的原因。这就像接听电话,讨论你的一天和天气,然后告诉来电者他的号码错了。

我之前多次遇到此错误。我确信所有 PHP 程序员至少一次出现此错误。要解决此错误,您可以根据您的问题级别解决使用解决方案:

可能解决方案 1:

您可能在之前之后(在文件末尾?> 之后)留下了空格

THERE SHOULD BE NO BLANK SPACES HERE
<?php  

   echo "your code here";

?>
DO CHECK FOR BLANK SPACES HERE AS WELL; THIS LINE (blank line) SHOULD NOT EXIST.

大多数情况下,这应该可以解决您的问题。请检查与您require文件关联的所有文件。

注意: 有时像 gedit(默认的 linux 编辑器)这样的 EDITOR(IDE)在保存保存文件上添加一个空白行。这不应该发生。如果你使用的是 linux。您可以使用 VI 编辑器删除页面末尾?> 后的空格 / 行。

如果这不是你的情况,那么你可以使用ob_start进行输出缓冲,如下所示:

可能的方案 2:

<?php
  ob_start();

  // code 

 ob_end_flush();
?>

这将打开输出缓冲,并在页面缓冲后创建标题。