PHP ob_get_contents“有时”在不应该返回时为空?

时间:2016-10-08 19:53:39

标签: php

问题:我正在目睹一个随机的情况,即ob_get_conents()什么都不返回,什么时候它什么都没有。每天都有成千上万的成功失败。随机

基础:我使用输出缓冲和写入文件将特定的HTML生成输出包装到变量中。然后,在使用新的HTML构建刷新文件之前,将此文件抛出到所有后续命中X分钟。它是一个基本的内联缓存构建器,它固定在较旧的站点代码上。

在目击了一些空页的问题后,我追踪到ob_get_contents()没有为给定的刷新运行返回任何内容。当它下次刷新时,通常都可以。然后,几小时后再次出现空的,空的回归(从未在'同一时间')。

这让我很生气,因为它不一致。当ob_get_contents()的返回为空时,我有php动作给我发电子邮件...带有一堆细节。似乎没有什么能够解释“为什么”。

将代码的复杂版本减少到其核心之后...这就是造成问题的原因:

ob_start();

// A lot of html generation code which would normally just output ...
// This html will ALWAYS have content ...

$guts = ob_get_contents();
if ( empty($guts) ) { /* email me a failure notice! */ }
ob_end_clean();
// write $guts to file and echo ...

其他一些细节:

  • PHP Version 5.5.9-1ubuntu4.19(可能是这个版本的错误?)
  • output_buffering 4096
  • ob_get_level()始终返回“2”
  • HTML生成范围从10KB到92KB,具体取决于哪个
  • 并不总是出现在同一个HTML片段
  • 所有人都没有通过POST或GET args的点击。
  • 大多数是这类代理(所有随机IP):

    • “红宝石”
    • “Mo%20PTT / 2016092702 CFNetwork / 808.0.2 Darwin / 16.0.0”
    • “的FeedBurner / 1.0”

请注意: 总是像其他关于ob_get_contents()的堆栈问题一样返回空。我读过那些,没有帮助......我希望它一直都是,那么这将是一个明显的解决方案。

3 个答案:

答案 0 :(得分:2)

我几个月来一直在同一个PHP版本(5.5.9)上看到类似的问题。也无法切换到其他PHP版本。 我一直在努力在我们的系统中检测到这一点,但幸运的是现在能够追踪并利用它。

在PHP 5.5.9中,函数print_r在内部使用输出缓冲,此版本中报告了有关print_r和输出缓冲的错误。

所以这就是你需要做的......

创建脚本first.php:

<?php
ignore_user_abort(true);// (curl disconnects after 1 second)
ini_set('max_execution_time','180');    // 3 minutes
ini_set('memory_limit','512M');         // 512 MB

function testPrint_r($length)
{
    $test1 = array('TEST'=>'SOMETHING');
    $test2 = print_r($test1, true);
    $test3 = "Array\n(\n    [TEST] => SOMETHING\n)\n";
    if(strcmp($test2, $test3)!==0) {
        throw new Exception("Print_r check failed, output length so far: ".$length);
        // consult your error.log then, or use some other reporting means
    }
}

$message = "123456789\n";
$length = strlen($message);
while(1)
{
    echo $message;
    $total_length += $length;
    testPrint_r($total_length);
}
die('it should not get here');

创建另一个脚本second.php:

<?php
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL,    'http://some.server/first.php');
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);

echo "all done";

这里发生了什么:

第一个脚本只是在循环中输出一些字符。它通过每次迭代回显10个字符来实现。如果您只是调用此脚本,它将在配置的时间后始终超时。

第二个脚本使用CURL调用第一个脚本,但是以断开的方式调用(1秒)。这就是为什么第一个脚本包含忽略用户中止的原因。

不知何故,很可能是由于某些特定于php版本5.5.9的bug,在大约1.8MB的数据已经被回显之后,print_r因此任何额外的输出缓冲使用中断。带有第二个参数TRUE的Print_r只返回任何内容。很可能一些内部或系统端缓冲区耗尽,其他字符不能放在任何地方,或者已经回显的字符被删除。不知道。我无法在阈值编号和phpinfo的任何配置设置之间找到相关性。输出缓冲在我们的系统中没有设置值。

我的建议

因此可能是某些CURL / WGET被使用和断开连接,或者只是在开始时使用和断开了普通浏览器。像“Ruby”,“FeedBurner”这样的名字对我而言就像是libs或机器人。

a)如果您的脚本不是太复杂,请尝试在PHP 5.5.9,print_r中避免输出缓冲。 var_export很好,工作方式不同。

b)在生成输出时,用字符串连接替换回声,直接写入文件。如果你使用smarty,那么这可能是不可能的,因为smarty在内部使用输出缓冲很多。

c)或者创建一个禁用代理列表,如果它们是这些失败的主要原因。

d)顺便说一下。如果ob_get_level()返回2,则表示默认情况下打开输出缓冲在系统中。我认为你不需要那个任务,关闭它可能甚至可以帮助你。值得一试。

尝试在您的系统上运行相同的脚本,并告诉我您的max.echoed大小是多少。

答案 1 :(得分:0)

您可以解决具有重建文件的cron作业的问题,并仅在成功时保存它,并且服务器仅提供静态文件。你会更新得到这样的空页。

答案 2 :(得分:0)

我想我通过将 php_flag output_buffering On 行添加到我的 .htaccess 文件中解决了类似的问题。就我而言,我的 PHP 文件首先包含 HTML 代码,然后是一个调用 ob_get_contents 命令的 PHP 块。有时这个 ob_get_contents 的结果是空的。我不确定这是否总是有效,或者为什么。