发送标题之前没有输出!
在进行任何输出之前, 必须调用发送/修改 HTTP 标头的函数 .摘要⇊否则调用失败:
警告:无法修改标头信息 - 标头已发送(输出开始于脚本:行)
修改 HTTP 标头的一些函数包括:
输出可以是:
-
p4
- Whitespace before
<?php
or after ?>
- The UTF-8 Byte Order Mark specifically
- Previous error messages or notices
-
p5
print
, echo
and other functions producing output - Raw
<html>
sections prior <?php
code.
为什么会发生这种情况?
要理解为什么必须在输出之前发送标头,必须查看典型的 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 收到第一个输出 ( print
, echo
, <html>
) 时,它将 刷新 所有收集的标头。之后它可以发送它想要的所有输出。但那时不可能发送进一步的 HTTP 标头。
如何才能找出过早输出发生的位置?
警告 header()
包含定位问题原因的所有相关信息:
头 信息 - 标头已由 (输出开始于 /www/usr2345/htdocs/auth.php:52 )在 /www/usr2345/htdocs/index.php 第 100 行发送
调用 header()
脚本 。
括号内的 \' 输出开始于 auth.php
是 line 52
。这就是您必须查找过早输出的地方。
典型原因:
-
p65
p14
p15
print
, echo
, printf
, vprintf
trigger_error
, ob_flush
, ob_end_flush
, var_dump
, print_r
readfile
, passthru
, flush
, imagepng
, imagejpeg
p16
-
p66
p17
<!DOCTYPE html><?php // Too late for headers already.
p18
- Place form processing code atop scripts.
- Use temporary string variables to defer messages.
- The actual output logic and intermixed HTML output should follow last.
-
p67
p19
<?php# There's a SINGLE space/newline before <? - Which already seals it.
p20
?><?php
p21
-
p68
p22
p23
p24
p25
p69
p26
phptags --whitespace *.php
p27
-
p70
p28
p29
-
p71
p30
- It's occasionally the
gzip
stream encoding setting or the ob_gzhandler
. - But it could also be any doubly loaded
extension=
modulegenerating an implicit PHP startup/warning message.
-
p72
p31
p32
没有错误信息
如果您已禁用 err或_reporting
or 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()
。
输出缓冲作为解决方法
PHP 的 输出缓冲 是缓解此问题的一种解决方法。它通常工作可靠,但不应取代正确的应用程序结构和将输出与控制逻辑分开。它的实际目的是最大限度地减少到 Web 服务器的分块传输。
-
p39
-
p40
因此,这两种方法都可能变得不可靠 - 特别是在开发设置和/或生产服务器之间切换时。这就是为什么输出缓冲被广泛认为只是一种拐杖/严格来说是一种解决方法。
另请参阅 基本使用示例 ,了解更多优缺点:
-
PHP 中的输出缓冲是什么?
-
为什么在 PHP 中使用输出缓冲?
-
使用输出缓冲被认为是一种不好的做法吗?
-
使用输出缓冲作为“标头已发送”的正确解决方案的用例
但它在另一台服务器上运行良好!?
如果您之前没有收到标头警告,则 输出 bufferingphp.ini 设置 已更改。它可能在当前/新服务器上未配置。
检查 headers_sent()
您始终可以使用 headers_sent()
来探测是否仍有可能...发送标头。这对于有条件地打印信息或应用其他后备逻辑很有用。
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
有用的后备解决方法是:
-
p74
p49
<meta http-equiv="Location" content="http://example.com/">
p50
<meta http-equiv="Refresh" content="2; url=../target.html">
p51
-
p75
p52
<script> location.replace("target.html"); </script>
p53
但是,当真正的 HTTP header() 调用失败时,这两种方法都可以做出可接受的回退。理想情况下,您始终会将其与用户友好的消息和可点击链接相结合,作为最后的手段。(例如, http_redirect() PECL 扩展就是这样做的。)
为什么 setcookie()
并且 session_start()
也受到影响
和 setcookie()
都 session_start()
需要发送 Set-Cookie:
HTTP 标头。因此适用相同的条件,并且对于过早输出的情况将生成类似的错误消息。
(当然,它们还会受到浏览器中禁用的 cookie 甚至代理问题的影响。会话功能显然还取决于可用磁盘空间和其他 php.ini 设置等。)
更多链接