繁体   English   中英

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

[英]How to fix "Headers already sent" error in PHP

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

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

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

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

发送标头之前没有输出!

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

)警告:无法修改标头信息 - 标头已发送(输出开始于

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

输出可以是:

  • 故意的:

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

为什么会这样?

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

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> )时,它将刷新所有收集的标题。 之后它可以发送它想要的所有输出。 但是那时发送更多的 HTTP 标头是不可能的。

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

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

警告:无法修改标头信息 - 标头已由第 100 行/www/usr2345/htdocs/index.php中的/www/usr2345/htdocs/auth.php:52发送的标头

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

括号内的“输出开始于”注释更重要。 它指定先前输出的来源。 在这个例子中,它是auth.php52 这就是你必须寻找过早输出的地方。

典型原因:

  1. 打印,回显

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

    产生输出的函数包括

    • printechovprintfprintf
    • trigger_error , ob_flush , ob_end_flush , var_dump , print_r
    • readfile , passthru , flush , imagepng , imagejpeg


    除其他外和用户定义的功能。

  2. 原始 HTML 区域

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

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

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

    • 将表单处理代码放在脚本之上。
    • 使用临时字符串变量来延迟消息。
    • 实际的输出逻辑和混合的 HTML 输出应该在最后。

  3. <?php之前的空格用于“script.php line 1 ”警告

    如果警告是指输出内联1 ,那么它主要是开头<?php标记之前的前导空格、文本或 HTML。

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

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

     ?> <?php

    PHP 实际上在关闭标签后吃掉了一个换行符。 但它不会补偿多个换行符或制表符或空格转移到此类间隙中。

  4. UTF-8 物料清单

    单独的换行符和空格可能是一个问题。 但也有“不可见”的字符序列会导致这种情况。 最著名的是大多数文本编辑器不显示的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 ,如果不是简化审计这些和其他问题的图形变体:

    显示 utf-8 bom 的 beav hexeditor

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

    校正实用程序

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

     phptags --whitespace *.php

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

  5. 后面的空格?>

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

    通常建议,尤其是对于新手,应该省略尾随的?> PHP 关闭标签。 避开了这些案例的一小部分。 (通常include()d脚本是罪魁祸首。)

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

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

    • 有时是gzip流编码设置ob_gzhandler
    • 但它也可能是任何双重加载的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=设置仍然可以提供帮助。 在现代 FPM/FastCGI 设置中,在php.ini或通过.htaccess甚至.user.ini配置它。
    启用它将允许 PHP 缓冲输出,而不是立即将其传递给网络服务器。 因此 PHP 可以聚合 HTTP 标头。

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

    • 即使<?php 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 设置等。)

更多链接

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

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

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

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

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

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

然而,虽然输出缓冲避免了这些问题,但您应该真正确定为什么您的应用程序在 HTTP 标头之前输出 HTTP 正文。 这就像在告诉来电者他的号码错误之前接听电话并讨论您的一天和天气一样。

我之前多次遇到此错误,并且我确信所有 PHP 程序员之前至少遇到过一次此错误。

可能的解决方案 1

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

ex) 这里不应该有空格

   echo "your code here";

?>
THERE SHOULD BE NO BLANK SPACES HERE

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

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

可能的解决方案 2:如果不是您的情况,请使用ob_start输出缓冲:

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

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

常见问题:

(复制自:来源

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

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

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;

这是因为这条线:

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

在发送标头之前,您不应打印/回显任何内容。

一个简单的提示:脚本中的一个简单空格(或不可见的特殊字符),就在第一个<?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 above 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 文件必须以一个single blank line结束。
  • 关闭 ?> 标记必须从only php的文件中omitted

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM