在运行脚本时,我会遇到以下几个错误:

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

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

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


当前回答

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

其他回答

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

我见过这些事;)

发送标题前没有输出!

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

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

修改HTTP标头的一些函数包括:

标头/标头移除会话启动/会话重新生成_idsetcookie/setrawcookie

输出可以是:

无意的:<?php或更高版本?>UTF-8字节顺序标记以前的错误消息或通知

意向性:打印、回声和其他产生输出的功能原始<html>节之前<?php代码。

为什么会这样?

为了理解为什么必须在输出前发送头,有必要看看典型的HTTP回答PHP脚本主要生成HTML内容,但也传递到Web服务器的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必须通过首先向Web服务器发送标头。它只能做到一次。在双线突破之后,它再也无法修正它们了。

当PHP收到第一个输出(print、echo、<html>)时,它将冲洗所有收集的集管。之后,它可以发送所有输出它想要。但是,发送更多的HTTP报头是不可能的。

你怎样才能发现过早的输出是在哪里发生的?

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

警告:无法修改标头信息-标头已由发送(输出开始于/www/usr2345/htdocs/auth.php:52)/www/usr2345/htdocs/index.php第100行

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

括号内的“输出开始于”注释更重要。它表示先前输出的来源。在本例中,它是auth.php以及第52行。这就是你必须寻找过早产出的地方。

典型原因:

打印,回音print和echo语句的有意输出将终止发送HTTP头的机会。必须重新构建应用程序流以避免这种情况。使用函数以及模板方案。确保消息之前发生header()调用被写出来。产生输出的函数包括打印、回音、打印、vprintf触发错误,ob_flush,ob_end_flush、var_dump、print_rreadfile、passthru、flush、imagepng、imagejpeg以及用户定义的功能。原始HTML区域php文件中未分析的HTML部分也是直接输出的。必须注意将触发header()调用的脚本条件在任何原始<html>块之前。<!DOCTYPE html><?php文件//对于标头来说已经太迟了。使用模板方案将处理与输出逻辑分开。将表单处理代码放在脚本顶部。使用临时字符串变量延迟消息。实际的输出逻辑和混合的HTML输出应该在最后。<?php表示“script.php第1行”警告如果警告是指输出内联1,那么开头前的前导空格、文本或HTML<?php令牌。<?php文件#<?-之前有一个空格/换行符它已经密封了它。类似地,附加的脚本或脚本部分也会出现这种情况:?><?php文件PHP实际上在结束标记后消耗了一个换行符。但它不会补偿多个换行符、制表符或空格,这些换行符或制表符或空白被移动到这样的间隙中。UTF-8 BOM换行符和空格可能是一个问题。但也有“隐形”可能导致这种情况的字符序列。最著名的是UTF-8 BOM(字节顺序标记)这是大多数文本编辑器无法显示的。它是字节序列EF BB BF,对于UTF-8编码的文档来说,它是可选的和冗余的。然而,PHP必须将其视为原始输出。它可能在输出中显示为字符ï»?(如果客户端将文档解释为Latin-1)或类似的“垃圾”。特别是图形编辑器和基于Java的IDE在场他们没有将其可视化(受Unicode标准的约束)。然而,大多数程序员和控制台编辑器都这样做:在那里,很容易及早发现问题。其他编辑可能会发现它在文件/设置菜单中的存在(Windows上的Notepad++可以识别和补救问题),检查BOM表存在的另一个选项是使用六边形编辑器。在*nix系统上,hexdump通常可用,如果不是简化审计这些和其他问题的图形变体:一个简单的解决方法是将文本编辑器设置为将文件保存为“UTF-8(无BOM)”或类似于这样的命名。通常情况下,新手会创建新文件,然后复制并粘贴以前的代码。校正实用程序也有自动工具来检查和重写文本文件(sed/awk或recode)。特别是对于PHP,有phptags标签整理器。它将close和open标签改写为长格式和短格式,但也很容易修复了前导和尾随空格、Unicode和UTF-x BOM问题:phptags--空白*.php它可以安全地用于整个include或项目目录。?>之后的空白如果错误源在正在关闭?>然后这是一些空白或原始文本被写出的地方。此时,PHP结束标记不会终止脚本执行。后面的任何文本/空格字符都将作为页面内容写入还通常建议,尤其是对新来者来说,后面的?>PHP文件应省略结束标记。这避免了这些情况中的一小部分。(通常include()d脚本是罪魁祸首。)错误源称为“第0行未知”如果没有错误源,通常是PHP扩展名或PHP.ini设置被具体化。偶尔是gzip流编码设置或ob_gzhandler。但它也可以是任何双重加载的扩展=模块生成隐式PHP启动/警告消息。前面的错误消息如果另一个PHP语句或表达式导致警告消息或通知被打印出来,这也算作过早输出。在这种情况下,您需要避免错误,延迟语句执行,或使用例如。isset()或@()-当两者都不妨碍以后的调试时。

