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

  显示原文与译文双语对照的内容

运行脚本时,出现了如下几个错误:

/some/file.php行上 23, 警告由( 在/some/file. php:12, 输出启动): 不能修改头信息- 头已经发

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

这个原因是什么原因? 以及如何修复它?

时间:

发送邮件头前没有输出 !

函数,这些函数发送/修改HTTP标题必须被调用任何输出之前由

警告:无法修改标头信息- 标头已经发送( 输出在 script:line )

某些修改HTTP标头的函数如下:

输出可以是:

  • 有意:

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

页眉/输出总是在页眉后面 。 PHP必须首先将头传递给web服务器。 它只能做一次。 在双linebreak之后,它可以修改它们。

当PHP接收到第一个输出( printecho<html> ) 它将清空收集的所有的邮件头。 之后它可以发送它想要的所有输出。 但是发送更多的HTTP报头是不可能的。

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

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

在行将警告:上/www/usr2345/htdocs/index.php 不能修改头信息- 头已经发送的( 输出开始在/www/usr2345/htdocs/授权- php: 52 )

这里"第 100行"引用了 header() 调用失败的脚本。

括号内的" - 输出在"注释更重要。 它是先前输出的源。 在本例中,它是 auth.php52 。 在那里你必须寻找过早的输出。

典型原因:

  1. 打印,echo

    printecho 语句的有意输出将终止发送HTTP标头的机会。 必须重新调整应用程序流程以避免。 使用函数和模板化方案。header() 调用继续发生,然后消息被写出来。

    生成输出的函数包括

    • printechoprintfvprintf
    • 宋体,trigger_errorob_flushob_end_flushvar_dumpprint_r
    • 宋体,readfilepassthruflushimagepngimagejpeg


    其他和user-defined函数。

  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实际上会吃掉一个 linebreak 。 但它不会补偿多个换行符或者制表符或者空格移动到这种间隙。

  4. UTF-8物料清单

    Linebreaks和空格可能是一个问题。 但也有"不可见"字符序列可以导致这一点。 最著名的是它的UTF-8物料清单 ( Byte-Order-Mark ) 哪个不是由大多数文字编辑器的显示。 它是字节序列 EF BB BF,它是可选的,对于UTF-8编码的文档是多余的。 然而,PHP必须把它当作原始输出。 在输出( 如果客户端将文档解释为 Latin-1 ) 或者类似该控件可以显示为字符

    特别是图形化编辑器和基于Java的ide对它的存在视而不见。 他们不会把它想象为( 由Unicode标准使用) 。 但是大多数程序员和控制台编辑器都有:

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

    很容易在早期识别出问题。 其他编辑器可能鉴别它的在文件/设置菜单( Windows 上可以解决这个问题的识别和 , NotePad++ )的出现,另一个选项来检查这种清单是求助于形式出现一个 hexeditor 。 在 *nix 系统上,hexdump 通常是可用的,如果不是图形化变量,可以简化审核这些和其他问题:

    beav hexeditor showing utf-8 bom

    一个简单的修复是设置文本编辑器将文件保存为"utf-8 ( 无BOM表) ) 或者类似的命名。 通常新的新文件会创建新的文件,只是copy&粘贴以前的代码。

    校正工具

    还有自动化工具来检查和重写文本文件( sed/awk 或者 recode ) 。 对于 PHP,有 phptags 标记整齐的 。 它将关闭和打开的标签改写为长的和短的形式,而且很容易地修复前导和尾随空白,Unicode和 UTF-x BOM问题:

    
    phptags --whitespace *.php
    
    

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

  5. ?> 后的空白

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

    特别是对新手来说,紧跟 ?> PHP关闭标记应该省略。 在这种情况下,回避了 。 ( 通常 include()d 脚本是罪魁祸首。)

  6. 错误源"在第 0行未知""

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

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

  7. 前面的错误消息

    如果另一个PHP语句或者表达式导致警告消息或者通知被删除,那么也会算作过早输出。

    在这种情况下,你需要避免错误,延迟语句执行,或者使用 比如 isset() 或者 @() 取消消息,当以后不妨碍调试时。

无错误信息

如果你有每个 php.inierror_reporting 或者 display_errors 已经禁用,则不会警告将会显示出来。 但是忽略错误不会让问题消失。 无法在过早输出后发送标头。

因此,当 header("Location:.. .") 重定向失败时,建议探测警告。 重新启用调用脚本上的两个简单命令:


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

