如何修复PHP中的“标头已发送”错误

摩西89

运行脚本时,出现如下错误:

警告:不能更改头信息-已经(发送了头输出在/some/file.php:12开始)在/some/file.php线23

错误消息中提到的行包含header()setcookie()调用。

这可能是什么原因?以及如何解决?

马里奥

发送头之前无输出!

在进行任何输出之前,必须调用发送/修改HTTP标头的函数summary ⇊否则,调用将失败:

警告:无法修改标头信息-标头已发送(输出从script:line开始

修改HTTP标头的一些功能是:

输出可以是:

  • 故意的:

    • printecho以及产生输出的其他函数
    • 原始<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接收第一输出(printecho<html>),它会刷新所有收集的头。之后,它可以发送所需的所有输出。但是,那时不可能发送更多的HTTP标头。

您如何找出过早输出发生的位置?

header()警告包含所有相关信息以查找问题原因:

警告:无法修改标头信息-行100上/www/usr2345/htdocs/index.php中已经发送过的标头(输出始于/ www / usr2345 / htdocs / auth.php:52

此处的“第100行”指的是header() 调用失败的脚本

括号内的输出始于”注释更为重要。它表示先前输出的来源。在此示例中,为auth.phpline52那是您必须寻找过早输出的地方。

典型原因:

  1. 打印,回显

    来自printecho语句的有意输出将终止发送HTTP标头的机会。必须对应用程序流程进行重组以避免这种情况。使用函数和模板方案。确保写出消息之前header()发生呼叫

    产生输出的功能包括

    • printechoprintfvprintf
    • trigger_errorob_flushob_end_flushvar_dumpprint_r
    • readfilepassthruflushimagepngimagejpeg


    以及用户定义的功能。

  2. 原始HTML区域

    .php文件中未解析的HTML部分也可以直接输出。header()必须在任何原始<html>之前注明将触发调用的脚本条件

    <!DOCTYPE html>
    <?php
        // Too late for headers already.
    

    使用模板方案将处理与输出逻辑分开。

    • 将表单处理代码放在脚本之上。
    • 使用临时字符串变量来延迟消息。
    • 实际输出逻辑和混合HTML输出应紧随其后。

  3. <?php“ script.php第1行”警告之前空白

    如果警告指的是line中的输出1,则在打开令牌之前,它主要是前导空格,文本或HTML <?php

     <?php
    # There's a SINGLE space/newline before <? - Which already seals it.
    

    类似地,它可能发生在附加的脚本或脚本节中:

    ?>
    
    <?php
    

    PHP实际上吃了一个单一的关闭标签后断行。但这不会补偿多个换行符或制表符或空格之间的空白。

  4. UTF-8 BOM

    仅换行和空格可能是一个问题。但是也有可能导致这种情况的“不可见”字符序列。最著名的是UTF-8 BOM(字节顺序标记),大多数文本编辑器都不会显示。这是字节序列EF BB BF,对于UTF-8编码的文档是可选的,并且是多余的。但是,PHP必须将其视为原始输出。它可能显示为输出中的字符(如果客户端将文档解释为Latin-1)或类似的“垃圾”。

    特别是图形编辑器和基于Java的IDE忽略了它的存在。他们没有可视化它(受Unicode标准约束)。但是,大多数程序员和控制台编辑器都这样做:

    joes编辑器显示UTF-8 BOM占位符,MC编辑器显示一个点

    尽早发现问题很容易。其他编辑器可以在文件/设置菜单中识别其存在(Windows上的Notepad ++可以识别和纠正问题)。检查BOM存在的另一种方法是使用hexeditorhexdump通常在* nix系统上可用,如果没有图形版本,则可以简化审核这些问题和其他问题:

    beav hexeditor显示utf-8 bom

    一个简单的解决方法是将文本编辑器设置为将文件另存为“ UTF-8(无BOM)”或类似的命名法。否则,新手经常会求助于创建新文件,然后只复制并粘贴以前的代码。

    校正工具

    还有一些自动工具可以检查和重写文本文件(sed/awkrecode)。专门针对PHP,有phptags标记tidier它将关闭和打开的标签改写为长号和短号形式,而且还可以轻松解决前导和尾随空格,Unicode和UTF-x BOM的问题:

    phptags  --whitespace  *.php
    

    在整个包含目录或项目目录上使用都是理智的。

  5. 之后的空白 ?>

    如果在关闭?>提到了错误源,那么这是写出一些空白或原始文本的地方。此时,PHP结束标记不会终止脚本执行。之后的任何文本/空格字符都将作为页面内容写出。

    通常建议,特别是对新手来说,?>应省略尾随的PHP关闭标记。避免了这些情况的一小部分。include()d罪魁祸首通常是剧本。)

  6. 错误源提到为“第0行未知”

    如果没有具体错误源,通常是PHP扩展或php.ini设置。

    • 有时是gzip流编码设置ob_gzhandler
    • 但是也可能是任何双重加载的extension=模块生成隐式的PHP启动/警告消息。

  7. 前面的错误消息

    如果另一个PHP语句或表达式导致警告消息或通知被打印出来,则这也算作过早的输出。

    在这种情况下,您需要避开错误,延迟语句执行,或使用例如isset()@()-抑制消息,但以后任何一个都不会妨碍调试。

没有错误讯息

如果您已将禁用error_reportingdisplay_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服务器的分块传输。

  1. output_buffering=设置仍然可以提供帮助。php.ini现代FPM / FastCGI设置中通过.htaccess甚至.user.ini进行配置
    启用它将允许PHP缓冲输出,而不是立即将其传递到Web服务器。因此,PHP可以聚合HTTP标头。

  2. 它也可以与ob_start();调用脚本顶部的调用一起使用。但是,由于多种原因,它的可靠性较差:

    • 即使<?php ob_start(); ?>启动第一个脚本,空格或BOM之前也可能被改组,使其无效

    • 它可以隐藏用于HTML输出的空格。但是,一旦应用程序逻辑尝试发送二进制内容(例如,生成的图像)​​,则缓冲的无关输出就成为问题。ob_clean()需要更进一步的解决方法。)

    • 缓冲区的大小是有限的,当保留默认值时,缓冲区很容易溢出。而且这也不是少见的事情很难在发生时进行跟踪

因此,这两种方法都可能变得不可靠-尤其是在开发设置和/或生产服务器之间切换时。这就是为什么输出缓冲被广泛认为只是拐杖/严格来说是一种解决方法的原因。

另请参见手册中基本用法示例以及更多优缺点:

但是它在另一台服务器上工作了!

如果以前没有收到标头警告,则输出缓冲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"));
}

有用的后备解决方法是:

  • HTML<meta>标签

    如果您的应用程序在结构上难以修复,则允许重定向的简单(但有些不专业)方法是注入HTML<meta>标记。重定向可以通过以下方式实现:

     <meta http-equiv="Location" content="http://example.com/">
    

    或短暂延迟:

     <meta http-equiv="Refresh" content="2; url=../target.html">
    

    当超出本<head>使用时,这将导致无效的HTML 大多数浏览器仍然接受它。

  • JavaScript重定向

    另外,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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何修复Express中的“错误:发送标头后无法设置标头”

PHP 错误-无法修改标头信息-标头已发送

错误无法修改标头信息 - 标头已发送

PHPpdf标头已发送错误

如何修复标头与PHP中的值分开的JSON结果

如何修复节点中的“发送到客户端后无法设置标头”错误?

如何修复无法设置标头后将其发送到客户端错误?

如何从脚本“index.php”修复 Apache 格式错误的标头:错误标头:在 Laravel 5.6 中注册

标头已发送错误找不到错误

已发送PHP错误标头,不包含空格或先前的回显,打印

在beforeAction中渲染视图时,在Yii中收到“已发送标头”错误

“已发送php标头”不再是问题?

PHP标头已发送-session_start()和setcookie()

Express.js-如何检查标头是否已发送?

PHP-setcookie(); 不起作用(无法修改标头信息-标头已发送)

开发WordPress插件时,无法使用Cookie修复已发送的标头问题?

如何修复反向标头?

PHP标头错误

警告:无法修改标头信息 - 标头已发送

无法修改输出已发送的标头信息标头

无法修改标头信息-标头已发送

PHP发送的HTTP标头

错误:发送标头后无法设置。在nodejs中

无法发送会话缓存限制器-已发送标头-CodeIgniter(Session.php)

如何使用Curl发送空白的接受标头-PHP

标头已在php中发送警告。(如何解决?)

错误:发送标头后无法设置标头

UnhandledPromiseRejectionWarning:错误:发送标头后无法设置标头

NodeJs错误:发送标头后无法设置标头