Home  >  Article  >  Backend Development  >  Detailed introduction to php output buffer (code example)

Detailed introduction to php output buffer (code example)

不言
不言forward
2019-03-01 11:59:452620browse

This article brings you a detailed introduction (code example) about the PHP output buffer. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

PHP output buffer:
Buffer: is actually a memory address space. It is used to store areas for data transmission between devices with unsynchronized speeds or devices with different priorities. Buffering can reduce the waiting time for interaction between processes, so that when data is read from a slow device, the operation process of the fast device will not be interrupted.

PHP's output stream contains a lot of content. It is usually text that developers want PHP to output. Most of these texts are output using the echo or printf() function

1>Any function that outputs content will use the output buffer

This refers to a normal PHP script. If you are developing a PHP extension, the function (C function) used may directly output and write to the SAPI buffer layer without going through the output buffer layer (we can write it in the PHP source file main /php_output.h Learn the API documentation for these C functions)

2>The output buffer is not the only layer used to buffer output, it is actually just one of many layers. The behavior of the output buffer layer is different from that used Related to SAPI (Web or CLI), different SAPI may have different behaviors

                                                                                                                                                                                                                                                                     What is commonly known as the 'output buffer'

3>Output buffer in SAPI, these are layers in PHP. When the output bytes leave PHP and enter the lower layers of the computer architecture, the buffer Areas will continue to appear (terminal buffer (terminal buffer), fast-cgi buffer, web server buffer, operating system buffer, TCP/IP stack buffer, etc.).

The SAPI of PHP CLI is a bit special. CLI is also called the command line interface. It will force the output_buffer option in the php.ini configuration to 0, which means that the default PHP output buffer is disabled, so in the CLI, by default you The content to be output will be passed directly to the SAPI layer, unless you manually call the ob_() class function, and in the CLI, the value of implicit_flush will also be set to 1,

Note: We often confuse the role of implicit_flush, PHP The source code says it all: when implicit_flush is set to on (value 1), as soon as any output is written to the SAPI buffer, it will be flushed (flush, meaning to write the data to the lower layer, and the buffer will be Clear)
That is to say, when any data is sent to the CLI SAPI, the CLI SAPI will immediately transfer the data to its next layer, usually the standard output pipe, write() and fflush(). The function is responsible for doing this

Default PHP output buffer

: If you use a SAPI different from the CLI, such as PHP-FPM, the following three buffer-related ones will be used php.ini configuration options output_buffering implicit_flush
output_handler
First of all, you cannot use the ini_set() function to modify the values ​​​​of these options when running, because these values ​​​​will be changed when the PHP program starts. It is parsed before running any script, so you can use ini_set() to change the value at runtime, but it will not take effect. We can only change their values ​​by editing the php.ini file or using the -d option when executing the PHP program
By default, the PHP distribution will set output_buffering to 4096 words in php.ini section, if its value is set to ON, the default output buffer size is 16KB

Using a buffer for output content in a Web application environment is good for performance
: The default 4K setting is a suitable value, meaning that you can first write 4096 ASCLL characters, then communicate with the SAPI layer below, and transmit the message byte by byte through the Socket in a web application environment. The way is not good for performance, a better way is to transfer all the content to the server at once, or at least transfer it piece by piece. The less the number of data exchanges between layers, the better the performance, which should always be maintained The output buffers are available, and PHP will be responsible for transmitting their contents to the end user after the request is completed. The developer does not have to do anything

Implicit_flush is set to off by default, so that new data writing will not refresh the SAPI. For the FastCGI protocol, the refresh operation is to send a FastCGI array packet after each write. If the FastCGI array packet is sent before sending the data packet It would be better if the buffer is full. If you need to manually refresh the SAPI buffer, use the flush() function. If you want to write a refresh, you can set implicit_flush or call the ob_implicit_flush() function once.
Recommended configuration:
output_buffering=4096
implicit_flush = Off/no
To modify the size of the output buffer, be sure to use a value that is a multiple of 4/8, which are 32/64-bit operating systems respectively

