我在我的PHP脚本(PHP 5.6,Apache 2.2)中间歇性地遇到了这个问题:
Warning: Cannot modify header information - headers already sent in /path/to/index.php on line 55
此警告没有我在其他问题中看到的"发送者"部分,所以我在违规的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个字符是否应该被缓存并等待更多,而不是过早发送?)
第一次启动包含Web服务器的Docker容器时出现此问题。之后,当我第一次访问我的网站时(可能是一两个小时),当我调用header()和setcookie()来记录用户或重定向到登录页面。
我已阅读并重读了一般"已发送标题"错误的答案,并且尽我所能,我已排除了这些可能的原因:
在我调用setcookie()或header()之前,HTML阻止或调用print,echo等
我的PHP标签之外的空格
物料清单
auto_prepend_file php.ini设置
gzip流编码 - 已安装zlib,但zlib.output_compression已关闭
重复extension= php.ini设置
答案提到了这一点
It's typically a PHP extension or php.ini setting if no error source is concretized.
所以,我现在正在查看我的扩展... 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的东西并没有解决问题的?这当然是一个原因。另一方面 - 看成功加载的扩展程序无济于事;很可能是错误会被触发 - 无法加载扩展。如果它表示第0行,您可以确定它与代码无关,但在PHP本身初始化时会触发某些内容。
@GustavoStraube啊,我假设ob_get_contents()可以在php.ini中使用我的output_buffering设置。也许我从根本上误解了输出缓冲。
@ChristosLytras文件是封闭源文件,但如果有必要,我会看到发布它们。
@Narf我在php.ini中有这一行:always_populate_raw_post_data = -1。感谢关于扩展的提示;我将寻找方法来识别任何无法加载的扩展(但我??希望看到这种效果的日志)。
在取出一些未使用的遗留代码(包括require_once)调用所有遗留代码的文件后,此问题不再发生。它可能会在我最初观察到的"间歇"条件下重现,但我复制它的方法不再表现出这个问题。
我不知道为什么这似乎有帮助 - 删除的行完全在标签内,我检查了删除的文件,以查找标签,BOM和CRLF之外的空白区域。
我也不知道为什么这个问题是断断续续的;如果它与删除的代码或文件有关,那么它应该每次都发生。
感谢所有的评论和答案!
它可以是UTF8 BOM字符,或错误的换行符,不可打印的字符,错误的php关闭标记(我不使用它们,以防止终止问题)。即使没有这样的问题,即使你做了所有事情来阻止它,它也可以(并且将会)发生。问题可能出在FTP服务器,文本编辑器,错误的PHP配置,编码,错误的cgi配置上。为了防止所有这些我使用空输出缓冲区,只需在第一个php文件中启动buffreing
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();
FileResponce.php:
ob_end_clean();
readfile($this->content);
在php5和php7输出缓冲有一些差异,也可以在php.ini中配置输出缓冲,并具有cgi和apache mod配置的差异。你不要在"默认"配置上进行中继。要处理警告消息,请使用set_error_handler(),您也可以使用ob_end_clean()去除它们
如果你开始新的项目看看symfony组件,我在我的CMS中使用了很多。这是你用奇怪的PHP漏洞防止不眠之夜的机会。
正如我在回答中所提到的,我排除了你所描述的常见问题(尽我所能),并且出于性能原因我想尽可能避免使用ob_start。 感谢您的建议; 如果/当我开始一个新的PHP项目时,我肯定会检查symfony组件。
在这种情况下没有性能松散。 输出前缓冲关闭。 没有双缓冲 - 没有增加内存使用量。
在做了一些研究之后,看起来我认为输出缓冲与性能优势相关是错误的。 对于巨大的输出(例如超过200 KB)来说这是一个坏主意,但这不适用于我的情况。 谢谢你让我直截了当!
我已经看到由于过去的行结束格式而出现问题。您是否将文件从一个操作系统环境移动到另一个?
有时隐藏的回车符(/ r)或其他特殊的空白字符可能会导致这种情况,但在文件中不可见。
谢谢你的提示; 我正在将文件从Windows移动到PHP并返回。 但是,我没有使用这里描述的几个方法找到任何CRLF:stackoverflow.com/q/73833/877682