提问



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



  警告:无法修改标题信息 - 已在行的/some/file.php 中通过(在/some/file.php:12 开始输出)发送的标题23



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


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

最佳参考


发送标题前没有输出!



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



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



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



  • header/header_remove

  • session_start/session_regenerate_id

  • setcookie/setrawcookie



输出可以是:[144] [145] [146] [147] [148] [149]



  • 非故意:



    • <?php之前或之后?>
    • 之前的空格
    • UTF-8字节顺序标记

    • 以前的错误消息或通知







  • 故意: [150]



    • printecho和其他产生输出的函数

    • 原始<html>部分之前<?php代码。




为什么会这样?



要理解为什么必须在输出之前发送标题
看一下典型的HTTP
响应。 PHP脚本主要生成HTML内容,但也传递了一个
Web服务器的HTTP/CGI标头集:[151]


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必须通过
首先是网络服务器的标头。它只能这样做一次。
双线后,它永远不会修改它们。


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


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



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



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



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


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


典型原因:



  1. 打印,回显



    来自printecho语句的故意输出将终止
    发送HTTP标头的机会。应用程序流必须
    重组以避免这种情况。使用功能
    和模板方案。确保在[[/em>消息之前发生header()次呼叫
    写出来了。[152]


    产生输出的函数包括



    • printechoprintfvprintf

    • trigger_errorob_flushob_end_flushvar_dumpprint_r

    • readfilepassthruflushimagepngimagejpeg




    以及其他用户定义的函数。

  2. 原始HTML区域



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


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


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



    • 在表单上放置表单处理代码。

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

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

      结果


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



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


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


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


    ?>
    
    <?php
    


    在关闭标记之后,PHP实际上会占用单个换行符。但它赢了
    补偿多个换行符或制表符或空格转换为此类空白。

  4. UTF-8 BOM



    单独的换行符和空格可能是个问题。但也有看不见的
    可能导致这种情况的字符序列。最着名的是
    UTF-8 BOM (字节顺序标记)
    大多数文本编辑器都没有显示它。它是字节序列EF BB BF,它是
    对于UTF-8编码的文档,它是可选的和冗余的。然而PHP必须要对待
    它作为原始输出。它可能会在输出中显示为字符(如果是客户端
    将该文件解释为Latin-1)或类似的垃圾。[153]


    特别是图形编辑器和基于Java的IDE没有注意到它
    存在。他们没有想象它(由Unicode标准强制要求)。
    然而,大多数程序员和控制台编辑器都会:





    在那里很容易在早期就认识到这个问题。其他编辑可能会认出来
    它存在于文件/设置菜单中(Windows上的Notepad ++可识别和
    解决问题),
    检查物料清单存在的另一个选择是使用 hexeditor
    在* nix系统hexdump通常可用,
    如果不是简化审计这些和其他问题的图形变体: [155]





    一个简单的解决方法是将文本编辑器设置为UTF-8(无BOM)
    或类似的这种命名法。通常情况下,新手会采用创造新手的方式
    文件,只需将以前的代码复制并粘贴回去。


    更正实用程序



    还有自动化工具来检查和重写文本文件
    (sed/awkrecode)。
    对于PHP,特别是phptags标签更整洁。
    它将密切和打开的标签重写为长短形式,但也很容易
    修复了前导和尾随空格,Unicode和UTF-x BOM问题: [157]


    phptags  --whitespace  *.php
    


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

  5. ?>

    之后的空格

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


    通常建议,特别是对新手来说,落后?> PHP
    关闭标签应该省略。这个避开这些案例中的一小部分。
    (通常include()d脚本是罪魁祸首。)

  6. 错误来源被称为第0行未知



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



    • 偶尔会有gzip流编码设置
      或者ob_gzhandler

    • 但它也可能是任何双重extension=模块
      生成隐式PHP启动/警告消息。

      结果


  7. 前面的错误消息



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


    在这种情况下,您需要避免错误,
    延迟语句执行,或用例如:
    isset()@() -
    什么时候不妨碍调试。[160] [161]



无错误消息



如果error_reportingdisplay_errorsphp.ini禁用,
那么就不会出现任何警告。但忽略错误不会让问题消失
远。标题仍然不能在过早输出后发送。


因此当header("Location: ...")重定向无声地失败时,它就是非常
建议探测警告。用两个简单的命令重新启用它们
在调用脚本之上:


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


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


说到重定向标题,你应该经常使用像这样的成语
这是最终的代码路径:


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


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


输出缓冲作为解决方法



PHP输出缓冲
是解决此问题的解决方法。它通常可靠地工作,但不应该
替代适当的应用程序结构并将输出与控制分开
逻辑。它的实际目的是尽量减少到网络服务器的分块转移。[162]



  1. output_buffering=
    然而,设置可以帮助。
    在php.ini中配置它
    或通过.htaccess
    甚至.user.ini上
    现代FPM/FastCGI设置。

    启用它将允许PHP缓冲输出而不是将其传递给Web服务器
    即刻。因此,PHP可以聚合HTTP头。[163] [164] [165] [166]

  2. 同样可以致电ob_start();
    在调用脚本之上。然而,由于多种原因,它不太可靠:[167]



    • 即使<?php ob_start(); ?>启动第一个脚本,空格或a
      BOM之前可能会被洗牌,使其失效。

    • 它可以隐藏HTML输出的空白。但是一旦申请
      逻辑尝试发送二进制内容(例如生成的图像)​​,
      缓冲的无关输出成为一个问题。 (必要ob_clean()
      作为更好的解决方法。)

    • 缓冲区的大小有限,并且在保留默认值时很容易溢出。
      而且这也不是罕见的,难以追查
      什么时候发生。




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


另请参见基本用法示例
在手册中,为了更多的利弊:[170]



  • 什么是输出缓冲?

  • 为什么在PHP中使用输出缓冲?

  • 使用输出缓冲是不好的做法?

  • 输出缓冲的用例是已发送标题的正确解决方案



但它在另一台服务器上运行了吗??



如果您之前没有收到标题警告,那么输出缓冲
 php.ini设置
 已经改变。它可能在当前/新服务器上未配置。 [175]


使用headers_sent()

进行检查

您始终可以使用headers_sent()来探测是否
它仍然可以......发送标题。这对条件打印很有用
信息或应用其他后备逻辑。[176]


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扩展确实。)[178]


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



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


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


更多链接




  • Google提供了类似讨论的冗长列表。

  • 当然,Stack Overflow也涵盖了许多具体案例。

  • Wordpress FAQ解释如何解决已发送的标题警告问题?以通用的方式。

  • Adob​​e社区:PHP开发:为什么重定向不起作用(已发送标题)

  • Nucleus常见问题解答:已发送页面标题是什么意思?

  • 更彻底的解释之一是HTTP Headers和PHP header()函数 - NicholasSolutions的教程(Internet Archive链接)。
    它详细介绍了HTTP,并提供了一些重写脚本的指南。


其它参考1


在发送HTTP标头(使用setcookieheader)之前发送任何时会触发此错误消息。在HTTP标头之前输出内容的常见原因是:[179] [181] [182] [183]​​]] [184] [185] [186]]]



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


     <?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[187] [188] [189] [190] [191] [192] [193] [194] [195]]]


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

