运行我的脚本时,我遇到了几个这样的错误:
警告:无法修改标头信息 - 标头已由 第 23 行/some/file.php 中的( 输出开始于 /some/file.php:12 ) 发送
错误消息中提到的行包含 header()
和 setcookie()
调用。
这可能是什么原因?以及如何解决?
原文由 Moses89 发布,翻译遵循 CC BY-SA 4.0 许可协议
运行我的脚本时,我遇到了几个这样的错误:
警告:无法修改标头信息 - 标头已由 第 23 行/some/file.php 中的( 输出开始于 /some/file.php:12 ) 发送
错误消息中提到的行包含 header()
和 setcookie()
调用。
这可能是什么原因?以及如何解决?
原文由 Moses89 发布,翻译遵循 CC BY-SA 4.0 许可协议
2 回答1.3k 阅读✓ 已解决
2 回答824 阅读✓ 已解决
1 回答911 阅读✓ 已解决
1 回答1k 阅读✓ 已解决
2 回答882 阅读
1 回答873 阅读
1 回答803 阅读
发送标头之前没有输出!
必须 在进行任何输出之前 调用发送/修改 HTTP 标头的函数。
摘要 ⇊
否则调用失败:一些修改 HTTP 标头的函数是:
header
/header_remove
session_start
/session_regenerate_id
setcookie
/setrawcookie
输出可以是:
无意:
<?php
之前或?>
--- 之后的空格故意的:
print
,echo
和其他产生输出的函数<html>
之前的部分<?php
代码。为什么会这样?
要了解为什么必须在输出之前发送标头,有必要查看典型的 HTTP 响应。 PHP 脚本主要生成 HTML 内容,但也会将一组 HTTP/CGI 标头传递给网络服务器:
页面/输出始终 跟随 标题。 PHP 必须首先将标头传递给网络服务器。它只能这样做一次。在双线断之后,它再也不能修改它们了。
当 PHP 接收到第一个输出(
print
,echo
,<html>
)时,它将 刷新 所有收集的标头。之后它可以发送它想要的所有输出。但是那时发送更多的 HTTP 标头是不可能的。如何找出过早输出发生的位置?
header()
警告包含所有相关信息以定位问题原因:这里的“第 100 行”是指
header()
调用 失败的脚本。括号内的“ _输出开始于_”注释更重要。它指定先前输出的来源。在此示例中,它是
auth.php
和 行52
。这就是你必须寻找过早输出的地方。典型原因:
来自
print
和echo
语句的有意输出将终止发送 HTTP 标头的机会。必须重组应用程序流程以避免这种情况。使用 函数 和模板方案。确保在写出消息 之前 发生header()
调用。产生输出的函数包括
print
,echo
,printf
,vprintf
trigger_error
,ob_flush
,ob_end_flush
,var_dump
,print_r
readfile
,passthru
,flush
,imagepng
,imagejpeg
除其他外和用户定义的功能。
.php
文件中未解析的 HTML 部分也是直接输出。触发header()
调用的脚本条件必须在 任何 原始<html>
块之前注明。使用模板方案将处理与输出逻辑分开。
<?php
之前的空格用于“script.php line 1 ”警告如果警告是指输出内联
1
,那么它主要是在开头<?php
标记之前的前导 空格、文本或 HTML。同样,附加脚本或脚本部分也可能发生这种情况:
单独的换行符和空格可能是一个问题。但也有“不可见”的字符序列会导致这种情况。最著名的是大多数文本编辑器不显示的 UTF-8 BOM (字节顺序标记) 。它是字节序列
EF BB BF
,对于 UTF-8 编码的文档是可选的和冗余的。然而,PHP 必须将其视为原始输出。它可能在输出中显示为字符
(如果客户端将文档解释为 Latin-1)或类似的“垃圾”。尤其是图形编辑器和基于 Java 的 IDE 并没有注意到它的存在。他们没有将其可视化(Unicode 标准要求)。然而,大多数程序员和控制台编辑器会:
很容易在早期发现问题。其他编辑器可能会在文件/设置菜单中识别它的存在(Windows 上的 Notepad++ 可以识别和 解决问题),检查 BOM 存在的另一个选项是使用 hexeditor 。在 *nix 系统上
一个简单的解决方法是将文本编辑器设置为将文件保存为“UTF-8(无 BOM)”或类似于此类命名法。通常,新手会求助于创建新文件,然后将以前的代码复制并粘贴回去。
hexdump
通常可用,如果不是简化审计这些和其他问题的图形变体:### 校正实用程序
还有用于检查和重写文本文件的自动化工具(
sed
/awk
或recode
)。对于 PHP,特别是phptags
标签 titier 。它将关闭和打开标签重写为长格式和短格式,还可以轻松修复前导和尾随空格、Unicode 和 UTF-x BOM 问题:在整个包含或项目目录上使用是安全的。
?>
如果错误源在 结尾
?>
后面提到,那么这就是一些空白或原始文本被写出的地方。 PHP 结束标记此时不会终止脚本执行。其后的任何文本/空格字符仍将作为页面内容写出。通常建议,特别是对于新手,应省略尾随的
?>
PHP 关闭标签。这 避开 了这些案例的一小部分。 (很常见include()d
脚本是罪魁祸首。)如果没有具体化错误源,它通常是 PHP 扩展或 php.ini 设置。
gzip
流编码设置 或ob_gzhandler
。extension=
模块生成隐式 PHP 启动/警告消息。如果另一个 PHP 语句或表达式导致打印出警告消息或通知,这也算作过早输出。
在这种情况下,您需要避免错误,延迟语句执行,或使用例如
isset()
或@()
禁止消息 - 当两者都不会妨碍以后的调试时。没有错误信息
如果您根据
display_errors
禁用了error_reporting
或 ---php.ini
,则不会出现警告。但是忽略错误不会让问题消失。过早输出后仍然无法发送标头。因此,当
header("Location: ...")
重定向静默失败时,建议探测警告。使用调用脚本顶部的两个简单命令重新启用它们:或者
set_error_handler("var_dump");
如果一切都失败了。说到重定向标头,您应该经常对最终代码路径使用这样的习惯用法:
最好是一个实用功能,它在
header()
失败的情况下打印用户消息。输出缓冲作为一种解决方法
PHP 输出缓冲 是缓解此问题的一种解决方法。它通常可靠地工作,但不应替代适当的应用程序结构和将输出与控制逻辑分离。它的实际目的是尽量减少到网络服务器的分块传输。
output_buffering=
设置仍然可以提供帮助。在现代 FPM/FastCGI 设置中,在 php.ini 或通过 .htaccess 甚至 .user.ini 配置它。启用它将允许 PHP 缓冲输出,而不是立即将其传递给网络服务器。因此 PHP 可以聚合 HTTP 标头。
它也可以在调用脚本顶部调用
ob_start();
。然而,由于多种原因,它不太可靠:即使
<?php ob_start(); ?>
启动第一个脚本,空白或 BOM 可能会在之前被打乱, 使其无效。它可以隐藏 HTML 输出的空格。但是,一旦应用程序逻辑尝试发送二进制内容(例如生成的图像),缓冲的无关输出就会成为问题。 (
ob_clean()
作为进一步的解决方法。)缓冲区的大小是有限的,并且在保留为默认值时很容易溢出。这也不是罕见的事情,当它发生时 很难追踪。
因此,这两种方法都可能变得不可靠——尤其是在开发设置和/或生产服务器之间切换时。这就是为什么输出缓冲被广泛认为只是一种拐杖/严格来说是一种解决方法。
另请参阅手册中的 基本用法示例,了解更多优点和缺点:
但它在另一台服务器上工作!?
如果您之前没有收到标头警告,则 输出缓冲 php.ini 设置 已更改。它可能在当前/新服务器上未配置。
检查
headers_sent()
您始终可以使用
headers_sent()
来探测是否仍然可以…发送标头。这对于有条件地打印信息或应用其他后备逻辑很有用。有用的后备解决方法是:
<meta>
标签如果您的应用程序在结构上难以修复,那么允许重定向的一种简单(但有点不专业)的方法是注入 HTML
<meta>
标签。可以通过以下方式实现重定向:或者有一个短暂的延迟:
当在
<head>
部分使用时,这会导致 HTML 无效。大多数浏览器仍然接受它。作为替代, JavaScript 重定向 可用于页面重定向:
虽然这通常比
<meta>
解决方法更符合 HTML,但它会导致对支持 JavaScript 的客户端的依赖。然而,当真正的 HTTP header() 调用失败时,这两种方法都可以接受回退。理想情况下,您总是将其与用户友好的消息和可点击的链接结合起来作为最后的手段。 (例如 http_redirect() PECL 扩展的作用。)
为什么
setcookie()
和session_start()
也会受到影响setcookie()
和session_start()
都需要发送一个Set-Cookie:
HTTP 头。因此适用相同的条件,并且对于过早的输出情况将生成类似的错误消息。(当然,它们还会受到浏览器中禁用的 cookie 甚至代理问题的影响。会话功能显然还取决于可用磁盘空间和其他 php.ini 设置等。)
更多链接