提问者:小点点

如何修复PHP中的“报头已发送”错误


在运行脚本时,我会遇到以下几个错误:

警告:无法修改头信息-头已经由第23行/some/file.php中的/some/file.php(输出从/some/file.php开始:12)发送

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

原因何在? 以及如何修复它?


共3个答案

匿名用户

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

警告:无法修改标头信息-标头已发送(输出从Script:Line)

修改HTTP报头的一些函数是:

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

输出可以是:

>

  • 无意:

    • <;?php前或?>
    • 后的空格
    • 特别是UTF-8字节顺序标记
    • 以前的错误消息或通知

    >

  • 有意:

    • printecho和其他生成输出的函数
    • 原始代码之前的部分。

    为了理解为什么必须在输出之前发送报头,有必要查看一个典型的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)时,它将刷新所有收集的头。 之后,它可以发送它想要的所有输出。 但是发送进一步的HTTP报头是不可能的。

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

    警告:无法修改标头信息-标头已由/www/usr2345/htdocs/index.php第100行中的/www/usr2345/htdocs/auth.php:52处的/www/usr2345/htdocs/index.php发送(输出开始于/www/usr2345/htdocs/auth.php:52)

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

    括号内的“Output started at”注释意义更大。 它表示以前输出的来源。 在本例中,它是auth.php和行52。 那就是你必须寻找过早输出的地方。

    典型原因:

    >

  • print,echo

    printecho语句的有意输出将终止发送HTTP标头的机会。 必须重新构造应用程序流以避免这种情况。 使用函数和模板方案。 确保在写出消息之前发生header()调用。

    产生输出的函数包括

    • printechoprintfvprintf
    • trigger_errorob_flushob_end_flushvar_dumpprint_r
    • ReadFilePassthruFlushImagePGImageJPEG


    包括其他函数和用户定义函数。

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

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

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

    • 将表单处理代码置于脚本之上。
    • 使用临时字符串变量延迟邮件。
    • 实际输出逻辑和混合HTML输出应在最后。

    如果警告指的是1行中的输出,那么在内标识开始之前,它主要是前导空格,文本或HTML。

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

    类似地,附加的脚本或脚本节也会发生这种情况:

    ?>
    
    <?php
    

    PHP实际上在close标记之后只需要一个换行符。 但它不会补偿多个换行,制表符或空格移位到这样的空格中。

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

    特别是,图形编辑器和基于Java的IDE会忽略它的存在。 他们没有将其可视化(Unicode标准所要求的)。 然而,大多数程序员和控制台编辑器都这样做:

    在那里,很容易及早认识到问题。 其他编辑器可能会在文件/设置菜单中识别BOM的存在(Windows上的记事本++可以识别并纠正该问题),检查BOM存在的另一种选择是求助于HexEditor。 在*nix系统上,hexdump通常可用,如果不是一个图形化的变体,可以简化审核这些问题和其他问题:

    一个简单的解决方法是设置文本编辑器将文件保存为“UTF-8(无BOM)”或类似的命名法。 通常情况下,新用户会创建新文件,然后将以前的代码复制和粘贴回来。

    也有自动工具检查和重写文本文件(sed/awkrecode)。 特别是对于PHP,phptags标记更加整齐。 它将关闭和打开标记重写为长和短格式,而且还可以轻松地修复前导和后尾空格,Unicode和UTF-x BOM问题:

    phptags  --whitespace  *.php
    

    在整个包含目录或项目目录中使用是明智的。

    如果错误源被提到在?>;后面,那么这就是一些空白或原始文本被写出的地方。 PHP结束标记并不在此时终止脚本执行。 之后的任何文本/空格字符仍将作为页面内容写出。

    通常建议,特别是对于新手,应该省略后面的?>PHP关闭标记。 这就避免了其中的一小部分案件。 (通常include()d脚本是罪魁祸首。)

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

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

    如果另一个PHP语句或表达式导致打印出警告消息或通知,这也算作过早输出。

    在这种情况下,您需要避免错误,延迟语句的执行,或者使用Isset()@()-来抑制消息。

    如果您在php.ini中禁用了error_reportingdisplay_errors,则不会显示任何警告。 但是忽略错误并不能使问题消失。 过早输出后仍无法发送标头。

    因此,当header(“location:。。。”)静默重定向失败时,最好探测警告。 使用调用脚本顶部的两个简单命令重新启用它们:

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

    或者set_error_handler(“var_dump”);

    说到重定向头,您应该经常对最终代码路径使用这样的习惯用法:

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

    最好是一个实用函数,它在header()失败的情况下打印用户消息。

    PHPs输出缓冲是缓解这个问题的一个解决方案。 它通常工作可靠,但不应取代适当的应用程序结构化和从控制逻辑中分离输出。 它的实际目的是最大限度地减少到Web服务器的分块传输。

    >

  • OUTPUT_BUFFERING=设置还是有帮助的。 在现代FPM/FastCGI设置中,在PHP.ini中或通过。htaccess甚至。user.ini配置它。
    启用它将允许PHP缓冲输出,而不是立即将其传递到Web服务器。 因此,PHP可以聚合HTTP头。

    同样,可以通过调用调用脚本顶部的ob_start();来使用它。 但是,由于多种原因,它的可靠性较低:

    >

  • 即使启动第一个脚本,空格或BOM可能会在此之前被洗牌,使其无效。

    它可以隐藏HTML输出的空格。 但是一旦应用程序逻辑试图发送二进制内容(例如生成的映像),缓冲的外部输出就会成为问题。 (需要使用ob_clean()作为进一步的解决办法。)

    缓冲区的大小是有限的,当保持默认值时很容易溢出。 这也不是什么稀罕事,当它发生的时候很难追踪。

    因此,这两种方法都可能变得不可靠--特别是在开发设置和/或生产服务器之间切换时。 这就是为什么输出缓冲被广泛地认为只是一个拐杖/严格地说是一个变通方法。

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

    • 什么是输出缓冲?
    • 为什么在PHP中使用输出缓冲?
    • 使用输出缓冲是否被认为是一种不良做法?
    • 输出缓冲的用例,作为“标题已发送”的正确解决方案

    如果您之前没有得到headers警告,那么输出缓冲php.ini设置已经更改。 它可能在当前/新服务器上未配置。

    您可以始终使用headers_sent()来探测是否仍然可能。。。 发送邮件头。 这对于有条件地打印信息或应用其他回退逻辑非常有用。

    if (headers_sent()) {
        die("Redirect failed. Please click on this link: <a href=...>");
    }
    else{
        exit(header("Location: /user.php"));
    }
    

    有用的后备变通方法有:

    >

  • HTML标记

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

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

    或短暂延迟:

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

    这将导致在使用部分时无效的HTML。 大多数浏览器仍然接受它。

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

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

    虽然这通常比变通方法更符合HTML,但它会导致对支持JavaScript的客户机的依赖。

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

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

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

    • Google提供了一个类似讨论的长篇列表。
    • 当然,在堆栈溢出方面也讨论了许多具体的情况。
    • Wordpress常见问题解释了如何解决标题已发送警告问题? 以一般方式。
    • Adobe Community:PHP Development:为什么重定向不工作(头已经发送)
    • 核心常见问题:“页眉已经发送”是什么意思?
    • 一个比较彻底的解释是HTTP头和PHP header()函数--这是NicholassSolutions(Internet Archive link)的教程。 它详细介绍了HTTP,并给出了重写脚本的一些指导原则。

  • 匿名用户

    如果在发送HTTP头(带有setcookieheader)之前发送任何内容,则会触发此错误消息。 在HTTP报头之前输出内容的常见原因有:

    >

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

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

     ; ; ; ; ;为避免这种情况,只需省略结尾?>-反正不需要。

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

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

    但是,虽然输出缓冲避免了这些问题,但是您应该确定为什么应用程序在HTTP头之前输出HTTP主体。 这就像接一个电话,先讨论一下你的日子和天气,然后再告诉打电话的人他打错了电话号码。

  • 匿名用户

    我以前多次遇到这种错误,我确信所有PHP程序员以前都至少遇到过一次这种错误。

    可能的解决方案1

    此错误可能是由文件开始前或文件结束后的空格造成的。这些空格不应该在这里。

    ex)此处不应有空格

       echo "your code here";
    
    ?>
    THERE SHOULD BE NO BLANK SPACES HERE
    

    检查与导致此错误的文件关联的所有文件。

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

    可能的解决方案2:如果这不是您的情况,那么使用ob_start输出缓冲:

    <?php
      ob_start();
    
      // code 
    
     ob_end_flush();
    ?> 
    

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