如何解决PHP中的“Headers already sent”错误

当运行我的脚本时,我得到了几个像这样的错误:

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

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

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


在发送标题之前没有输出!

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

警告:不能修改标题信息 - 已经发送的标题(输出在脚本:行开始)

一些修改HTTP头的函数是:

  • header / header_remove
  • session_start / session_regenerate_id
  • setcookie / setrawcookie
  • 输出可以是:

  • 意外:

  • <?php或之后?>之前的空格
  • UTF-8字节顺序具体标记
  • 以前的错误消息或通知
  • 故意的:

  • printecho和其他功能产生输出
  • Raw <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行的/www/usr2345/htdocs/index.php中输出的标题(输出开始于 /www/usr2345/htdocs/auth.php:52)

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

    括号内的“输出始于”注释更为重要。 它指定了以前输出的来源。 在这个例子中它是auth.php52 。 这就是你必须寻找过早产出的地方。

    典型原因:

  • 打印,回声

    printecho语句的意图输出将终止发送HTTP头的机会。 应用程序流程必须重构以避免这种情况。 使用函数和模板方案。 在写出消息之前确保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 for“script.php line 1 ”警告前的空格

    如果警告引用第1行的输出,那么它在打开<?php标记之前主要是引导空白 ,文本或HTML。

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

    同样,它可以发生在附加的脚本或脚本部分:

    ?>
    
    <?php
    

    PHP实际上在关闭标签之后吃了一个linebreak。 但它不会补偿多个换行符或制表符或空格转移到这些空白处。

  • UTF-8 BOM

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

    特别是图形编辑器和基于Java的IDE不知道它的存在。 它们没有将其可视化(由Unicode标准承担责任)。 然而,大多数程序员和控制台编辑却这样做:

    显示UTF-8 BOM占位符的joes编辑器,以及MC编辑器的一个点

    在那里很容易早期发现问题。 其他编辑可能会在文件/设置菜单中识别它的存在(Windows上的Notepad ++可以识别并解决问题),另一种检查BOM存在的方法是使用hexeditor 。 在* nix系统上,通常可以使用hexdump ,如果不是一个简化审计这些和其他问题的图形变体:

    显示utf-8 bom的beav hexeditor

    一个简单的解决方法是将文本编辑器设置为将文件保存为“UTF-8(无BOM)”或类似的术语。 通常新手会采用创建新文件的方式,并将之前的代码复制并粘贴回原来的位置。

    更正实用程序

    还有自动化工具来检查和重写文本文件( sed / awkrecode )。 对于PHP专门有phptags标签phptags 。 它将长时间和短时间的关闭和打开标签重写,但也很容易修复前导和尾随空白,Unicode和UTF-x BOM问题:

    phptags  --whitespace  *.php
    

    在整个包含或项目目录上使用是理智的。

  • ?>之后的空格

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

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

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

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

  • 偶尔是gzip流编码设置或ob_gzhandler
  • 但它也可以是任何双重加载的extension= module生成隐式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()失败的情况下打印用户消息。

    输出缓冲作为解决方法

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

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

  • 它也可以通过调用ob_start(); 在调用脚本之上。 然而,由于多种原因,这不太可靠:

  • 即使<?php ob_start(); ?> <?php ob_start(); ?>开始第一个脚本,空白或BOM可能会在之前被洗牌,使其无效。

  • 它可以隐藏HTML输出的空白。 但是,只要应用程序逻辑尝试发送二进制内容(例如生成的图像)​​,缓冲的无关输出就成为问题。 (必须将ob_clean()作为其他解决方法。)

  • 缓冲区的大小是有限的,并且在默认情况下很容易溢出。 这也不是一件难得的事情,当它发生时很难追踪。

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

    另见手册中的基本使用示例,以及更多优点和缺点:

  • 什么是输出缓冲?
  • 为什么在PHP中使用输出缓冲?
  • 是否使用输出缓冲被认为是一种不好的做法?
  • 用于输出缓冲的用例作为“已发送头文件”的正确解决方案
  • 但它在另一台服务器上工作!?

    如果您以前没有收到标题警告,那么输出缓冲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头()调用失败时都会产生可接受的回退。 理想情况下,你总是将这个与用户友好的消息和可点击链接结合起来作为最后的手段。 (这例如是http_redirect()PECL扩展所做的)。

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

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

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

    更多链接

  • Google提供了类似讨论的冗长列表。
  • 当然,Stack Overflow也涵盖了很多具体的案例。
  • WordPress的常见问题解释如何解决报头已发出警告问题? 以通用的方式。
  • Adobe社区:PHP开发:为什么重定向不起作用(头文件已经发送)
  • Nucleus FAQ:“页面标题已发送”是什么意思?
  • 其中一个更深入的解释是HTTP Headers和PHP header()函数 - NicholasSolutions的教程(Internet Archive链接)。 它详细介绍了HTTP,并为重写脚本提供了一些指导。

  • 在发送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_errors或error_reporting配置,但应该修复问题。
    常见的原因是访问未定义的数组元素(例如$_POST['input']而不使用emptyisset来测试输入是否设置),或者使用未定义的常量而不是字符串文本(如$_POST[input] ,注意缺失的引号)。
  • 打开输出缓冲应该使问题消失; 在调用ob_start之后,所有输出缓冲在内存中,直到释放缓冲区为止,例如使用ob_end_flush

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


    我得到了这个错误很多次,我相信所有的PHP程序员至少有一次出现这个错误。 要解决这个错误,您可以根据问题级别解决使用问题:

    可能的解决方案1

    你可能会在之前或之后留下空格(在?>之后的文件末尾)即ie

    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文件关联的所有文件。

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

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

    可能的解决方案2

    <?php
      ob_start();
    
      // code 
    
     ob_end_flush();
    ?> 
    
    链接地址: http://www.djcxy.com/p/5917.html

    上一篇: How to fix "Headers already sent" error in PHP

    下一篇: Flex Mobile Item Renderer in List