首页 文章

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

提问于
浏览
836

运行我的脚本时,我收到了以下几个错误:

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

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

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

11 回答

  • 76

    正是因为这条线:

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

    在发送 Headers 之前,您不应该 print/echo .

  • 1

    发送 Headers 前没有输出!

    必须调用发送/修改HTTP标头的函数 before any output is made . 摘要⇊否则呼叫失败:

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

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

    输出可以是:

    • 无心:

    • <?php 之前或 ?> 之后的空白

    • 特别是UTF-8 Byte Order Mark

    • 以前的错误消息或通知

    • 故意:

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

    页面/输出始终跟在 Headers 之后 . PHP必须首先将标头传递给Web服务器 . 它只能这样做一次 . 双线后,它永远不会修改它们 .

    当PHP收到第一个输出( printecho<html> )时,它将刷新所有收集的 Headers . 之后它可以发送它想要的所有输出 . 但是,发送更多HTTP标头是不可能的 .

    如何找出过早输出发生的位置?

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

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

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

    括号内的“输出开始于”注释更为重要 . 它表示先前输出的来源 . 在这个例子中它是 auth.phpline 52 . 那是你必须寻找过早产出的地方 .

    典型原因:

    打印,回声

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

    产生输出的函数包括

    • printechoprintfvprintf

    • trigger_errorob_flushob_end_flushvar_dumpprint_r

    • readfilepassthruflushimagepngimagejpeg

    其中包括用户定义的函数 .

    原始HTML区域

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

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

    使用模板方案将处理与输出逻辑分开 .

    • 在脚本顶部放置表单处理代码 .

    • 使用临时字符串变量来推迟消息 .

    • 实际输出逻辑和混合HTML输出应该遵循最后一个 .

    <?php之前的空白“script.php第1行”警告

    如果警告引用 1 行中的输出,则它在打开 <?php 标记之前主要是 whitespace ,文本或HTML .

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

    同样,附加脚本或脚本部分也可能出现这种情况:

    ?>
    
    <?php
    

    在关闭标记之后,PHP实际上会占用一个换行符 . 但它不会补偿多个换行符或制表符或空格转移到这些空白中 .

    UTF-8 BOM

    单独的换行符和空格可能是个问题 . 但是也有"invisible"字符序列可以导致这种情况 . 最着名的是UTF-8 BOM (Byte-Order-Mark),它不是字节序列 EF BB BF ,对于UTF-8编码文档来说是可选的和冗余的 . 然而,PHP必须将其视为原始输出 . 它可能会在输出中显示为字符  (如果客户端将文档解释为Latin-1)或类似的"garbage" .

    特别是图形编辑器和基于Java的IDE不知道它的存在 . 它们没有可视化(由Unicode标准强制要求) . 然而,大多数程序员和控制台编辑器都会:

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

    在那里很容易早期发现问题 . 其他编辑可以在文件/设置菜单中识别它的存在(Windows上的记事本可以识别和remedy the problem),检查BOM存在的另一个选项是求助于 hexeditor . 在* nix系统上hexdump通常是可用的,如果不是图形变体,它简化了审计这些和其他问题:

    beav hexeditor showing utf-8 bom

    一个简单的解决方法是将文本编辑器设置为将文件保存为“UTF-8(无BOM)”或类似的命名法 . 通常,新手通常会创建新文件,而只是复制并粘贴以前的代码 .

    更正实用程序

    还有自动化工具来检查和重写文本文件(sed/awkrecode ) . 对于PHP,特别是phptags tag tidier . 它将close和open标记重写为长形和短形,但也可以轻松修复前导和尾随空格,Unicode和UTF-x BOM问题:

    phptags  --whitespace  *.php
    

    在整个包含或项目目录上使用是明智的 .

    空格后?>

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

    通常建议,特别是新手,应该省略尾随 ?> PHP关闭标签 . 这避免了这些案件中的一小部分 . (通常 include()d 脚本是罪魁祸首 . )

    错误来源提到“第0行未知”

    如果没有错误源具体化,它通常是PHP扩展或php.ini设置 .

    • 偶尔 gzip 流编码设置or the 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"); 如果一切都失败了 .

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

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

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

    输出缓冲作为变通方法

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

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

    • 同样可以调用ob_start();调用脚本 . 然而,由于多种原因,它不太可靠:

    • 即使 <?php ob_start(); ?> 启动第一个脚本,空格或BOM也可能在之前被洗牌,rendering it ineffective .

    • 它可以隐藏HTML输出的空白 . 但是一旦应用程序逻辑尝试发送二进制内容(例如生成的图像),缓冲的无关输出就成了问题 . (需要 ob_clean() 作为更好的解决方法 . )

    • 缓冲区的大小有限,并且在保留默认值时很容易溢出 . 而且,当它发生时,这也不是罕见的事情 .

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

    另请参阅手册中的basic usage example,以获取更多优点和缺点:

    但它在其他服务器上运行了!?

    如果之前未收到 Headers 警告,则output buffering php.ini setting已更改 . 它可能在当前/新服务器上未配置 .

    使用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 redirect可用于页面重定向:

    <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设置等)

    更多链接

  • 24

    而不是下面的行

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

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

    要么

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

    它肯定会解决你的问题 . 我遇到了同样的问题,但我通过以上述方式编写 Headers 位置解决了 .

  • 14

    常见问题:

    (复制自:source

    ====================

    1) header(.......); 命令之前不应有任何输出(即 echo.. 或HTML代码) .

    2)<?php 之前和 ?> 标记之后删除任何 white-space (或 newline ) .

    3) GOLDEN RULE! - 检查那个php文件(以及,如果你 include 其他文件)是否有 UTF8 without BOM 编码(而不仅仅是 UTF-8 ) . 这在许多情况下都是问题(因为 UTF8 编码文件在php文件的开头有一些特殊字符,你的文本编辑器没有显示)!!!!!!!!!!!

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

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

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

    6) Turn on error reporting, and find the error. 您的错误可能是由无效的功能引起的 . 当您打开错误报告时,您应该始终首先修复最顶层的错误 . 例如,它可能是"Warning: date_default_timezone_get(): It is not safe to rely on the system's timezone settings." - 然后再向下你可能会看到"headers not sent"错误 . 修复最顶层(第一个)错误后,重新加载页面 . 如果仍有错误,请再次修复最顶层的错误 .

    7) 如果以上都没有帮助,使用JAVSCRIPT重定向(但是,非强烈推荐的方法),可能是自定义案例中的最后一次机会......:

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

    有时当开发进程有WIN工作站和LINUX系统(托管)时,在代码中你没有看到相关行之前的任何输出,它可能是文件的格式化和缺乏Unix LF(换行)行结束 .

    我们通常做的是为了快速解决这个问题,重命名文件并在LINUX系统上创建一个新文件而不是重命名文件,然后将内容复制到该文件中 . 很多时候,这解决了这个问题,因为在WIN中创建的一些文件一旦移动到托管就会导致此问题 .

    此修复程序是我们通过FTP管理的网站的简单修复,有时可以节省我们的新团队成员一段时间 .

  • 29

    一个简单的提示:脚本中的一个简单空格(或不可见的特殊字符),就在第一个 <?php 标记之前,可能会导致这种情况!特别是当你在一个团队中工作而某人正在使用"weak" IDE或者在文件中乱用奇怪的文本编辑器时 .

    我见过这些东西;)

  • 20

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

    Possible Solution 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 .

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

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

    Possible Solution 2:

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

    这将打开输出缓冲,并在页面缓冲后创建 Headers .

  • 105

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

    不可预知的错误示例:

    <?php //a white-space before <?php also send for output and arise error
    session_start();
    session_regenerate_id();
    
    //your page content
    

    还有一个例子:

    <?php
    includes 'functions.php';
    ?> <!-- This new line will also arise error -->
    <?php
    session_start();
    session_regenerate_id();
    
    //your page content
    

    结论:在调用 session_start()header() 函数之前不要输出任何字符,甚至不是空格或换行符

  • 183

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

    • 意外空格,通常在文件的开头或结尾,如下所示:
    <?php
    // Note the space before "<?php"
    ?>
    

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

    • Byte order marks在php文件的开头 . 用十六进制编辑器检查你的php文件,看看是否是这种情况 . 它们应该以字节 3F 3C 开头 . 您可以安全地从文件的开头删除BOM EF BB BF .

    • 显式输出,例如对 echoprintfreadfilepassthru 的调用, <? 之前的代码等 .

    • 如果设置了display_errors php.ini属性,则由php输出警告 . 而不是崩溃程序员的错误,PHP默默地修复错误并发出警告 . 虽然您可以修改display_errorserror_reporting配置,但您应该修复此问题 .
      常见的原因是访问数组的未定义元素(例如 $_POST['input'] ,不使用emptyisset来测试输入是否已设置),或使用未定义的常量而不是字符串文字(如 $_POST[input] ,请注意缺少的引号) .

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

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

  • 25

    你做

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

    在设置cookie之前,这是不允许的 . 您不能在 Headers 之前发送任何输出,甚至不能发送空白行 .

  • 38

    另一个不好的做法可以调用这个尚未说明的问题 .

    请参阅以下代码段:

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

    事情还可以,对吧?

    如果“a_important_file.php”是这样的:

    <?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 :


    • All PHP files MUST use the Unix LF (linefeed) line ending.

    • All PHP files MUST end with a single blank line.

    • The closing ?> tag MUST be omitted from files containing only php


    相信我,遵循这些标准可以为你节省大量的时间:)

相关问题