无错误消息

如果根据php.ini禁用了error_reporting或display_errors,则不会显示任何警告。但是忽略错误并不能解决问题离开过早输出后仍无法发送邮件头。

因此,当头(“Location:…”)无提示重定向失败时建议探测警告。用两个简单的命令重新启用它们在调用脚本顶部:

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

或set_error_handler(“var_dump”);如果其他一切都失败了。

说到重定向头,您应该经常使用这样的习惯用法对于最终代码路径:

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

最好甚至是打印用户消息的实用程序功能如果header()失败。

输出缓冲作为解决方法

PHP输出缓冲是解决此问题的方法。它通常工作可靠,但不应该替代适当的应用程序结构和将输出与控制分离思维方式它的实际目的是最小化到Web服务器的分块传输。

输出缓冲区=尽管如此,设置也会有所帮助。在php.ini中配置它或通过.htaccess或甚至打开.user.ini现代FPM/FastCGI设置。启用它将允许PHP缓冲输出,而不是立即将其传递给Web服务器。因此,PHP可以聚合HTTP标头。它同样可以调用ob_start();在调用脚本的顶部。然而,由于多种原因,其可靠性较低:即使<?php ob_start();?>启动第一个脚本、空白或BOM之前可能会被打乱,使其无效。它可以隐藏HTML输出的空白。但是一旦应用逻辑试图发送二进制内容(例如,生成的图像),缓冲的外部输出成为问题。(需要访问ob_clean()作为进一步的解决方法。)缓冲区的大小有限,如果保留默认值,则很容易溢出。这也不罕见,很难追踪当它发生时。

因此,这两种方法都可能变得不可靠,尤其是在切换开发设置和/或生产服务器。这就是为什么输出缓冲是人们普遍认为这只是一种依靠/严格来说是一种变通办法。

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

什么是输出缓冲?为什么在PHP中使用输出缓冲?使用输出缓冲是否被认为是错误的做法?输出缓冲用例是“已发送邮件头”的正确解决方案

但它在另一台服务器上运行!?

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

检查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=“位置”content=“http://example.com/">或在短时间内:<meta http equiv=“Refresh”content=“2;url=../target.html”>当经过<head>部分使用时,这会导致无效的HTML。大多数浏览器仍然接受它。JavaScript重定向作为替代方案,JavaScript重定向可用于页面重定向:<script>location.replace(“target.html”)</脚本>虽然这通常比<meta>解决方案更符合HTML,这导致了对支持JavaScript的客户端的依赖。

然而,当真正的HTTP头()调用失败。理想情况下,您应该始终将其与用户友好的信息相结合最后的办法是点击链接。(例如,http_redirect()PECL扩展确实如此。)

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

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

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

更多链接

谷歌提供了一长串类似的讨论。当然,堆栈溢出也涵盖了许多具体情况。WordPress常见问题解答解释了如何解决邮件头已发送的警告问题?以通用的方式。Adobe社区:PHP开发:为什么重定向不起作用(头已经发送)Nucleus常见问题解答:“页面标题已发送”是什么意思?其中一个更彻底的解释是HTTP头和PHP头()函数——NicholasSolutions的教程(互联网档案链接)。它详细介绍了HTTP,并为重写脚本提供了一些指导。

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

可能的解决方案1

此错误可能是由文件开始之前或文件结束之后的空格造成的。这些空格不应在此处。

前)这里不应该有空格在这里回显“您的代码”;?>这里不应该有空格检查与导致此错误的文件关联的所有文件。注意:有时像gedit(默认的linux编辑器)这样的EDITOR(IDE)会在保存文件中添加一行空白。这不应该发生。如果您使用的是Linux。您可以使用VI编辑器删除?>之后的空格/行在页面末尾。可能的解决方案2:如果不是这样,则使用ob_start输出缓冲:<?php文件ob_start();//代码ob_end_flush();?> 这将打开输出缓冲,页面缓冲后将创建标题。

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

另一种不良做法可能会引发尚未说明的问题。

请参见以下代码段:

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

一切都很好,对吧?

如果“a_import_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:


所有PHP文件必须使用Unix LF(linefeed)行尾。所有PHP文件必须以一个空白行结尾。闭幕式?>标记必须从仅包含php的文件中省略


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