如何修复PHP中的“标题已发送”错误

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

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

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

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

1847516 次浏览

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

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

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

       为了避免这种情况,只需省略结束?>-无论如何都不需要它。

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

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

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

你确实需要

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

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

这是因为这条线:

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

您不应该在发送标头之前打印/回声任何东西。

发送标题之前没有输出!

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

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

修改HTTP标头的一些函数是:

输出可以是:

  • 意图:

    • printecho和其他产生输出的函数
    • 原始<?php代码之前的<html>部分。

为什么会这样?

要理解为什么必须在输出之前发送标头,有必要看一个典型的超文本传输协议PHP脚本主要生成超文本标记语言内容,但也通过Web服务器的一组HTTP/CGI标头:

HTTP/1.1 200 OKPowered-By: PHP/5.3.7Vary: Accept-EncodingContent-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>)时,它将同花顺所有收集的标头。之后它可以发送所有输出但是发送更多的HTTP标头是不可能的。

你怎么能发现过早输出发生在哪里?

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

警告:无法修改标头信息-标头已由(输出从开始) /www/usr2345/htdocs/auth.php:52)in/www/usr2345/htdocs/index.php100

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

括号内的“输出开始于”注释更重要。它为上一个输出的来源命名。在这个例子中,它是auth.php行#0。这就是你必须寻找过早输出的地方。

典型原因:

  1. 打印,回声

    printecho语句的故意输出将终止发送HTTP标头的机会。必须重组应用程序流以避免这种情况。使用函数和模板方案。确保header()调用发生之前消息都写好了

    产生输出的函数包括

    • printechoprintfvprintf
    • trigger_errorob_flushob_end_flushvar_dumpprint_r
    • readfilepassthruflushimagepngimagejpeg


    和其他用户定义的函数。

  2. 原始超文本标记语言区域

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

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

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

    • 将表单处理代码放在脚本之上。
    • 使用临时字符串变量延迟消息。
    • 实际输出逻辑和混合的超文本标记语言输出应该在最后。
  3. <?php之前的空白表示“script.php第1行”警告

    如果警告指的是内联输出1,那么它主要是前导空白,文本或超文本标记语言在打开<?php令牌之前。

     <?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)或类似的“垃圾”。

    特别是图形编辑器和基于Java的IDE忽略了其存在。他们不可视化它(由Unicode标准强制)。然而,大多数程序员和控制台编辑器都这样做:

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

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

    beav hexeditor showing utf-8 bom

    一个简单的解决方案是将文本编辑器设置为“UTF-8(无BOM)”保存文件或者类似于这样的命名法。通常新人会求助于创建新文件并将之前的代码复制粘贴回来。

    更正实用程序

    还有自动工具来检查和重写文本文件(#0/#1recode)。对于PHP,有#0标签整理器。它将关闭和打开的标签重写为长和短形式,但也很容易修复前导和尾随空格、Unicode和UTF-x BOM问题:

    phptags  --whitespace  *.php

    在整个包含或项目目录上使用是安全的。

  5. ?>后出现空白

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

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

  6. 错误源提到为“第0行未知”

    如果没有错误源,它通常是PHP扩展或php.ini设置#36825;化了

    • 它偶尔是gzip流编码设置或#0.
    • 但它也可以是任何双重加载的extension=模块生成隐式PHP启动/警告消息。

  7. 前面的错误消息

    如果另一个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输出缓冲是缓解此问题的解决方法。它通常可以可靠地工作,但不应该替代正确的应用程序结构并将输出与控制分离逻辑。它的实际目的是最小化到网络服务器的分块传输。

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

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

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

    • 它可以隐藏超文本标记语言输出的空格。但是一旦应用程序逻辑尝试发送二进制内容(例如生成的图像),缓冲的无关输出成为一个问题。(必要ob_clean()#36825;的解决方案)

    • 缓冲区的大小有限,如果保留为默认值,很容易溢出。这也不是什么稀罕事难以追踪当它发生时

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

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

但它在另一个服务器上工作!?

如果您之前没有收到标头警告,则输出缓冲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"));}

有用的后备解决方法是:

  • 超文本标记语言<meta> tag

    如果您的应用程序在结构上难以修复,那么一个简单的(但有点不专业)允许重定向的方法是注入超文本标记语言<meta>标签。可以使用以下方式实现重定向:

     <meta http-equiv="Location" content="http://example.com/">

    或者有短暂的延迟:

     <meta http-equiv="Refresh" content="2; url=../target.html">

    当使用超过<head>部分时,这会导致无效的超文本标记语言。大多数浏览器仍然接受它。

  • JavaScript重定向

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

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

    虽然这通常比<meta>解决方案更符合超文本标记语言,它依赖于支持JavaScript的客户端。

然而,当真正的HTTP标头()时,这两种方法都可以接受回退呼叫失败。理想情况下,您总是将其与用户友好的消息和作为最后手段的可点击链接。(例如,http_redirect()PECL扩展没有。)

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

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

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

更多链接

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

我见过这些东西;)

我以前多次遇到过这个错误,我确信所有PHP程序员以前至少遇到过一次这个错误。

可能的解决方案1

此错误可能是由文件开头的空格之前或文件结尾的空格之后引起的。这些空格不应该在这里。

ex这里不应该有空白的地方

   echo "your code here";
?>THERE SHOULD BE NO BLANK SPACES HERE

检查与导致此错误的文件关联的所有文件。

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

可能的解决方案2:如果这不是你的情况,那么使用ob_start输出缓冲:

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

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

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

请参阅此代码片段:

<?phpinclude('a_important_file.php'); //really really really bad practiseheader("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:


  • 所有PHP文件必须使用#0。<强>
  • 所有PHP文件必须以#0结尾。<强>
  • 结束?>标签必须是#0,来自包含#1的文件<强>

相信我,遵循这些标准可以让你从生活中节省很多时间:)

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

不可预测错误示例:

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

再举一个例子:

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

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

常见问题:

(转载自:来源

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

1)header(.......);命令之前不应有任何输出(即echo..或超文本标记语言代码)。

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;

6)打开错误报告,并找到错误。您的错误可能是由一个不工作的函数引起的。当您打开错误报告时,您应该始终首先修复最上面的错误。例如,它可能是“警告:date_default_timezone_get():依赖系统的时区设置不安全。”-然后再往下看,您可能会看到“标头未发送”错误。修复最上面(第一次)错误后,重新加载您的页面。如果您仍然有错误,然后再次修复最上面的错误。

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

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

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

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

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