或者 set_error_handler("var_dump"); 如果所有其他都失败。

谈到重定向标头,你应该经常使用类似于 final 代码路径的习惯用法:


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

Preferrably甚至一个实用程序函数,在 header() 失败时打印用户消息。

输出缓冲作为变通方法

php 输出缓冲是一个缓解这个问题的解决方案。 它通常工作得可靠,但不应该替代正确的应用程序构造和将输出与控制逻辑分离。 它的实际目的是最小化到web服务器的分块传输。

  1. output_buffering= 设置仍然可以帮助。 在现代柔性参数化/fastcgi setups,中配置这里字段 php.ini 或者通过 。htaccess 甚至 。用户。ini.
    启用它将允许PHP缓冲输出,而不是立即传递给web服务器。 因此,PHP可以聚合HTTP头。

  2. 它同样可以与调用脚本上的ob_start(); 调用。 但由于多种原因,它的可靠性较差:

    • 即使 <?php ob_start();?> 启动了第一个脚本,空白或者BOM也可能被打乱,呈现它无效。

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

    • 缓冲区大小有限,当离开时很容易溢出。 这也不是一种罕见的情况,很难在它发生时跟踪它。

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

请参阅手册中的基本用法示例 。and,了解更多的优缺点:

但它在其他服务器上工作? !

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

正在使用 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">
    
    

    这将导致 non-valid HTML在 <head> 部分被使用时。 大多数浏览器仍然接受它。

  • JavaScript重定向

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

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

    虽然这通常比 <meta> 解决方案更加兼容,但它会对JavaScript-capable客户端产生依赖性。

然而,当真正的HTTP header() 调用失败时,这两种方法都可以接受。 理想情况下,你总是将它的与用户友好的消息和可以点击的链接组合在一起。 ( 例如 http_redirect()的扩展是什么)

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

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

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

进一步链接

这里错误消息之前得以触发发送什么时发送的httpheader ( 使用 setcookie 或者 header ) 。 在HTTP标头之前输出内容的常见原因如下:

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

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

为了避免这种情况,只需去掉关闭的?> - 它是不需要的。

ob_end_flush, 转输出缓冲上应该将问题都消失了,所有输出的调用完成之后 ob_start 缓冲在内存中,直到你释放该缓冲区,

然而,虽然输出缓冲避免了问题,但你应该确定应用程序在HTTP头之前输出HTTP正文的原因。 这就像打电话和讨论你的一天和天气之前,告诉调用者他的号码有误。

我多次遇到这个错误 before.and 我确信所有的PHP程序员at-least都曾经收到这个错误。 要解决这里错误,你可以按照你的问题级别解决使用解决方案:

1: 可能的解决方案

在文件的末尾after,你可能已经留出了空位 之前或者之后 (? > ) i.e.


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.

大多数时候,这应该会解决你的problem.Do 检查与你 require 相关的所有文件。

保存时保存 file.This 不应 happen, 注意: 有时 EDITOR(IDE) 像 gedit(a default linux editor) 添加一个空白向前移动一行 如果你正在使用 linux 。 你可以使用VI编辑器删除空格/线条? 页面末尾的> 。

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

2: 可能的解决方案


<?php
 ob_start();

//code 

 ob_end_flush();
?> 

而不是下面的行


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

这肯定会解决你的问题。 我遇到了同样的问题,但是我通过上面的方式解决了标题位置。

你做


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

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

是因为这一行:


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

你不应该 打印/echo 什么在发送响应信息之前头。

简单的提示:脚本中的一个简单空间( 或者不可见的特殊字符),恰好在第一个 <?php 标记之前,可以导致 ! 特别是当你在一个团队中工作并且有人使用"弱电"ide或者在文件中出现了奇怪的文本编辑器时。

我看到过这些东西;

另一个错误的实践可以调用这个还没有声明的问题。

查看这里代码段:


<?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 上面 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文件必须就餐者
  • 关闭 > 标记必须是包含 only php的文件的omitted

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

或者你也可以尝试在任何文件 like(header.php) 包含之前调用header函数

我被卡在某天的这个错误,我终于找到了我的问题在于 code.As 才明白他误差主要因为你是发送一些输出任何标头已经发送之前。 显然我的例子是在 page.check 标记之前的空格,如果在打开php标记. hope 之前没有空格,这也适用于你

...