运行我的脚本时,我收到几个如下错误:
警告:无法修改标头信息 - 标头已由 /some/file.php 中的(输出从 /some/file.php:12 开始)发送 <第23行<第23行
错误消息中提到的行包含 header() 和 setcookie() 调用。
header()
setcookie()
这可能是什么原因?又该如何解决呢?
在发送 HTTP 标头之前发送任何内容(使用 setcookie 或 header)。在 HTTP 标头之前输出某些内容的常见原因是:
setcookie
header
意外的空格,通常出现在文件的开头或结尾,如下所示:
为了避免这种情况,只需省略结尾的 ?> - 无论如何都不需要。
?>
3F 3C
EF BB BF
echo
printf
readfile
passthru
之前的代码>
display_errors
$_POST['input']
empty
isset
$_POST[input]
打开输出缓冲应该可以解决问题;调用 ob_start 后的所有输出都缓冲在内存中,直到您释放缓冲区,例如与 ob_end_flush。
ob_start
ob_end_flush
然而,虽然输出缓冲可以避免这些问题,但您应该真正确定应用程序在 HTTP 标头之前输出 HTTP 正文的原因。这就像接电话并讨论你的一天和天气,然后告诉打电话的人他拨错了号码。
在进行任何输出之前,必须调用发送/修改 HTTP 标头的函数。 摘要⇊ 否则调用失败:
修改 HTTP 标头的一些函数是:
标题
header_remove
session_start
session_regenerate_id
setrawcookie
输出可以是:
无意:
之前或 ?> 之后的空格
故意:
print
代码之前的原始 部分。
要理解为什么必须在输出之前发送标头,这是必要的 查看典型的 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 PHP page output page Content Some more output follows... and
Some more output follows...
页面/输出始终跟随标题。 PHP 必须通过 首先将标头发送到网络服务器。它只能这样做一次。 双换行之后就再也不能修改它们了。
当 PHP 收到第一个输出(print、echo、)时,它将 刷新所有收集的标头。之后它可以发送所有输出 它想要。但是发送进一步的 HTTP 标头是不可能的。
header() 警告包含所有相关信息 定位问题原因:
此处“第 100 行”指的是 header() 调用 失败的脚本。
括号内的“输出开始于”注释更为重要。 它表示先前输出的来源。在此示例中,它是 auth.php 和第52行。这就是您必须寻找过早输出的地方。
auth.php
52
典型原因:
print 和 echo 语句的有意输出将终止发送 HTTP 标头的机会。必须重组应用程序流程以避免这种情况。使用函数 和模板方案。确保 header() 调用发生在消息之前 都写出来了。
产生输出的函数包括
打印
vprintf
trigger_error
ob_flush
var_dump
print_r
flush
imagepng
imagejpeg
以及其他和用户定义的函数。
.php 文件中未解析的 HTML 部分也是直接输出。 必须注意将触发 header() 调用的脚本条件 在任何原始块之前。
使用模板方案将处理与输出逻辑分开。
之前的空格表示“script.php 第 1 行”警告
如果警告引用内联输出1,那么主要是 开头 标记之前的前导空格、文本或 HTML。
1
标记之前的前导空格、文本或 HTML。
类似地,附加脚本或脚本部分也可能发生这种情况: ?> PHP 实际上会在关闭标签后占用一个单个换行符。但它不会 补偿移入此类间隙的多个换行符、制表符或空格。
类似地,附加脚本或脚本部分也可能发生这种情况:
?> PHP 实际上会在关闭标签后占用一个单个换行符。但它不会 补偿移入此类间隙的多个换行符、制表符或空格。
PHP 实际上会在关闭标签后占用一个单个换行符。但它不会 补偿移入此类间隙的多个换行符、制表符或空格。
仅换行符和空格就可能是一个问题。但也有“看不见的” 可能导致这种情况的字符序列。最著名的是 UTF-8 BOM(字节顺序标记) 大多数文本编辑器不显示它。它是字节序列 EF BB BF,对于 UTF-8 编码的文档来说,它是可选且冗余的。然而 PHP 必须将其视为原始输出。它可能在输出中显示为字符 (如果客户端将文档解释为 Latin-1)或类似的“垃圾”。

特别是图形编辑器和基于 Java 的 IDE 并没有注意到它 在场。他们没有将其可视化(Unicode 标准规定)。 然而,大多数程序员和控制台编辑器都会:
这样很容易尽早发现问题。其他编辑可能会识别 它存在于文件/设置菜单中(Windows 上的 Notepad++ 可以识别并 解决问题), 检查 BOM 存在的另一种选择是使用十六进制编辑器。 在 *nix 系统上 hexdump 通常可用, 如果不是简化审核这些问题和其他问题的图形变体:
hexdump
一个简单的解决方法是将文本编辑器设置为将文件保存为“UTF-8(无 BOM)” 或类似于这样的命名法。通常,新手会求助于创建新文件,然后将以前的代码复制并粘贴回来。
还有自动化工具来检查和重写文本文件 (sed/awk代码> 或重新编码)。 对于 PHP,特别是 phptags 标记 tidier。 它将关闭和打开标签重写为长和短形式,而且也很容易 修复了前导和尾随空格、Unicode 和 UTF-x BOM 问题:
sed
awk代码>
重新编码
phptags
phptags --whitespace *.php
在整个包含或项目目录上使用是安全的。
后有空格?>
如果后面提到了错误来源 关闭 ?> 那么这就是一些空白或原始文本被写出的地方。 PHP 结束标记此时不会终止脚本执行。其后的任何文本/空格字符都将作为页面内容写出 仍然。
通常建议,特别是对于新手,尾随 ?> PHP 应省略关闭标签。这避开了这些案例中的一小部分。 (很常见 include()d 脚本是罪魁祸首。)
include()d
如果没有错误源,通常是 PHP 扩展或 php.ini 设置 具体化了。
gzip
ob_gzhandler
extension=
如果另一个 PHP 语句或表达式导致警告消息或 注意被打印出来,这也算作过早输出。
在这种情况下,您需要避免错误, 延迟语句执行,或使用例如抑制消息 isset() 或 @() - 当任何一个都不会妨碍稍后的调试时。
isset()
@()
如果您根据 php.ini 禁用了 error_reporting 或 display_errors, 那么就不会出现任何警告。但忽略错误并不能解决问题 离开。过早输出后仍然无法发送标头。
php.ini
error_reporting
因此,当 header("Location: ...") 重定向默默失败时,这是非常严重的 建议探测警告。使用两个简单的命令重新启用它们 在调用脚本之上:
header("Location: ...")
error_reporting(E_ALL); ini_set("display_errors", 1);
或者set_error_handler("var_dump");如果其他方法都失败了。
set_error_handler("var_dump");
说到重定向标头,您应该经常使用这样的习惯用法 这是最终的代码路径:
exit(header("Location: /finished.html"));
最好是一个打印用户消息的实用函数 如果 header() 失败。
PHP 输出缓冲 是缓解此问题的解决方法。它通常工作可靠,但不应该 替代正确的应用程序结构并将输出与控制分开 逻辑。它的实际目的是最大限度地减少到网络服务器的分块传输。
output_buffering= 不过,设置还是有帮助的。 在 php.ini 中配置它 或通过 .htaccess 甚至 .user.ini 现代 FPM/FastCGI 设置。 启用它将允许 PHP 缓冲输出,而不是立即将其传递到网络服务器。 PHP 因此可以聚合 HTTP 标头。
output_buffering=
它同样可以通过调用 ob_start(); 在调用脚本之上。然而,由于多种原因,它不太可靠:
ob_start();
即使 开始第一个脚本,空格或 BOM 可能会在渲染之前被打乱无效。
它可以隐藏 HTML 输出的空白。但是,一旦应用程序逻辑尝试发送二进制内容(例如生成的图像), 缓冲的无关输出成为一个问题。 (需要ob_clean()) 作为进一步的解决方法。)
缓冲区的大小有限,如果保留默认值,很容易溢出。 这种情况也不少见,很难追踪一个> 当它发生时。
因此,这两种方法都可能变得不可靠 - 特别是在两者之间切换时 开发设置和/或生产服务器。这就是为什么输出缓冲是 广泛认为只是一个拐杖/严格来说是一种解决方法。
另请参阅基本用法示例 在手册中,以及更多优点和缺点:
如果您之前没有收到标头警告,则输出缓冲 php.ini 设置 已经改变。当前/新服务器上可能未配置它。
headers_sent()检查
您始终可以使用 headers_sent() 来探测是否 仍然可以...发送标头。这对于有条件打印很有用 信息或应用其他后备逻辑。
headers_sent()
if (headers_sent()) { die("Redirect failed. Please click on this link: "); } else{ exit(header("Location: /user.php")); }
有用的后备解决方法是:
如果您的应用程序在结构上很难修复,那么一个简单的(但 有点不专业)允许重定向的方法是注入 HTML 标签。可以通过以下方式实现重定向:
或者短暂延迟:
当使用超过 部分时,这会导致无效的 HTML。 大多数浏览器仍然接受它。
作为替代方案,JavaScript 重定向 可用于页面重定向:
sssccc
虽然这通常比 解决方法更符合 HTML, 它会导致对支持 JavaScript 的客户端的依赖。
然而,当真正的 HTTP header() 时,这两种方法都会产生可接受的后备 呼叫失败。理想情况下,您总是将其与用户友好的消息结合起来, 作为最后手段的可点击链接。 (例如,http_redirect() PECL 扩展确实如此。)
session_start()
setcookie() 和 session_start() 都需要发送 Set-Cookie: HTTP 标头。 因此,适用相同的条件,并且将生成类似的错误消息 用于过早输出的情况。
Set-Cookie:
(当然,它们还受到浏览器中禁用 cookie 的影响 甚至代理问题。会话功能显然也依赖于免费 磁盘空间和其他 php.ini 设置等)
在发送 HTTP 标头之前发送任何内容(使用
setcookie
或header
)。在 HTTP 标头之前输出某些内容的常见原因是:意外的空格,通常出现在文件的开头或结尾,如下所示:
为了避免这种情况,只需省略结尾的
?>
- 无论如何都不需要。3F 3C
开头。您可以安全地从文件开头删除 BOMEF BB BF
。echo
、printf
、readfile
、passthru
、之前的代码>
等display_errors
php.ini属性已设置。 php 不会因程序员错误而崩溃,而是默默地修复错误并发出警告。虽然您可以修改display_errors
或 error_reporting 配置,您应该解决问题。常见原因是访问数组中未定义的元素(例如
$_POST['input']
而不使用empty
或isset
进行测试是否设置了输入),或者使用未定义的常量而不是字符串文字(如$_POST[input]
中,请注意缺少的引号)。打开输出缓冲应该可以解决问题;调用
ob_start
后的所有输出都缓冲在内存中,直到您释放缓冲区,例如与ob_end_flush
。然而,虽然输出缓冲可以避免这些问题,但您应该真正确定应用程序在 HTTP 标头之前输出 HTTP 正文的原因。这就像接电话并讨论你的一天和天气,然后告诉打电话的人他拨错了号码。
发送标头之前没有输出!
在进行任何输出之前,必须调用发送/修改 HTTP 标头的函数。 摘要⇊ 否则调用失败:
修改 HTTP 标头的一些函数是:
标题
/header_remove
session_start
/session_regenerate_id
setcookie
/setrawcookie
输出可以是:
无意:
之前或
?>
之后的空格故意:
print
、echo
和其他产生输出的函数代码之前的原始
部分。
为什么会发生这种情况?
要理解为什么必须在输出之前发送标头,这是必要的 查看典型的 HTTP 回复。 PHP脚本主要生成HTML内容,同时也传递一个 发送到网络服务器的 HTTP/CGI 标头集:
页面/输出始终跟随标题。 PHP 必须通过 首先将标头发送到网络服务器。它只能这样做一次。 双换行之后就再也不能修改它们了。
当 PHP 收到第一个输出(
print
、echo
、)时,它将 刷新所有收集的标头。之后它可以发送所有输出 它想要。但是发送进一步的 HTTP 标头是不可能的。
如何找出过早输出发生的位置?
header()
警告包含所有相关信息 定位问题原因:此处“第 100 行”指的是
header()
调用 失败的脚本。括号内的“输出开始于”注释更为重要。 它表示先前输出的来源。在此示例中,它是
auth.php
和第52
行。这就是您必须寻找过早输出的地方。典型原因:
打印、回显
print
和echo
语句的有意输出将终止发送 HTTP 标头的机会。必须重组应用程序流程以避免这种情况。使用函数 和模板方案。确保header()
调用发生在消息之前 都写出来了。产生输出的函数包括
打印
、echo
、printf
、vprintf
trigger_error
、ob_flush
、ob_end_flush
、var_dump
、print_r
readfile
、passthru
、flush
、imagepng
、imagejpeg
以及其他和用户定义的函数。
原始 HTML 区域
.php 文件中未解析的 HTML 部分也是直接输出。 必须注意将触发
header()
调用的脚本条件 在任何原始块之前。
之前的空格表示“script.php 第 1 行”警告
如果警告引用内联输出
1
,那么主要是 开头标记之前的前导空格、文本或 HTML。
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代码>
或重新编码
)。 对于 PHP,特别是phptags
标记 tidier。 它将关闭和打开标签重写为长和短形式,而且也很容易 修复了前导和尾随空格、Unicode 和 UTF-x BOM 问题:在整个包含或项目目录上使用是安全的。
后有空格?>
如果后面提到了错误来源 关闭
?>
那么这就是一些空白或原始文本被写出的地方。 PHP 结束标记此时不会终止脚本执行。其后的任何文本/空格字符都将作为页面内容写出 仍然。通常建议,特别是对于新手,尾随
?>
PHP 应省略关闭标签。这避开了这些案例中的一小部分。 (很常见include()d
脚本是罪魁祸首。)错误源被称为“第 0 行未知”
如果没有错误源,通常是 PHP 扩展或 php.ini 设置 具体化了。
gzip
流编码设置 或ob_gzhandler
。extension=
模块 生成隐式 PHP 启动/警告消息。前面的错误消息
如果另一个 PHP 语句或表达式导致警告消息或 注意被打印出来,这也算作过早输出。
在这种情况下,您需要避免错误, 延迟语句执行,或使用例如抑制消息
isset()
或@()
- 当任何一个都不会妨碍稍后的调试时。没有错误消息
如果您根据
php.ini
禁用了error_reporting
或display_errors
, 那么就不会出现任何警告。但忽略错误并不能解决问题 离开。过早输出后仍然无法发送标头。因此,当
header("Location: ...")
重定向默默失败时,这是非常严重的 建议探测警告。使用两个简单的命令重新启用它们 在调用脚本之上:或者
set_error_handler("var_dump");
如果其他方法都失败了。说到重定向标头,您应该经常使用这样的习惯用法 这是最终的代码路径:
最好是一个打印用户消息的实用函数 如果
header()
失败。输出缓冲作为解决方法
PHP 输出缓冲 是缓解此问题的解决方法。它通常工作可靠,但不应该 替代正确的应用程序结构并将输出与控制分开 逻辑。它的实际目的是最大限度地减少到网络服务器的分块传输。
output_buffering=
不过,设置还是有帮助的。 在 php.ini 中配置它 或通过 .htaccess 甚至 .user.ini 现代 FPM/FastCGI 设置。启用它将允许 PHP 缓冲输出,而不是立即将其传递到网络服务器。 PHP 因此可以聚合 HTTP 标头。
它同样可以通过调用
ob_start();
在调用脚本之上。然而,由于多种原因,它不太可靠:即使
开始第一个脚本,空格或 BOM 可能会在渲染之前被打乱无效。
它可以隐藏 HTML 输出的空白。但是,一旦应用程序逻辑尝试发送二进制内容(例如生成的图像), 缓冲的无关输出成为一个问题。 (需要ob_clean()) 作为进一步的解决方法。)
缓冲区的大小有限,如果保留默认值,很容易溢出。 这种情况也不少见,很难追踪一个> 当它发生时。
因此,这两种方法都可能变得不可靠 - 特别是在两者之间切换时 开发设置和/或生产服务器。这就是为什么输出缓冲是 广泛认为只是一个拐杖/严格来说是一种解决方法。
另请参阅基本用法示例 在手册中,以及更多优点和缺点:
但它在其他服务器上工作!?
如果您之前没有收到标头警告,则输出缓冲 php.ini 设置 已经改变。当前/新服务器上可能未配置它。
使用
headers_sent()检查
您始终可以使用
headers_sent()
来探测是否 仍然可以...发送标头。这对于有条件打印很有用 信息或应用其他后备逻辑。有用的后备解决方法是:
HTML
标签
如果您的应用程序在结构上很难修复,那么一个简单的(但 有点不专业)允许重定向的方法是注入 HTML
标签。可以通过以下方式实现重定向:
或者短暂延迟:
当使用超过
部分时,这会导致无效的 HTML。 大多数浏览器仍然接受它。
JavaScript 重定向
作为替代方案,JavaScript 重定向 可用于页面重定向:
虽然这通常比
解决方法更符合 HTML, 它会导致对支持 JavaScript 的客户端的依赖。
然而,当真正的 HTTP header() 时,这两种方法都会产生可接受的后备 呼叫失败。理想情况下,您总是将其与用户友好的消息结合起来, 作为最后手段的可点击链接。 (例如,http_redirect() PECL 扩展确实如此。)
为什么
setcookie()
和session_start()
也受到影响setcookie()
和session_start()
都需要发送Set-Cookie:
HTTP 标头。 因此,适用相同的条件,并且将生成类似的错误消息 用于过早输出的情况。(当然,它们还受到浏览器中禁用 cookie 的影响 甚至代理问题。会话功能显然也依赖于免费 磁盘空间和其他 php.ini 设置等)
更多链接