其它参考2


我之前多次遇到过这个错误。我确信所有的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进行输出缓冲,如下所示:[196]


可能的解决方案2:


<?php
  ob_start();

  // code 

 ob_end_flush();
?> 

其它参考3


而不是下面的行


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


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

其它参考4


你做


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


在设置cookie之前,这是不允许的。你不能在标题之前发送任何输出,甚至不是空行。

其它参考5


正是因为这行:


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


在发送标头之前,您不应该 打印/回显

其它参考6


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


我见过这些东西;)

其它参考7


使用



  ob_start();



在脚本的最顶端,和



  ob_end_flush()函数;



在脚本的底部。
这将打开输出缓冲,并在页面缓冲后创建标题。


常见问题:



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


(复制答案来自:来源)


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


2)<?php之前和?>标记之后删除任何空白(或换行符)。


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


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


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


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


6) 启用错误报告。并告诉错误。


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


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

其它参考8


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


请参阅以下代码段:


<?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:[198]






  • 所有PHP文件必须使用Unix LF (linefeed) line ending

  • 所有PHP文件必须以single blank line结尾。

  • 关闭?>标签必须是omitted来自包含only php
  • 的文件





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

其它参考9


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


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


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

其它参考10


通常,当我们在回显或打印后发送标题时会出现此错误。如果在特定页面上出现此错误,请确保在调用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()函数之前不要输出任何字符,甚至不是空格或换行符