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

Mos*_*s89 833 php header

运行我的脚本时,我收到几个这样的错误:

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

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

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

mar*_*rio 2921

发送标头前没有输出!

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

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

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

输出可以是:

  • 故意的:

    • print,echo等功能产生输出
    • 原始<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>
Run Code Online (Sandbox Code Playgroud)

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

当PHP接收第一输出(print,echo,<html>),它会 刷新所有收集的头.之后它可以发送它想要的所有输出.但是,发送更多HTTP标头是不可能的.

你怎么知道过早输出发生在哪里?

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

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

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

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

典型原因:

  1. 打印,回声

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

    产生输出的函数包括

    • print,echo,printf,vprintf
    • trigger_error,ob_flush,ob_end_flush,var_dump,print_r
    • readfile,passthru,flush,imagepng,imagejpeg


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

  2. 原始HTML区域

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

    <!DOCTYPE html>
    <?php
        // Too late for headers already.
    
    Run Code Online (Sandbox Code Playgroud)

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

    • 在脚本上放置表单处理代码.
    • 使用临时字符串变量来推迟消息.
    • 实际输出逻辑和混合HTML输出应该遵循最后一个.

  3. 之前<?php的空格为"script.php 第1行 "警告

    如果警告引用了行中的输出1,那么它主要在开始令牌之前引导空格,文本或HTML <?php.

     <?php
    # There's a SINGLE space/newline before <? - Which already seals it.
    
    Run Code Online (Sandbox Code Playgroud)

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

    ?>
    
    <?php
    
    Run Code Online (Sandbox Code Playgroud)

    PHP实际上吃了一个单一的关闭标签后断行.但它不会补偿多个换行符或标签或空格转移到这些空白.

  4. UTF-8 BOM

    仅有线路和空格可能是个问题.但也有"隐形"字符序列可以导致这种情况.最着名的 是大多数文本编辑器都没有显示的 UTF-8 BOM(字节顺序标记).这是字节序列EF BB BF,对于UTF-8编码的文档是可选的和冗余的.然而,PHP必须将其视为原始输出.它可能显示为输出中的字符(如果客户端将文档解释为Latin-1)或类似的"垃圾".

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

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

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

    beav hexeditor显示utf-8 bom

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

    纠正工具

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

    phptags  --whitespace  *.php
    
    Run Code Online (Sandbox Code Playgroud)

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

  5. 空白之后 ?>

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

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

  6. 错误源提到"第0行未知"

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

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

  7. 前面的错误消息

    如果另一个PHP语句或表达式导致警告消息或通知被打印出来,那么这也会被视为过早输出.

    在这种情况下,您需要避免错误,延迟语句执行,或者使用eg isset()@()- 来抑制消息, 如果以后没有阻碍调试.

没有错误消息

如果您有error_reporting或已display_errors禁用php.ini,则不会显示任何警告.但忽略错误不会使问题消失.过早输出后仍然无法发送标题.

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

error_reporting(E_ALL);
ini_set("display_errors", 1);
Run Code Online (Sandbox Code Playgroud)

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

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

exit(header("Location: /finished.html"));
Run Code Online (Sandbox Code Playgroud)

优选地甚至是实用功能,其在header()出现故障时打印用户消息.

输出缓冲作为变通方法

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

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

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

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

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

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

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

另请参阅 手册中的基本用法示例,以及更多优缺点:

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

如果您之前没有收到标题警告,则输出缓冲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"));
}
Run Code Online (Sandbox Code Playgroud)

有用的后备解决方法是:

  • HTML <meta>标记

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

     <meta http-equiv="Location" content="http://example.com/">
    
    Run Code Online (Sandbox Code Playgroud)

    或者延迟很短:

     <meta http-equiv="Refresh" content="2; url=../target.html">
    
    Run Code Online (Sandbox Code Playgroud)

    这通过该<head>部分使用时会导致无效的HTML .大多数浏览器仍然接受它.

  • JavaScript重定向

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

     <script> location.replace("target.html"); </script>
    
    Run Code Online (Sandbox Code Playgroud)

    虽然这通常比<meta>解决方法更符合HTML ,但它依赖于支持JavaScript的客户端.

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

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

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

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