output_handler is a callback function that can be used in Modify the content in the buffer before refreshing the buffer

The content in the buffer will be passed to the callback function of your choice (only one can be used) to perform content conversion , so if you want to get the content that PHP gives to the web server and the user, you can use the output buffer callback. The output refers to: message header, message header. The HTTP message header is also part of the output buffer layer
Message header and message body:
In fact, any PHP function related to the output of the message header (header(), setcookie() , session_start()) all use the internal sapi_header_op function, which will only write the content into the message header buffer. When we use the printf() function, the content will first be written to the output buffer (possibly multiple). When the content of the output ring area needs to be sent, PHP will first send the message header, and then Send message body. In order to handle everything in PHP, if you want to do it yourself, you can only disable the output buffer

User output buffer:

If you want to use the default PHP output buffer layer, just CLI cannot be used because it has disabled this layer

/*launched via php -d output_buffering=32 -d implicit_flush=1 
 * */
echo str_repeat('a',31);
sleep(3);
echo 'b';
sleep(3);
echo 'c';
?>

The default output buffer size is set to 32 bytes. When the program is running, it will first write 31 bytes, then sleep, and then write 1 byte. , this byte fills the buffer, it will immediately refresh itself and pass the data inside to the buffer of the SAPI layer. Because implicit_flush is set to 1, the buffer of the SAPI layer will also be immediately refreshed to the next layer, so Output aa...b, then sleep, and then output a byte. At this time, the buffer has 31 empty bytes, but the script is executed, so the buffer containing this byte will be refreshed immediately, which will be output on the screen. c

User output buffer: Created through ob_start(), we can create multiple such buffers (until the memory is exhausted). These buffers form a stack structure, and each new buffer will be stacked to On the previous buffer, every time it is filled or overflows, a refresh operation will be performed, and then the data will be passed to the next buffer

  function callback($buffer)
{
  // replace all the apples with oranges
  return ucfirst($buffer);
}
 function callback1($buffer)
{
  // replace all the apples with oranges
  static $a=0;
  return $a++.'-'.$buffer."\n";
}
ob_start('callback1',10);
ob_start("callback",3);
echo "fo";
sleep(2);
echo 'o';
sleep(2);
echo "barbazz";
sleep(2);
echo "hello";

According to the first-in-last-out principle of the stack, any output will Store it in buffer 2 first. The size of buffer 2 is 3 bytes, so when the first echo statement outputs the string 'fo', it will be stored in buffer 2 first, with one character missing. When the second echo statement outputs 'o' , buffer 2 is full, so it flushes. The callback function of ob_start() is called before refreshing. This function converts the first letter of the buffer to uppercase, so the output is 'Foo', then it will be saved in buffer 1, and the third output is 'barbazz' , it will still be placed in buffer 2 first. This string has 7 bytes. Buffer 2 has overflowed, so it is refreshed immediately. The structure obtained by calling the callback function is 'Barbazz', and then passed to buffer 1. , the ten characters of 'FooBarbazz' are saved in buffer 1. Buffer 1 will be refreshed. Similarly, the callback function of ob_start() will be called first. The callback function of buffer 1 will add the model to the string signature, so The first line is '0-FooBarbazz',

The last echo statement outputs a string 'hello', which is greater than three characters, so buffer 2 will be triggered, because the script is executed at this time, so it is also Buffer 1 will be flushed immediately, getting '1-Hello'.

Therefore, using the echo function is such a simple thing, but it is also complicated if it involves the buffer and performance, so pay attention to the size of the output content using echo. If the buffer configuration is different from the output If the content is similar, the performance will be better. If the buffer configuration is smaller than the output content, the output content needs to be segmented in the application.

Output buffer mechanism: Mainly because the entire buffer layer has been rewritten after 5.4. When we develop our own PECL extension, we can declare our own output buffer callback method, so Can be distinguished from other PECL extensions to avoid conflicts

