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

How to fix “Headers already sent” error in PHP

在运行我的脚本时,我遇到了如下几个错误:

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()调用。

为什么会这样?如何修复?


发送邮件头之前没有输出!

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

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

Ok.

修改HTTP头的一些函数是:好的。

  • header/header_remove
  • session_start/session_regenerate_id
  • setcookie/setrawcookie

输出可以是:好的。

  • 无意的:好的。

    • 之前或?>之后的空白
    • UTF-8字节顺序标记
    • 以前的错误消息或通知
  • 故意的:好的。

    • printecho等产生输出的功能
    • 段先于码。

为什么会这样?

要理解为什么在输出之前必须发送头,必须查看典型的HTTP反应。PHP脚本主要生成HTML内容,但也传递一个Web服务器的http/cgi头集:好的。

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Powered-By: PHP/5.3.7
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8

<html><head>PHP page output page</head>
<body>Content <p>
Some more output follows...
</p>Ok.


and  <img src=internal-icon-delayed>

页面/输出始终遵循标题。PHP必须通过首先指向Web服务器的头。它只能做到一次。在双断线之后,它永远无法修正它们。好的。

当php接收到第一个输出(printecho时,它将冲洗所有收集的收割台。然后它可以发送所有输出它想要。但是发送更多的HTTP头是不可能的。好的。如何找出提前输出发生的位置?

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

Ok.

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

括号内的"输出开始于"注释更为重要。它指定了以前输出的源。在这个例子中,它是auth.php。以及EDOCX1号线〔18〕。这就是你必须寻找提前产出的地方。好的。

典型原因:好的。

  • 打印,回声

    printecho声明的故意输出将终止。发送HTTP头的机会。应用程序流必须重组以避免这种情况。使用函数以及模板化方案。确保header()调用发生在消息之前都写出来了。好的。

    产生输出的函数包括好的。

    • printechoprintfvprintf
    • trigger_errorob_flushob_end_flushvar_dumpprint_r
    • readfilepassthruflushimagepngimagejpeg

    以及用户定义的函数。好的。

  • 原始HTML区域

    .php文件中未分析的HTML部分也是直接输出的。必须注意将触发header()调用的脚本条件在任何未加工的块之前。好的。

    1
    2
    3
    <!DOCTYPE html>
    <?php
        // Too late for headers already.

    使用模板方案将处理与输出逻辑分离。好的。

    • 将表单处理代码放在脚本上。
    • 使用临时字符串变量延迟消息。
    • 实际的输出逻辑和混合的HTML输出应该紧随其后。
  • "script.php line 1"警告前的空白

    如果警告指的是1行中的输出,那么它主要是打开标记前的前导空格、文本或HTML。好的。

    1
    2
     <?php
    # There's a SINGLE space/newline before <? - Which already seals it.

    类似地,附加的脚本或脚本部分也会出现这种情况:好的。

    1
    2
    3
    ?>

    <?php

    PHP实际上会在关闭标记后占用一个换行符。但它不会补偿多个换行符、制表符或空格移动到这些间隙中。好的。

  • UTF-8 BOM

    换行符和空格本身就是一个问题。但也有"看不见的"可能导致这种情况的字符序列。最著名的是UTF-8 BOM(字节顺序标记)大多数文本编辑器都不显示。它是字节序列EF BB BF,其中对于UTF-8编码文档是可选的和冗余的。但是PHP必须处理它作为原始输出。它可以显示为输出中的字符???(如果客户机将文档解释为拉丁文-1)或类似的"垃圾"。好的。

    特别是图形编辑器和基于Java的IDE是不被注意的。在场。它们不将其可视化(受Unicode标准的约束)。然而,大多数程序员和控制台编辑器都会:好的。

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

    在那里,很容易及早发现问题。其他编辑可确定它出现在文件/设置菜单中(Windows上的记事本++可以识别和解决问题)。另一个检查boms存在的选项是使用hexeditor。on*nix系统通常提供hexdump,如果不是简化审计这些和其他问题的图形变体:好的。

    beav hexeditor showing utf-8 bom好的。

    一个简单的解决方法是设置文本编辑器将文件保存为"UTF-8(无BOM)"。或类似的命名法。通常新来者都会选择创造新的文件,然后复制并粘贴上一个代码。好的。修正实用程序

    还有自动工具来检查和重写文本文件(sed/awkrecode)。特别是对于PHP,有phptags标签tidier。它将关闭和打开的标签改写为长和短格式,但也很容易修复前导和尾随空白、Unicode和UTF-X BOM问题:好的。

    1
    phptags  --whitespace  *.php

    整体上使用include或project目录是明智的。好的。

  • ?>后的空白

    如果错误源在关闭?>。然后这就是一些空白或原始文本被写出的地方。PHP结束标记不会在此终止脚本执行点。之后的任何文本/空格字符将作为页面内容写入仍然。好的。

    人们通常建议,尤其是对新来者来说,要跟踪?>php应省略结束标记。这避免了这些案例中的一小部分。(通常情况下,include()d脚本是罪魁祸首。)好的。

  • 错误源称为"第0行未知"

    如果没有错误源,它通常是一个php扩展或php.ini设置。具体化。好的。

    • 有时是gzip流编码设置或者是ob_gzhandler
    • 但它也可以是任何双重加载的extension=模块。生成隐式PHP启动/警告消息。
  • 前面的错误消息

    如果另一个php语句或表达式导致警告消息或请注意,正在打印输出,这也算作过早输出。好的。

    在这种情况下,您需要避免错误,延迟语句执行,或用例如isset()@()。-当两者都不妨碍以后的调试时。好的。

  • 无错误消息

    如果你的error_reportingdisplay_errors根据php.ini被禁用,这样就不会出现任何警告。但是忽略错误并不能解决问题离开。过早输出后仍无法发送邮件头。好的。

    所以当header("Location: ...")重新定向时,它会自动失败建议探测警告。用两个简单命令重新启用它们在调用脚本顶部:好的。

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

    或者,如果所有其他方法都失败的话,用set_error_handler("var_dump");。好的。

    说到重定向头,您应该经常使用类似这样的成语对于最终代码路径:好的。

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

    甚至是打印用户消息的实用程序函数如果header()故障。好的。输出缓冲作为解决方法

    PHPS输出缓冲是缓解这一问题的一种解决办法。它通常工作可靠,但不应该替代适当的应用程序结构并将输出与控制分离逻辑。它的实际用途是最小化到Web服务器的分块传输。好的。

  • 江户十一〔15〕号尽管如此,设置还是有帮助的。在php.ini中配置它或通过.htaccess甚至.user.ini打开现代fpm/fastcgi设置。启用它将允许PHP缓冲输出,而不是将其传递给Web服务器立刻。因此,PHP可以聚合HTTP头。好的。

  • 它也可以拨打ob_start();的电话。在调用脚本的顶部。但由于多种原因,其可靠性较低:好的。

    • 即使启动第一个脚本,空白或bom可能会被洗牌之前,使其无效。好的。

    • 它可以为HTML输出隐藏空白。但是一旦申请逻辑尝试发送二进制内容(例如生成的图像),缓冲的外部输出成为一个问题。(需要EDOCX1[0]当弗尔赫在工作的时候。)好的。

    • 缓冲区的大小是有限的,如果保留为默认值,则很容易溢出。这也不是罕见的,很难找到。当它发生的时候。好的。

  • 因此,这两种方法都可能变得不可靠,尤其是在两种方法之间切换时开发设置和/或生产服务器。这就是为什么输出缓冲被广泛认为只是一种拐杖/严格来说是一种权宜之计。好的。

    另请参见基本用法示例在手册中,为了获得更多的优点和缺点:好的。

    • 什么是输出缓冲?
    • 为什么在PHP中使用输出缓冲?
    • 使用输出缓冲是否被视为一种坏做法?
    • 输出缓冲的用例,作为"已发送邮件头"的正确解决方案

    但它在另一台服务器上工作!?

    如果之前没有收到头警告,那么输出缓冲PHPini设置改变了。它可能在当前/新服务器上未配置。好的。与headers_sent()核对

    如果仍然有可能…发送标题。有条件地打印信息或应用其他回退逻辑。好的。

    1
    2
    3
    4
    5
    6
    if (headers_sent()) {
        die("Redirect failed. Please click on this link:");
    }
    else{
        exit(header("Location: /user.php"));
    }

    有用的回退解决方法是:好的。

    • html 标记

      如果您的应用程序在结构上很难修复,那么允许重定向的方法是注入一个HTML标签。可通过以下方式实现重定向:好的。

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

      或短暂延迟:好的。

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

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

    • javascript重定向

      作为替代,JavaScript重定向可用于页面重定向:好的。

      1
        location.replace("target.html");

      虽然这通常比解决方案更符合HTML,它依赖于支持JavaScript的客户机。好的。

    但是,当真正的HTTP头()时,这两种方法都会进行可接受的回退。呼叫失败。理想情况下,您总是将此与用户友好的消息结合在一起,并且点击链接作为最后手段。(例如,http_redirect()是什么)PECL扩展功能。)好的。为什么setcookie()session_start()也受到影响

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

    (当然,它们还受到浏览器中禁用的cookie的影响,甚至代理问题。会话功能显然也依赖于自由磁盘空间和其他php.ini设置等。)好的。进一步环节

    • 谷歌提供了一长串类似的讨论。
    • 当然,在堆栈溢出中也涵盖了许多特定的情况。
    • WordPress常见问题解答解释了如何解决已经发送的邮件头的警告问题?以一般的方式。
    • Adobe社区:PHP开发:为什么重定向不起作用(头文件已经发送)
    • Nucleus常见问题解答:"page headers already sent"是什么意思?
    • 其中一个更深入的解释是http headers和php header()函数——Nicholassolutions(Internet存档链接)的教程。它详细介绍了HTTP,并给出了一些重写脚本的指导原则。

    好啊。


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

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

      1
      2
      3
       <?php
      // Note the space before"<?php"
      ?>

    &为了避免这一点,只需将收市日期省略1(2)-无论如何都不需要。

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

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

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


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

    可能的解决方案1:

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

    1
    2
    3
    4
    5
    6
    7
    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编辑器)这样的编辑器(ide)会在保存文件时添加一个空行。这不应该发生。如果您使用的是Linux。您可以使用vi编辑器删除后面的空格/行吗?>在页面的末尾。

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

    可能的解决方案2:

    1
    2
    3
    4
    5
    6
    7
    <?php
      ob_start();

      // code

     ob_end_flush();
    ?>

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


    而不是下一行

    1
    //header("Location:".ADMIN_URL."/index.php");

    1
    echo("location.href = '".ADMIN_URL."/index.php?msg=$msg';");

    1
    ?><?php echo("location.href = '".ADMIN_URL."/index.php?msg=$msg';");?><?php

    这肯定能解决你的问题。我也面临同样的问题,但我通过上面的方式写标题位置解决了这个问题。


    你做

    1
    printf ("Hi %s,</br />", $name);

    在设置cookie之前,这是不允许的。您不能在标题之前发送任何输出,即使是空行也不行。


    这是因为这一行:

    1
    printf ("Hi %s,</br />", $name);

    在发送邮件头之前,不应打印/回送任何内容。


    常见问题:

    (复制自:源)

    (=)=

    1)在header(.......);命令之前,不应该有任何输出(即echo..或html代码)。

    2)清除?>标签后的空白(或换行)。

    3)黄金法则!-检查该php文件(以及,如果include其他文件)是否具有不带bom编码(而不仅仅是utf-8)的utf8。这在很多情况下都是问题(因为utf8编码的文件在php文件的开头有一些特殊的字符,而文本编辑器没有显示这些字符)!!!!!!!!!!!!!!!!

    4)在header(...);之后,必须使用exit;

    5)始终使用301或302参考:

    1
    header("location: http://example.com",  true,  301 );  exit;

    6)打开错误报告,并查找错误。您的错误可能是由一个不工作的函数引起的。当您打开错误报告时,您应该总是首先修复最顶端的错误。例如,它可能是"warning:date_default_timezone_get():依赖系统的时区设置是不安全的。"-然后向下看,您可能会看到"headers not sent"错误。修复最上面的(第一个)错误后,重新加载页面。如果仍然有错误,请再次修复最顶端的错误。

    7)如果以上都没有帮助,那么使用jawscript重定向(但是,强烈不推荐的方法),可能是自定义情况下的最后一次机会…:

    1
    echo"<script type='text/javascript'>window.top.location='http://website.com/';"; exit;


    一个简单的提示:脚本中的一个简单的空格(或不可见的特殊字符)就在第一个标记之前,可能会导致这种情况发生!尤其是当你在一个团队中工作时,有人正在使用一个"弱"的IDE,或者在文件中使用了奇怪的文本编辑器。

    我见过这些东西;)


    另一个不好的做法可能引发这个尚未说明的问题。

    请参见以下代码段:

    1
    2
    3
    4
    <?php
    include('a_important_file.php'); //really really really bad practise
    header("Location:A location");
    ?>

    一切都好吧?

    如果"一个重要的文件.php"是:

    1
    2
    3
    4
    5
    6
    7
    <?php
    //some php code
    //another line of php code
    //no line above is generating any output
    ?>

     ----------This is the end of the an_important_file-------------------

    这样不行?为什么?因为已经生成了新行。

    现在,尽管这不是一个常见的场景,但是如果您使用的是一个MVC框架,它在将事情移交给控制器之前加载了大量文件,该怎么办?这不是一个罕见的情况。做好准备。

    来自PSR-2 2.2:

    • 所有PHP文件都必须使用Unix LF (linefeed) line ending
    • 所有PHP文件必须以single blank line结尾。
    • 闭幕?>标签必须是包含only php的文件中的omitted

    相信我,遵循这些标准可以为你节省很多时间。)


    有时,当dev进程同时拥有win工作站和Linux系统(主机),并且在代码中,在相关行之前看不到任何输出时,可能是文件的格式设置和缺少unix lf(linefeed)。行结束。

    为了快速修复这个问题,我们通常要做的是重命名文件,在Linux系统上创建一个新文件而不是重命名的文件,然后将内容复制到该文件中。很多时候,这就解决了这个问题,因为在Win中创建的一些文件一旦转移到宿主,就会导致这个问题。

    对于我们通过ftp管理的站点,此修复是一个简单的修复,有时可以节省我们的新团队成员一些时间。


    通常,当我们在回送或打印后发送头段时会出现此错误。如果在特定页面上出现此错误,则在调用start_session()之前,请确保该页面没有回送任何内容。

    不可预测错误示例:

    1
    2
    3
    4
    5
     <?php //a white-space before <?php also send for output and arise error
    session_start();
    session_regenerate_id();

    //your page content

    再举一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    <?php
    includes 'functions.php';
    ?> <!-- This new line will also arise error -->
    <?php
    session_start();
    session_regenerate_id();

    //your page content

    结论:在调用session_start()header()函数之前不要输出任何字符,即使是空白或新行