运行脚本时,出现如下错误:
警告:不能更改头信息-已经(发送了头输出在/some/file.php:12开始)在/some/file.php上线23
错误消息中提到的行包含header()
和setcookie()
调用。
这可能是什么原因?以及如何解决?
在进行任何输出之前,必须调用发送/修改HTTP标头的函数。summary ⇊否则,调用将失败:
警告:无法修改标头信息-标头已发送(输出从script:line开始)
修改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接收第一输出(print
,echo
,<html>
),它会刷新所有收集的头。之后,它可以发送所需的所有输出。但是,那时不可能发送更多的HTTP标头。
该header()
警告包含所有相关信息以查找问题原因:
警告:无法修改标头信息-行100上/www/usr2345/htdocs/index.php中已经发送过的标头(输出始于/ www / usr2345 / htdocs / auth.php:52)
此处的“第100行”指的是header()
调用失败的脚本。
括号内的“输出始于”注释更为重要。它表示先前输出的来源。在此示例中,为auth.php
和line52
。那是您必须寻找过早输出的地方。
典型原因:
来自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>
块之前注明将触发调用的脚本条件。
<!DOCTYPE html>
<?php
// Too late for headers already.
使用模板方案将处理与输出逻辑分开。
<?php
“ script.php第1行”警告之前的空白如果警告指的是line中的输出1
,则在打开令牌之前,它主要是前导空格,文本或HTML <?php
。
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
类似地,它可能发生在附加的脚本或脚本节中:
?>
<?php
PHP实际上吃了一个单一的关闭标签后断行。但这不会补偿多个换行符或制表符或空格之间的空白。
仅换行和空格可能是一个问题。但是也有可能导致这种情况的“不可见”字符序列。最著名的是UTF-8 BOM(字节顺序标记),大多数文本编辑器都不会显示。这是字节序列EF BB BF
,对于UTF-8编码的文档是可选的,并且是多余的。但是,PHP必须将其视为原始输出。它可能显示为
输出中的字符(如果客户端将文档解释为Latin-1)或类似的“垃圾”。
特别是图形编辑器和基于Java的IDE忽略了它的存在。他们没有可视化它(受Unicode标准约束)。但是,大多数程序员和控制台编辑器都这样做:
尽早发现问题很容易。其他编辑器可以在文件/设置菜单中识别其存在(Windows上的Notepad ++可以识别和纠正问题)。检查BOM存在的另一种方法是使用hexeditor。hexdump
通常在* nix系统上可用,如果没有图形版本,则可以简化审核这些问题和其他问题:
一个简单的解决方法是将文本编辑器设置为将文件另存为“ UTF-8(无BOM)”或类似的命名法。否则,新手经常会求助于创建新文件,然后只复制并粘贴以前的代码。
还有一些自动工具可以检查和重写文本文件(sed
/awk
或recode
)。专门针对PHP,有phptags
标记tidier。它将关闭和打开的标签改写为长号和短号形式,而且还可以轻松解决前导和尾随空格,Unicode和UTF-x BOM的问题:
phptags --whitespace *.php
在整个包含目录或项目目录上使用都是理智的。
?>
如果在关闭?>
后提到了错误源,那么这是写出一些空白或原始文本的地方。此时,PHP结束标记不会终止脚本执行。之后的任何文本/空格字符都将作为页面内容写出。
通常建议,特别是对新手来说,?>
应省略尾随的PHP关闭标记。这避免了这些情况的一小部分。(include()d
罪魁祸首通常是剧本。)
如果没有具体错误源,通常是PHP扩展或php.ini设置。
gzip
流编码设置或ob_gzhandler
。extension=
模块生成隐式的PHP启动/警告消息。如果另一个PHP语句或表达式导致警告消息或通知被打印出来,则这也算作过早的输出。
如果您已将禁用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()
故障时打印用户消息。
PHP的输出缓冲是缓解此问题的一种解决方法。它通常可以可靠地工作,但不能替代适当的应用程序结构并将输出与控制逻辑分开。其实际目的是最大程度地减少到Web服务器的分块传输。
该output_buffering=
设置仍然可以提供帮助。在php.ini或现代FPM / FastCGI设置中通过.htaccess甚至.user.ini进行配置。
启用它将允许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">
当超出本<head>
节使用时,这将导致无效的HTML 。大多数浏览器仍然接受它。
另外,JavaScript重定向可用于页面重定向:
<script> location.replace("target.html"); </script>
尽管这通常比<meta>
解决方法更符合HTML要求,但它会依赖具有JavaScript功能的客户端。
但是,当真正的HTTP header()调用失败时,两种方法都会产生可接受的回退。理想情况下,您总是将其与用户友好的消息和可点击的链接结合起来作为最后的选择。(例如,http_redirect() PECL扩展名就是这样做的。)
setcookie()
和session_start()
也受到影响双方setcookie()
并session_start()
需要发送一个Set-Cookie:
HTTP标头。因此,适用相同的条件,并且对于过早的输出情况,将生成类似的错误消息。
(当然,它们还受到浏览器中禁用的cookie甚至代理问题的影响。会话功能显然还取决于可用磁盘空间和其他php.ini设置等。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句