输出缓冲区的陷阱

      有些PHP的内部函数也使用了输出缓冲区,它们会叠加到其他的缓冲区上,这些函数会填满自己的缓冲区然后刷新,或者返回里面的内容,比如print_r()、higglight_file()和highlight_file::handle()都是此类,所以不应该在输出缓冲区的回调函数中使用这些函数,这样会导致未定义的错误

     同样的道理,当PHP执行echo、print时,也不会立即通过tcp输出到浏览器,而时将数据先写入PHP的默认缓冲区,我们可以理解PHP有一套自己的输出缓冲机制,在传送给系统缓存之前建立一个新的队列,数据经过该队列,当一个PHP缓冲区写满以及脚本执行逻辑需要输出时,脚本会把里面的数据传输给SAPI浏览器

    echo/print->php输出缓冲区->SAPI缓冲区->TCP缓冲区->浏览器缓冲区->浏览器展示

     ob_flush()和flush()区别

        ob_flush():把数据从php的缓冲区中释放出来

        flush():把不再缓冲区中的或者说是被释放出来的数据发送到浏览器,严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区.

      在nginx中ob_flush和flush两个都失效

        解决办法:  发现在nginx的配置中,有如下的设置

              fastcgi_buffer_size 128k;

     fastcgi_buffers 8 128k;

Nginx会缓冲PHP输出的信息,当达到128k时才会将缓冲区的数据发送给客户端,那么我们首先需要将这个缓冲区调小

比如:

     fastcgi_buffer_size 4k;

     fastcgi_buffers 8 4k;

并且,必须禁用gzip

             gzip off;

然后,在php中,在ob_flushflush前,输出一段达到4k的内容,例如:

            echo str_repeat(‘ ‘, 1024*4);

到此,PHP就可以正常通过ob_flushflush逐行输出需要的内容了。

输出缓冲区实践

    1> 通过ob_start()函数手动处理PHP缓冲区机制,这样即便输出内容超过配置参数大小,也不会把数据传输给浏览器,ob_start()将PHP缓冲区空间设置到足够大,只有脚本执行结束后或调用ob_end_flush()函数,才会把数据发送给浏览器 

for($i=0;$i<10;$i++){
echo $i.&#39;<br/>&#39;;
sleep($i+1);
}

 执行之后不会每隔几秒就有输出,知道脚本循环结束后,才会一次性输出,这是因为数据量太小,输出缓冲区没有写满

2>当修改output_buffering=0

for($i=0;$i<10;$i++){
echo $i.&#39;<br/>&#39;;
flush();
sleep($i+1);
}

 因为缓冲区的容量设置为0,禁用PHP缓冲区机制,这是我们在浏览器看到断断续续输出,而不必等到脚本执行完毕才看到输出,这是因为数据没有在缓存中停留

3>我们把参数修改为output_buffering=4096,输出数据大于一个缓冲区,不调用ob_start()函数

    首先先输出一个4k的内容记下来加本来输出的内容:   