更多链接

  • 从php文件末尾删除关闭`?>`通常是一种很好的做法,有助于最大限度地减少这些错误.文件末尾不会出现不需要的空格,您仍然可以在以后向响应添加标题.如果使用输出缓冲,它也很方便,并且不希望在包含的文件生成的部分的末尾看到添加的不需要的空格. (4认同)
  • 仅遗漏了一个细节,您可能需要添加。`$file = $line = null; headers_sent($file, $line); 如果错误消息省略了该信息,die("$file:$line");` 将准确告诉您发送标头的内容。不知道为什么,但有时似乎就是这样。 (2认同)

phi*_*hag 193

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

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

  • 字节顺序标记在php文件的开头.用十六进制编辑器检查你的php文件,看看是否是这种情况.它们应该从字节开始3F 3C.您可以安全地EF BB BF从文件的开头删除BOM .
  • 显式输出,如呼叫echo,printf,readfile,passthru,代码前<?
  • 如果display_errors设置了php.ini属性,则由php输出警告.而不是崩溃程序员的错误,PHP默默地修复错误并发出警告.虽然您可以修改display_errorserror_reporting配置,但您应该修复此问题.
    常见的原因是访问数组的未定义元素(例如,$_POST['input']不使用emptyisset测试输入是否已设置),或使用未定义的常量而不是字符串文字(如下所示$_POST[input],请注意缺少的引号).

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

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


Man*_*ava 119

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

可能解决方案1:

你可能已经留出了空位前,(后文件的结尾?>)之后

   echo "your code here";

?>
THERE SHOULD BE NO BLANK SPACES HERE
Run Code Online (Sandbox Code Playgroud)

大多数情况下,这应该可以解决您的问题.请检查与您相关的所有文件__CODE__.

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

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

可能的方案2:

<?php
  ob_start();

  // code 

 ob_end_flush();
?> 
Run Code Online (Sandbox Code Playgroud)

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

  • `ob_start()`只是隐藏了问题; 不要用它来解决这个特殊的问题. (18认同)
  • `ob_start()`不会*"隐藏"*问题,它**解决了问题. (3认同)
  • 解决了我的问题。 (2认同)

Ips*_*out 83

而不是下面的行

//header("Location:".ADMIN_URL."/index.php");
Run Code Online (Sandbox Code Playgroud)

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

要么

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

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

  • 它没关系,但如果用户禁用java脚本,它将无法正常工作. (17认同)

Set*_*gie 40

你做

printf ("Hi %s,</br />", $name);
Run Code Online (Sandbox Code Playgroud)

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


Sar*_*raz 32

正是因为这条线:

printf ("Hi %s,</br />", $name);
Run Code Online (Sandbox Code Playgroud)

在发送标题之前,不应打印/回显任何内容.


Jor*_*orn 30

常见问题:

(复制自:来源)

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

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

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

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

4)后,header(...);您必须使用exit;

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

header("location: http://example.com",  true,  301 );  exit;
Run Code Online (Sandbox Code Playgroud)

6) 打开错误报告,找到错误. 您的错误可能是由无效的功能引起的.当您打开错误报告时,您应该始终首先修复最顶层的错误.例如,它可能是"Warning:date_default_timezone_get():依赖系统的时区设置是不安全的." - 然后再下来你可能会看到"标题未发送"错误.修复最顶层(第一个)错误后,重新加载页面.如果仍有错误,请再次修复最顶层的错误.

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

echo "<script type='text/javascript'>window.top.location='http://website.com/';</script>"; exit;
Run Code Online (Sandbox Code Playgroud)


Sli*_*liq 26

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

我见过这些东西;)


MD.*_*oob 21

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

请参阅以下代码段:

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

事情还可以,对吧?

如果"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-------------------
Run Code Online (Sandbox Code Playgroud)

这不行吗?为什么?因为已经生成了新行.

现在,虽然这不是一种常见的情况,如果您使用的MVC框架在将内容切换到控制器之前加载了大量文件,那该怎么办呢?这不是一种罕见的情况.为此做好准备.

来自PSR-2 2.2:


  • 所有PHP文件必须使用Unix LF (linefeed) line ending.
  • 所有PHP文件必须以a结尾single blank line.
  • 结束?>标签必须omitted来自包含的文件only php

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

  • 根据几个标准(例如Zend),你不应该在任何情况下将结束`?>`标记放在任何文件中 (2认同)

Lup*_*pin 15

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

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

对于我们通过FTP管理的网站,此修复程序很容易修复,有时可以节省新团队成员的时间.


归档时间:

查看次数:

1333908 次

最近记录:

6 年,1 月 前