本篇文章是对php缓冲output_buffering进行了详细的分析介绍,须要的朋友参考下php
buffer
buffer是一个内存地址空间,Linux系统默认大小通常为4096(4kb),即一个内存页。主要用于存储速度不一样步的设备或者优先级不一样的设备之间传办理数据的区域。经过buffer,可使进程这间的相互等待变少。这里说一个通俗一点的例子,你打开文本编辑器编辑一个文件的时候,你每输入一个字符,操做系统并不会当即把这个字符直接写入到磁盘,而是先写入到buffer,当写满了一个buffer的时候,才会把buffer中的数据写入磁盘,固然当调用内核函数flush()的时候,强制要求把buffer中的脏数据写回磁盘。
浏览器
一样的道理,当执行echo,print的时候,输出并无当即经过tcp传给客户端浏览器显示, 而是将数据写入php buffer。php output_buffering机制,意味在tcp buffer以前,创建了一新的队列,数据必须通过该队列。当一个php buffer写满的时候,脚本进程会将php buffer中的输出数据交给系统内核交由tcp传给浏览器显示。因此,数据会依次写到这几个地方:echo/print -> php buffer -> tcp buffer -> browser缓存
php output_buffering
默认状况下,php buffer是开启的,并且该buffer默认值是4096,即4kb。你能够经过在php.ini配置文件中找到output_buffering配置.当echo,print等输出用户数据的时候,输出数据都会写入到php output_buffering中,直到output_buffering写满,会将这些数据经过tcp传送给浏览器显示。你也能够经过ob_start()手动激活php output_buffering机制,使得即使输出超过了4kb数据,也不真的把数据交给tcp传给浏览器,由于ob_start()将php buffer空间设置到了足够大。只有直到脚本结束,或者调用ob_end_flush函数,才会把数据发送给客户端浏览器。服务器
1.当output_buffering=4096,而且输出较少数据(少于一个buffer)
app
复制代码代码以下:less
<?php
for ($i = 0; $i < 10; $i++) {
echo $i . '<br/>';
sleep($i + 1); //
}
?>
tcp
现象:不是每隔几秒就会有间断性输出,而是直到响应结束,才能看一次性看到输出,在等待服务器脚本处理结束以前,浏览器界面一直保持空白。这是由于,数据量过小,php output_buffering没有写满。写数据的顺序,依次是echo->php buffer->tcp buffer->browser编辑器
2.当output_buffering=0,而且输出较少数据(少于一个buffer)
函数
复制代码代码以下:post
<?php
//经过ini_set('output_buffering', 0)并不生效
//应该编辑/etc/php.ini,设置output_buffering=0禁用output buffering机制
//ini_set('output_buffering', 0); //完全禁用output buffering功能
for ($i = 0; $i < 10; $i++) {
echo $i . '<br/>';
flush(); //通知操做系统底层,尽快把数据给客户端浏览器
sleep($i + 1); //
}
?>
现象:与刚才显示并不一致,禁用了php buffering机制以后,在浏览器能够断断续续看到间断性输出,没必要等到脚本执行完毕才看到输出。这是由于,数据没有在php output_buffering中停留。写数据的顺序依次是echo->tcp buffer->browser
3.当output_buffering=4096.,输出数据大于一个buffer,不调用ob_start()
复制代码代码以下:
#//建立一个4kb大小的文件
$dd if=/dev/zero of=f4096 bs=4096 count=1
<?php
for ($i = 0; $i < 10; $i++) {
echo file_get_contents('./f4096') . $i . '<br/>';
sleep($i +1);
}
?>
现象:响应还没结束(http链接没有关闭),断断续续能够看到间断性输出,浏览器界面不会一直保持空白。尽管启用了php output_buffering机制,但依然会间断性输出,而不是一次性输出,是由于output_buffering空间不够用。每写满一个php buffering,数据就会发送到客户端浏览器。
4.当output_buffering=4096, 输出数据大于一个tcp buffer, 调用ob_start()
复制代码代码以下:
<?php
ob_start(); //开启php buffer
for ($i = 0; $i < 10; $i++) {
echo file_get_contents('./f4096') . $i . '<br/>';
sleep($i + 1);
}
ob_end_flush();
?>
现象:直到服务端脚本处理完成,响应结束,才看到完整输,输出间隔时间很短,以致你感觉不到停顿。在输出以前,浏览器一直保持着空白界面,等待服务端数据。这是由于,php一旦调用了ob_start()函数,它会将php buffer扩展到足够大,直到ob_end_flush函数调用或者脚本运行结速才发送php buffer中的数据到客户端浏览器。
output buffering函数
1.ob_get_level
返回输出缓冲机制的嵌套级别,能够防止模板重复嵌套本身。
1.ob_start
激活output_buffering机制。一旦激活,脚本输出再也不直接出给浏览器,而是先暂时写入php buffer内存区域。
php默认开启output_buffering机制,只不过,经过调用ob_start()函数据output_buffering值扩展到足够大。也能够指定$chunk_size来指定output_buffering的值。$chunk_size默认值是0,表示直到脚本运行结束,php buffer中的数据才会发送到浏览器。若是你设置了$chunk_size的大小,则表示只要buffer中数据长度达到了该值,就会将buffer中的数据发送给浏览器。
固然,你能够经过指定$ouput_callback,来处理buffer中的数据。好比函数ob_gzhandler,将buffer中的数据压缩后再传送给浏览器。
2.ob_get_contents
获取一份php buffer中的数据拷贝。值得注意的是,你应该在ob_end_clean()函数调用以前调用该函数,不然ob_get_contents()返回一个空字符中。
3.ob_end_flush与ob_end_clean
这二个函数有点类似,都会关闭ouptu_buffering机制。但不一样的是,ob_end_flush只是把php buffer中的数据冲(flush/send)到客户端浏览器,而ob_clean_clean将php bufeer中的数据清空(erase),但不发送给客户端浏览器。ob_end_flush调用以后,php buffer中的数据依然存在,ob_get_contents()依然能够获取php buffer中的数据拷贝。而ob_end_clean()调用以后ob_get_contents()取到的是空字符串,同时浏览器也接收不到输出,即没有任何输出。
惯用案例
经常在一些模板引擎和页面文件缓存中看到ob_start()使用。下面湿CI中加载模板的程序代码:
复制代码代码以下:
<SPAN style="WHITE-SPACE: pre"> </SPAN>/* * Buffer the output * * We buffer the output for two reasons: * 1. Speed. You get a significant speed boost. * 2. So that the final rendered template can be * post-processed by the output class. Why do we * need post processing? For one thing, in order to * show the elapsed page load time. Unless we * can intercept the content right before it's sent to * the browser and then stop the timer it won't be accurate. */ ob_start(); // If the PHP installation does not support short tags we'll // do a little string replacement, changing the short tags // to standard PHP echo statements. if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE) { //替换短标记<?=***> echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path)))); } else { include($_ci_path); // include() vs include_once() allows for multiple views with the same name } //记录调试信息 log_message('debug', 'File loaded: '.$_ci_path); // Return the file data if requested if ($_ci_return === TRUE) { $buffer = ob_get_contents(); @ob_end_clean(); return $buffer; } /* * Flush the buffer... or buff the flusher? * * In order to permit views to be nested within * other views, we need to flush the content back out whenever * we are beyond the first level of output buffering so that * it can be seen and included properly by the first included * template and any subsequent ones. Oy! * */ if (ob_get_level() > $this->_ci_ob_level + 1) { ob_end_flush(); } else { //将模板内容添加到输出流中 $_ci_CI->output->append_output(ob_get_contents()); //清除buffer @ob_end_clean(); }