如何修复PHP中的“标头已发送”错误
运行脚本时,我收到几个这样的错误:
警告:无法修改标头信息 - 已由 /some/file 中第 23 行的 /some/file.php 中的 (输出从 /some/file.php:12 开始)发送的标头
错误消息中提到的行包含 header()
和 setcookie()
调用。
这可能是什么原因?如何解决它?
运行脚本时,我收到几个这样的错误:
警告:无法修改标头信息 - 已由 /some/file 中第 23 行的 /some/file.php 中的 (输出从 /some/file.php:12 开始)发送的标头
错误消息中提到的行包含 header()
和 setcookie()
调用。
这可能是什么原因?如何解决它?
在进行任何输出之前,必须调用发送/修改 HTTP 标头的函数。 否则,调用将失败:summary ⇊
警告:无法修改标头信息 - 标头已发送(输出在脚本:行处启动))
修改 HTTP 标头的一些函数包括:
输出可以是:
无心的:
<?php
?>
故意:
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>
页面/输出始终跟在标题后面。PHP必须首先将标头传递给Web服务器。它只能这样做一次。在双换行符之后,它再也不能修改它们了。
当 PHP 收到第一个输出 (, , ) 时,它将刷新所有收集的标头。之后,它可以发送所需的所有输出。但是,发送更多的HTTP标头是不可能的。print
echo
<html>
该警告包含查找问题原因的所有相关信息:header()
警告:无法修改标头信息 - 已由 /www/usr2345/htdocs/index 中第 100 行的 /www/usr2345/htdocs/auth.php 中的标头(输出从 /www/usr2345/htdocs/auth.php:52 开始)发送
此处的“第 100 行”是指调用失败的脚本。header()
括号内的“输出开始于”注释更为重要。它指定了先前输出的源。在此示例中,它是 和第 52 行
。这就是你必须寻找过早输出的地方。auth.php
典型原因:
和 语句的有意输出将终止发送 HTTP 标头的机会。必须对应用程序流进行重组以避免这种情况。使用函数和模板方案。确保在写出消息之前发生呼叫。print
echo
header()
产生输出的函数包括
print
, , ,echo
printf
vprintf
trigger_error
, , , ,ob_flush
ob_end_flush
var_dump
print_r
readfile
, , , ,passthru
flush
imagepng
imagejpeg
其中包括用户定义的函数。
文件中未分析的 HTML 部分也是直接输出。在任何原始块之前,必须注意将触发调用的脚本条件。.php
header()
<html>
<!DOCTYPE html>
<?php
// Too late for headers already.
使用模板方案将处理与输出逻辑分开。
<?php
如果警告指的是输出内联 1
,则它主要是在打开标记之前的前导空格、文本或 HTML。<?php
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
同样,对于追加的脚本或脚本部分,也会发生这种情况:
?>
<?php
PHP实际上在关闭标签后会吃掉一个换行符。但它不会补偿多个换行符或制表符或空格移动到这样的间隙。
换行符和空格本身就可能是一个问题。但也有一些“不可见”的字符序列可能导致这种情况。最著名的是UTF-8 BOM(字节顺序标记),大多数文本编辑器不显示它。它是字节序列 ,对于 UTF-8 编码文档是可选的和冗余的。然而,PHP必须将其视为原始输出。它可能会显示为输出中的字符(如果客户端将文档解释为Latin-1)或类似的“垃圾”。EF BB BF

特别是图形编辑器和基于Java的IDE忘记了它的存在。他们不会将其可视化(受Unicode标准的约束)。然而,大多数程序员和控制台编辑器都这样做:
在那里,很容易在早期发现问题。其他编辑器可能会在文件/设置菜单中识别它的存在(Windows上的Notepad ++可以识别并解决问题),检查BOM存在的另一个选项是诉诸十六进制编辑器。在*nix系统上,通常可以使用十六转储
,如果不是图形变体,可以简化对这些和其他问题的审计:
一个简单的解决方法是将文本编辑器设置为将文件另存为“UTF-8(无 BOM)”或类似的命名法。通常,新手会诉诸于创建新文件,而只是复制并粘贴以前的代码。
还有自动化工具可以检查和重写文本文件(sed
/awk
或 )。特别是对于PHP,phptags
标签更整洁。它将关闭和打开标记重写为长格式和短格式,但也可以轻松修复前导和尾随空格、Unicode 和 UTF-x BOM 问题:recode
phptags --whitespace *.php
在整个包含目录或项目目录上使用是安全的。
?>
如果错误源被提及为结束的后面 ->
那么这就是一些空格或原始文本被写出来的地方。此时,PHP 结束标记不会终止脚本执行。之后的任何文本/空格字符仍将作为页面内容写出。
通常建议,特别是对于新手,应该省略尾随的PHP关闭标签。这避开了这些案件的一小部分。(通常,脚本是罪魁祸首。?>
include()d
它通常是 PHP 扩展或 php.ini设置(如果未具体化错误源)。
ob_gzhandler
。gzip
extension=
如果另一个 PHP 语句或表达式导致打印出警告消息或通知,这也算作过早输出。
在这种情况下,您需要避免错误,延迟语句执行,或者使用例如isset()
或@()
来抑制消息 - 当任何一个都不会妨碍以后的调试时。
如果您已禁用 或 禁用了 per ,则不会显示任何警告。但是忽略错误不会使问题消失。过早输出后仍无法发送标头。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 输出缓冲是缓解此问题的解决方法。它通常工作可靠,但不应取代正确的应用程序结构和将输出与控制逻辑分离。它的实际目的是最大限度地减少到Web服务器的分块传输。
不过,output_buffering=
设置可能会有所帮助。在php.ini或通过.htaccess甚至.user.ini现代FPM / FastCGI设置中配置它。
启用它将允许PHP缓冲输出,而不是立即将其传递到Web服务器。因此,PHP可以聚合HTTP标头。
它同样可以通过调用调用脚本顶部的ob_start();
来参与。然而,由于多种原因,它不太可靠:
因此,这两种方法都可能变得不可靠 - 特别是在开发设置和/或生产服务器之间切换时。这就是为什么输出缓冲被广泛认为只是一个拐杖/严格意义上的解决方法。
另请参阅手册中的基本用法示例,以及更多优缺点:
如果您之前没有收到标头警告,则输出缓冲 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>
如果您的应用程序在结构上难以修复,那么允许重定向的一种简单(但有些不专业)方法是注入HTML标记。重定向可以通过以下方式实现:<meta>
<meta http-equiv="Location" content="http://example.com/">
或者有短暂的延迟:
<meta http-equiv="Refresh" content="2; url=../target.html">
这会导致在超过该部分时使用时无效的 HTML。大多数浏览器仍然接受它。<head>
作为替代方案,JavaScript重定向可用于页面重定向:
<script> location.replace("target.html"); </script>
虽然这通常比变通办法更符合 HTML 标准,但它会依赖于支持 JavaScript 的客户端。<meta>
但是,这两种方法都会在真正的 HTTP header() 调用失败时进行可接受的回退。理想情况下,您始终将其与用户友好的消息和可点击的链接相结合,作为最后的手段。(例如,http_redirect() PECL 扩展的作用。
setcookie()
session_start()
两者都需要发送HTTP标头。因此,相同的条件也适用,并且对于过早的输出情况,将生成类似的错误消息。setcookie()
session_start()
Set-Cookie:
(当然,它们还受到浏览器中禁用的cookie甚至代理问题的影响。会话功能显然还取决于可用磁盘空间和其他 php.ini设置等。
如果在发送 HTTP 标头(使用 setcookie
或标头)之前发送任何内容,则会触发此错误消息。在 HTTP 标头之前输出某些内容的常见原因是:
意外的空格,通常在文件的开头或结尾,如下所示:
<?php
// Note the space before "<?php"
?>
为了避免这种情况,只需省略关闭 - 无论如何都不需要。?>
3F 3C
EF BB BF
echo
printf
readfile
passthru
<?
php.ini 属性,
则 php display_errors输出的警告。php不会因程序员错误而崩溃,而是静默地修复错误并发出警告。虽然您可以修改display_errors
或error_reporting配置,但您应该解决问题。空
或 isset
来测试输入是否已设置),或者使用未定义的常量而不是字符串文本(如 中所示,请注意缺少的引号)。$_POST['input']
$_POST[input]
打开输出缓冲应该使问题消失;调用ob_start
后的所有输出都将缓冲在内存中,直到您释放缓冲区,例如使用ob_end_flush
。
但是,虽然输出缓冲避免了这些问题,但您应真正确定应用程序在 HTTP 标头之前输出 HTTP 正文的原因。这就像打个电话,讨论你的一天和天气,然后告诉来电者他弄错了号码。