for($i=0;$i<10;$i++){
echo   echo str_repeat(&#39; &#39;, 1024*4*8).$i<br/>;
sleep($i);
}

发现可以HTTP连接未关闭,可以看到间断输出,尽管启用了PHP输出缓冲区机制,但是也不是一次性输出,这还是因为PHP缓冲区空间不够,每写满一个缓冲区,数据就会发送到浏览器。

4>参照上例子,这次我们调用ob_start()

ob_start(); //开启PHP缓冲区
for($i=0;$i<10;$i++){
echo   echo str_repeat(&#39; &#39;, 1024*4*8).$i<br/>;
sleep($i);
}

等到服务端脚本全部处理完,响应结束才会看到完整的输出,在输出前浏览器会一直保持空白,这是因为,PHP一旦调用了ob_start()会将PHP缓冲区扩展到足够大,知道ob_end_flush函数调用或者脚本运行结束才发送PHP缓冲区中的数据到客户端浏览器

  可以通过tcpdump命令监控TCP的报文,来观察一下使用ob_start()和没有使用它的区别

 总结:ob_start激活output_buffering机制,一旦激活,脚本不再直接输出给浏览器,而是先暂时写入PHP缓冲区

            PHP默认开启out_buffering机制,通过调用ob_start函数把output_buffering值扩展到足够大,也可以通过$chunk_size来指定output_buffering的值,$chunk_size默认值是0,表示直到脚本运行结束后,PHP缓冲区中的数据才会发送到浏览器,若设置了$chunk_size的大小,则只要缓冲区达到这个值,就会发送给浏览器你

           可以通过指定output_callback参数来处理PHP缓冲区的数据,比如ob_gzhandler()将缓冲区中的数据压缩后传送给浏览器,ob_get_contents()是获取一份PHP缓冲区中的数据拷贝

ob_start();
echo date(&#39;Y-m-d h:i:s&#39;);

$output=ob_get_contents();
ob_end_flush();
echo &#39;<!output>&#39;.$output;

 后者是从ob_get_contents取的缓冲区的内容

 ob_end_flush()与ob_end_clean(0这两个函数都会关闭输出缓冲,区别是前者只是把PHP缓冲中的数据发生给客户端浏览器,而后者将PHP缓冲区中的数据删掉,但不发送给客户端,前者调用之后数据依然存在,ob_get_contents()依然可以获取PHP缓冲区中的数据拷贝

输出缓冲与静态页面

     大家都知道静态页面的加载速度快,不用请求数据库,以下就是生成静态页面的脚本代码

echo str_pad(&#39;&#39;,1024);//使缓冲区溢出
ob_start();//打开缓冲区
$content=ob_get_contents();//获取缓冲区内容
$f=fopen(&#39;./index.html&#39;,&#39;w&#39;);
fwrite($f,$content);//写入到文件
fclose($f);
ob_end_clean()清空并关闭缓冲区

  在一些模板引擎和页面文件缓冲中ob_start()函数被使用,如 WP、Drupal、Smarty,例如:

在Wp经常在主题目录header.php看到类似的代码:

只有一个flush(),它的所在的位置就是告诉浏览器那一部分的缓存需要更新,即页面头部以上部分需缓存

以及Wp的部分代码,可以看到,在缓冲区开启时,加入自己的回调方法
 内容压缩输出:就是把输出到客户端浏览器的内容进行压缩

好处:降低客户端对服务器出口带宽的占用,提升带宽的利用率。降低Web服务器(如Nginx、Apache、Tomcat等)处理文本时引入的开销,用户端可以减少网络传输延时对用户体验的影响,降低浏览器加载页面内容时占用的内存,有利于改善浏览器稳定性 

ob_start(&#39;ob_gzhandler&#39;);//使用gz格式压缩输出
print&#39;my contents&#39;;
ob_end_flush();

   之压缩当前脚本与缓冲区,对其他脚本没有影响,PHP还提供另外一种压缩方式,在php.ini修改

  zlib_output_compression=On

  这样输出时所有页面都以zlib的压缩方式输出,但是两者混用是毫无意义的,只会额外消耗CPU性能,让它压缩已经压缩好的内容,但是基于实践,使用PHP压缩效果并不是十分理想,通常做法是放在Web服务器,比如Apache启用defate、Nginx使用gzip的方式都比PHP端压缩效果好得多

 输出缓冲允许第三方库和应用框架(Laravel、TP等)开发者完全控制它们自己输出的内容。比如把他们放在一个全局缓冲区中处理,对于任何输出流的内容(如数据压缩)和任何HTTP消息头、PHP都以正确的书序发送,使用输出缓冲区能够有效地节省带宽,比如图片、字体、CSS、JS等前端内容,特别是限制前端框架也越来越来,让它使用户反应速度更快,从而有效提高系统性能

The above is the detailed content of Detailed introduction to php output buffer (code example). For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete