为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

隔江千里 2019-05-20 15:11:24
运行我的脚本时,我收到几个这样的错误:警告:不能更改头信息-头已经发出(输出开始/some/file.php:12)在/some/file.php上线23错误消息中提到的行包含header()和setcookie()调用。这可能是什么原因?以及如何解决它?
查看完整描述

7 回答

?
侃侃无极

TA贡献2051条经验 获得超10个赞

发送标头前没有输出!

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

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

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

输出可以是:

  • 故意的:

    • printecho等功能产生输出

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

为什么会这样?

要理解为什么必须在输出之前发送标头,必须查看典型的HTTP 响应。PHP脚本主要生成HTML内容,但也将一组HTTP / CGI标头传递给Web服务器:

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必须首先将标头传递给Web服务器。它只能这样做一次。双线后,它永远不会修改它们。

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

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

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

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

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

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

典型原因:

  1. 打印,回声

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

    产生输出的函数包括


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

    • 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,那么它主要在开始令牌之前引导空格,文本或HTML <?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标准强制要求)。但是,大多数程序员和控制台编辑器都会:


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


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

    纠正工具 

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

    phptags  --whitespace  *.php

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

  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);

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

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

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

优选地甚至是实用功能,其在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"));}

有用的后备解决方法是:

  • 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 header()调用失败时,这两种方法都会产生可接受的回退。理想情况下,您总是将此与用户友好的消息和可点击的链接结合起来作为最后的手段。(例如,http_redirect()PECL扩展的功能。)

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

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

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

更多链接


查看完整回答
反对 回复 2019-05-20
?
FFIVE

TA贡献1797条经验 获得超6个赞

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

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

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

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

  • 字节顺序标记在php文件的开头。用十六进制编辑器检查你的php文件,看看是否是这种情况。它们应该从字节开始3F 3C。您可以安全地EF BB BF从文件的开头删除BOM 。

  • 显式输出,如呼叫echoprintfreadfilepassthru,代码前<?

  • 如果display_errors设置了php.ini属性,则由php输出警告。而不是崩溃程序员的错误,PHP默默地修复错误并发出警告。虽然您可以修改display_errorserror_reporting配置,但您应该修复此问题。
    常见的原因是访问数组的未定义元素(例如,$_POST['input']不使用emptyisset测试输入是否已设置),或使用未定义的常量而不是字符串文字(如下所示$_POST[input],请注意缺少的引号)。

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

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


查看完整回答
反对 回复 2019-05-20
?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

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

可能的方案2:

<?php
  ob_start();

  // code 

 ob_end_flush();?>

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


查看完整回答
反对 回复 2019-05-20
?
幕布斯7119047

TA贡献1794条经验 获得超8个赞

而不是下面的行

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

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


查看完整回答
反对 回复 2019-05-20
  • 7 回答
  • 0 关注
  • 4099 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信