【问题标题】:PHP 5.6: headers_sent intermittently returns true, empty file name, and line 0PHP 5.6:headers_sent 间歇性返回 true、空文件名和第 0 行
【发布时间】:2017-08-07 05:49:03
【问题描述】:

我在我的 PHP 脚本(PHP 5.6、Apache 2.2)中间歇性地遇到这个问题:

警告:无法修改标头信息 - 标头已在第 55 行的 /path/to/index.php 中发送

这个警告没有我在其他问题中看到的“发送者”部分,所以我在有问题的 header()setcookie() 调用之前添加了这段代码:

if (headers_sent($filename, $linenum)){
    echo("Output buffer: #" . ob_get_contents() . "#");
    echo "Headers already sent in $filename on line $linenum: ";
    print_r(headers_list());
}

这是我在问题发生时得到的输出:

Output buffer: ##
Headers already sent in on line 0:
Array (
  [0] => X-Powered-By: PHP/5.6.23
  [1] => Content-type: text/html; charset=UTF-8
)

(旁注:我在 php.ini 中将 output_buffering 设置为 4096 字节,所以这两个标头中的 63 个字符不应该被缓冲并等待更多,而不是过早发送吗?)

这个问题在我第一次启动包含网络服务器的 Docker 容器时出现。之后,它仅(但不是总是)在我一段时间内(可能是一两个小时)第一次访问我的网站时发生,当我调用header()setcookie() 进行登录时用户进入或重定向到登录页面。

我已经反复阅读this answer 到一般的“标头已发送”错误,并且尽我所能排除了这些可能的原因:

  1. 在我调用setcookie()header() 之前,HTML 会阻止或调用printecho
  2. PHP 标记外的空白
  3. 物料清单
  4. auto_prepend_filephp.ini 设置
  5. gzip 流编码 - zlib 已安装,但 zlib.output_compression 已关闭
  6. 复制extension= php.ini 设置

那个答案提到了

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

所以,我现在正在查看我的扩展...get_loaded_extensions 给了我一个 51 长度的数组,其中包含这些条目:

Core, date, ereg, libxml, openssl,
pcre, zlib, filter, hash, Reflection,
SPL, session, standard, apache2handler, bz2,
calendar, ctype, curl, dom, exif,
fileinfo, ftp, gd, gettext, iconv,
mysqlnd, PDO, Phar, posix, shmop,
SimpleXML, snmp, soap, sockets, sqlite3,
sysvmsg, sysvsem, sysvshm, tokenizer, xml,
xmlwriter, xsl, mysql, mysqli, pdo_mysql,
pdo_sqlite, wddx, xmlreader, json, zip, mhash

我没有使用所有这些,所以我计划检查并删除未使用的,并希望其中一个导致问题。

在最坏的情况下,我会尝试将我的 output_buffering 值或使用 ob_start()ob_end_flush() 到我的文件的开头和结尾。我不知道为什么当我当前的 output_buffering 值 4096 没有解决它时,这会解决它,而且我知道这种解决方法有其自身的问题。

我在这里遗漏了什么——我需要检查其他可能的原因吗?我应该尝试不同的 PHP 版本,还是在没有扩展的干净 PHP 安装上运行我的代码子集?

编辑:添加了ob_get_contents() 调用和输出,以及有关能够通过启动新的 Docker 容器来持续重现此内容的信息。删除了关于我的 error_reporting 值的信息;更改此设置仅发现了 always_populate_raw_post_data 弃用通知,修复了对此处描述的问题没有影响。

【问题讨论】:

  • 请提供您设置的完整 PHP 文件代码并检查其中的标题。我想是index.php?如果包含,请提供整个包含链源,包括索引文件。
  • 您是否在入口点脚本的开头调用ob_start()?您的问题尚不清楚,但没有ob_get_contents() 将无法工作。另外,请尝试使用var_dump(ob_get_contents())。我认为这比简单地回显值要好,因为它会说出字符串的长度。
  • 您如何确定修复 always_populate_raw_post_data 问题并没有解决问题?这当然是一个原因。另一方面,查看成功加载的扩展也无济于事;相反的情况可能会触发错误 - failure 加载扩展。如果它显示第 0 行,则可以确定它与代码无关,而是在 PHP 本身初始化时触发了某些东西。
  • @GustavoStraube 啊,我假设ob_get_contents() 可以与我在php.ini 中的output_buffering 设置一起使用。也许我从根本上误解了输出缓冲。
  • @ChristosLytras 这些文件是封闭源代码,但如果有必要,我会看看发布它们。

标签: php http-headers php-extension php-5.6


【解决方案1】:

过去我已经看到由于行结束格式而出现的问题。您是否将文件从一种操作系统环境移动到另一种?

有时隐藏的回车 (/r) 或其他特殊的空白字符会导致此问题,但在文件中不可见。

【讨论】:

  • 感谢您的提示;我正在将文件从 Windows 移动到 PHP 并返回。但是,我未能使用此处描述的几种方法找到任何 CRLF:stackoverflow.com/q/73833/877682
【解决方案2】:

它可以是 UTF8 BOM 字符,或者错误的换行符、不可打印的字符、错误的 php 关闭标记(我不使用它们,以防止出现结束行问题)。即使没有这样的问题,即使你尽一切努力阻止它,它也可能(并且将会)在以后发生。问题可能与 FTP 服务器、文本编辑器、错误的 PHP 配置、编码、错误的 cgi 配置有关。为了防止所有这些我使用空输出缓冲区,只需在第一个 php 文件中开始缓冲

index.php:

if(version_compare(PHP_VERSION, '7.0.0') >= 0 || ob_get_level() < 1)
    ob_start();

HtmlResponce.php:

ob_end_clean();
echo $this->getRenderedContent();

文件响应.php:

ob_end_clean();
readfile($this->content);

在 php5 和 php7 中的输出缓冲差别不大,输出缓冲也可以在 php.ini 中配置,在 cgi 和 apache mod 配置上也有区别。您不应使用“默认”配置进行中继。要处理警告消息,请使用 set_error_handler() 您也将使用 ob_end_clean() 删除它们

如果您开始新项目,请查看symfony components,我在我的 CMS 中使用了很多。这是您避免因奇怪的 PHP 错误而失眠的机会。

【讨论】:

  • 正如我的回答中提到的,我排除了您描述的常见问题(尽我所能),出于性能原因,我想尽可能避免使用ob_start。谢谢你的建议;如果/当我开始一个新的 PHP 项目时,我一定会检查 symfony 组件。
  • 在这种情况下没有性能松动。缓冲区在输出前关闭。没有双重缓冲 - 没有增加内存使用量。
  • 在做了一些研究之后,我认为输出缓冲与性能优势相关的观点似乎是错误的。对于巨大的输出(比如超过 200 KB)来说,这是一个坏主意,但这不适用于我的情况。谢谢你让我直截了当!
【解决方案3】:

在取出一些未使用的旧代码(包括对所有旧代码文件的 require_once 调用)后,此问题不再发生。它可能会在我最初观察到的“间歇性”条件下再次出现,但我重现它的方法不再出现此问题。

我不知道为什么这似乎有帮助 - 删除的行完全在 &lt;php? ?&gt; 标记内,我检查了删除的文件是否有标签、BOM 和 CRLF 之外的空白。

我也不知道为什么这个问题是间歇性的;如果它与被删除的代码或文件有关,那么它应该每次都发生。

感谢大家的cmets和回答!

【讨论】:

    猜你喜欢
    • 2019-12-05
    • 2014-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多