php缓冲区的介绍及应用

缓冲区的作用是把输入或输出的内容先放进内存,而不直接显示或读取。php提供了一些输出缓冲区的函数来控制输出。当执行echo、print之类的会输出数据的代码,php就会将要输出的数据保存在自己的缓冲区,然后等待输出。

缓冲流程

缓冲一般有3个阶段,php的缓冲、web服务器的缓冲、浏览器的缓冲。

和缓冲相关的配置

在php.ini中有几个和缓冲区相关的配置,下面我们一起来看看

output_buffering

该参数用来设置是否开启缓冲区以及缓冲区的大小,有三种设置方式

  • On 当设置为on时表示开启缓冲区且缓冲区不受限制(使用它要注意)
  • Off 关闭输出缓冲
  • 数字 表示开启缓冲区且设置缓冲区的大小,单位是字节(建议的配置)

output_handler

默认为NULL,它是设置一个回调函数,将脚本的所有输出,用所定义的函数进行处理。类似与ob_start($output_callback)。

$output_callback:

  • ob_gzhandler : 使用ext/zlib压缩输出
  • mb_output_handler : 使用ext/mbstring转换字符编码
  • ob_iconv_handler : 使用ext/iconv转换字符编码
  • ob_tidyhandler : 使用ext/tidy整理输出的HTML文本

implicit_flush

若开启此项,则表示当php有数据发送到web server时,web sever会直接输出。这相当于在每次调用echo或print后调用flush函数。开启次选项会严重影响性能,一般只建议开发阶段可以开启。

和缓冲区有关的函数

ob_start

此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。

ob_clean

删除内部缓冲区的内容,不关闭缓冲区(不输出)。

ob_end_clean

删除内部缓冲区的内容,关闭缓冲区(不输出)。

ob_get_clean

返回内部缓冲区的内容,关闭缓冲区(不输出)。相当于执行 ob_get_contents()和 ob_end_clean()

ob_flush

发送内部缓冲区的内容到浏览器,删除缓冲区的内容,不关闭缓冲区。

ob_end_flush

发送内部缓冲区的内容到浏览器,删除缓冲区的内容,关闭缓冲区。

ob_get_flush

返回内部缓冲区的内容,并关闭缓冲区,再释放缓冲区的内容。相当于ob_end_flush()并返回缓冲区内容。

flush

一般会和ob_flush()一起使用,ob_flush()发送php自身缓冲区,而flush则刷新web server缓冲区。

 if (ob_get_level() == 0) ob_start();
  for ($i = 0; $i<10; $i++){
          echo "<br> Line to show.";
          echo str_pad('',4096)."\n";    
   
          ob_flush();
          flush();
          sleep(2);
  }
   
  echo "Done.";
  ob_end_flush();

ob_get_contents

返回缓冲区的内容,不输出。

ob_get_length()

返回内部缓冲区的长度,如果缓冲区未被激活,该函数返回FALSE。

注意事项

ob_flush和flush的次序关系

ob_flush是和php自身相关的,而flush操作的是apache的缓冲区,所以我们在使用这两个函数的时候,需要先执行ob_flush,再执行flush,因为我们需要先把数据从PHP上发送到apache,然后再由apache返回到浏览器。如果php还没有把数据刷新到apache,就调用了flush,则apache无任何数据返回到浏览器.

webserver不刷新缓冲问题

有些webserver,他自身的输出缓冲区会有一些限制,比如nginx,他有一个配置fastcgi_buffer_size 4k, 就是表明,当自身的输出缓冲区的内容达到4K才会刷新,所以为了保证内容的数据,可以添加以下代码,保证内容长度

echo str_repeat(" ",4096);

输出缓冲应用

使 header() 函数前可以有echo代码

输出缓冲函数可以让你自由控制脚本中数据的输出。它非常地有用,特别是对于:当你想在数据已经输出后,再输出文件头的情况。输出缓冲函数不会对setcookie以及header造成影响。

复制代码 代码如下:

ob_start();                   //打开缓冲区 
echo "Hello\n";               //输出
header(“location:index.php”); //把浏览器重定向到index.php  
ob_end_flush();               //输出全部内容到浏览器

上面代码不会报错,但如果没有开启输出缓冲的话就会出问题。

保存 phpinfo() 函数的输出

我们知道phpinfo里面有大量有用的信息,我们有时候经常会去查找相关的信息。但如果直接输出到浏览器,那么许多敏感信息也会显示。我们可以将phpinfo()产生的信息保存到一个文件里。

<?php

ob_start();                     //打开缓冲区  
phpinfo();                       //使用phpinfo函数  
$info = ob_get_contents();       //得到缓冲区的内容并且赋值给$info  
$file = fopen('info.txt', 'w'); //打开文件info.txt
ob_clean(); // 关闭缓冲区
fwrite($file, $info);           //写入信息到info.txt  
fclose($file);                   //关闭文件info.txt

静态模版技术

一般的cms都有静态模板,它的好处是节省资源(不走php程序)、加快响应。使用输出缓冲可以完成该应用。

<?php

ob_start();   //打开缓冲区  

echo '...';   // 输出内容

$fp = fopen('index.html', 'w');
fwrite($fp, ob_get_contents());
fclose($fp);

ob_end_clean();

收集错误信息并屏蔽错误输出

在系统上线的时候,我们通常不希望用户看到错误信息,但希望将错误信息保存到日志里,方便我们日后排查。使用输出缓冲,可以来完成。

<?php

// 处理非致命错误
function errorHandle($errLevel, $errInfo, $errFile, $errLine)
{
ob_start();
   echo '错误:'.PHP_EOL;
   print_r(['file' => $errFile, 'level' => $errLevel, 'line' => $errLine, 'info' => $errInfo]);
   file_put_contents('error.txt',ob_get_contents(), FILE_APPEND );
   ob_clean();
}

// 处理致命错误及异常
function exceptionHandle(Throwable $ex)
{
ob_start();
   echo '异常:'.PHP_EOL;
   print_r(['file' => $ex->getFile(), 'level' => $ex->getCode(), 'line' => $ex->getLine(), 'info' => $ex->getMessage()]);
   file_put_contents('error.txt',ob_get_contents(), FILE_APPEND );
    ob_clean();
}

set_error_handler('errorHandle');
set_exception_handler('exceptionHandle');

echo $a;
new b();

文章部分内容参考自:https://segmentfault.com/a/1190000009220776#item-4