缓冲区是什么?
简单来说,缓冲区的作用是将输入或输出的内容暂时存储在内存中,而不是立即显示或读取。缓冲区的本质作用是协调高速CPU和相对缓慢的IO设备(如磁盘)之间的运作。
PHP在执行过程中,缓冲区在哪里使用?
要了解PHP的缓冲区,需要知道在执行PHP时,缓冲区被设置在什么地方。当执行PHP时,如果遇到echo、print_r等会输出数据的代码,PHP会将要输出的数据放入PHP自身的缓冲区,等待输出。当PHP自身的缓冲区接收到指令,指示要输出缓冲区的内容时,会将缓冲区内的数据输出到Apache服务器上。Apache接收到PHP输出的数据后,再将该数据存储到Apache自身的缓冲区中,等待输出。当Apache接收到指令,只需输出缓冲区的内容时,会将缓冲区的内容输出并返回给浏览器。
由此可见,当PHP需要输出数据时,数据会经过两个缓冲区(一个是PHP自身的,另一个是Apache的),然后再返回给浏览器。
缓冲区在PHP中起到什么作用?
1. 最常见的用途是在使用header函数之前已经输出了一些数据,这可能导致错误,例如“Cannot modify header information – headers already sent by”的错误。这个错误的原因是,在header之前已经输出了一些数据,而在输出这些数据的同时,Apache会向浏览器发送一个响应状态。之后,你又再次使用header函数发送HTTP标头,这将导致错误。错误的意思是:HTTP标头已经发送出去了,你不能对其进行修改。
为什么使用缓冲区可以避免这个错误?
因为header函数不受缓冲区的影响。当遇到header函数时,PHP会立即执行Apache发送HTTP标头到浏览器的操作。当PHP打开输出缓冲区后,这些输出的数据会被暂存在缓冲区中,等待输出。这样就可以避免之前发生的错误。
2. 通过PHP编写文件下载程序时,缓冲区也发挥着重要作用。当用户请求下载文件时,PHP会将文件内容写入缓冲区,然后将缓冲区的内容发送给浏览器。这样可以提高文件下载速度,减少用户等待时间。同时,缓冲区还可以用于处理大文件的下载,避免一次性将整个文件加载到内存中导致的内存不足问题。
为了让文件下载更安全,同时提高更多的可控性,很多朋友都喜欢用PHP写文件下载页面。其原理很简单,就是通过fwrite把文件内容读出并显示,然后通过header来发送http标头,让浏览器知道这是一个附件,这样就可以达到提供下载的效果。如果用上面的办法提供下载页面,会碰到一个效率问题。如果一个文件很大,假设为100M,那么在不开启缓冲区输出的情况下必须要把100M数据全部读出,然后一次返回到页面上。如果这样做,用户将会在所有数据读完之后才会得到响应,降低了用户体验感。如果开启了输出缓冲区当PHP程序读完文件的某一段后马上输出到apache然后让apache马上返回到浏览器这样就可以减少用户等待时间。那后面的数据怎么办呢?我们可以写一个while循环一直一段一段地读取文件每读一段就马上输出直到把文件全部输出为止这样浏览器就可以持续地接受到数据而不必等到所有文件读取完毕。
另外该做法还解决了另外一个很严重的问题例如一个文件是100M如果不开启缓冲区的情况下则需要把100M文件全部读入内存然后再输出但是如果PHP程序做了内存限制呢为了保证服务器稳定管理员通常会把PHP执行内存设一个限制(通过php.ini总的memory_limit其默认值是8M)也就是每个PHP程序使用的内存不能使用超过这个值的内存假设该值为8M而要读入的文件是100M根本就没有足够的内存来读入该文件这个时候我们就需要用到上面的办法来解决这个问题每次只读某一段这样就可以避免了内存限制。
现在很多公司有这么一个需求某个页面在第一次访问的时候会执行PHP然后把显示的内容返回到浏览器同时需要把这次显示的内容保存到服务器上这就是所谓的“静态页面缓存”。那怎么样才能做到把内容返回到浏览器的同时把数据保存到服务器上呢?这就要用到输出缓冲区了。
ob_start(); echo 'aaa'; $string = ob_get_contents(); file_put_contents('a.html', $string); ob_flush(); flush();
与输出缓冲区有关的配置可以通过以下命令进行设置:
- output_buffering: 是否启用输出缓冲区默认为开启状态
- output_handler: 自定义输出处理函数
- output_buffering_manual: 是否手动控制输出缓冲区关闭状态
在PHP.INI中,有两个与缓冲区紧密相关的配置项:output_buffering和implicit_flush。
1. output_buffering:该配置直接影响的是php本身的缓冲区,有3种配置参数:on、off和xK(x为某个整型数值)。
- on:开启缓冲区
- off:关闭缓冲区
- 256k:开启缓冲区,当缓冲区的内容超过256k时,自动刷新缓冲区(将数据发送到apache)。
2. implicit_flush:该配置直接影响apache的缓冲区,有2种配置参数:on和off。
- on:自动刷新apache缓冲区,即当php发送数据到apache的缓冲区时,不需要等待其他指令,直接将输出返回到浏览器。
- off:不自动刷新apache缓冲区,接受到数据后,等待刷新指令。
与缓冲区有关的函数包括:
1. ob_implicit_flush:作用和implicit_flush一样,是否自动刷新apache的缓冲区。
2. flush:作用是发送指令到apache,让apache刷新自身的输出缓冲区。
3. ob_start:打开输出缓冲区,无论php.ini的文件如何配置,如果使用该函数,即使output_buffering设置成off,也会打开输出缓冲区。ob_start函数还接受一个参数,该参数是一个函数的回调,意思是在输入缓冲区内容之前,需要使用调用传递进来的参数把缓冲区的内容处理一次,再放入缓冲区内。
4. ob_flush:指示php本身刷新自身的缓冲区,把数据发送到apache。
5. ob_clean:清除php缓冲区里面的内容。
6. ob_end_clean:清除php缓冲区内的内容,并关闭输出缓冲区。
7. ob_end_flush:把php自身的缓冲区里的内容发送到apache,并清除自身缓冲区内的内容。
8. ob_get_clean:获取缓冲区的内容之后,清除缓冲区。
9. ob_get_contents:获取输出缓冲区里的内容。
10. ob_get_flush:获取缓冲区里的内容,并将这些内容发送到apache。
11. ob_get_length:获取缓冲区里内容的长度。
12. ob_list_handlers:获取运行ob_start时所回调的函数名称,例如:ob_start('ob_gzhandler');print_r(ob_list_handlers);
以下是重构后的内容:
1. ob_gzhandler函数的作用是在缓冲区刷新之前,对数据进行gzip或deflate压缩。这个函数需要zlib扩展的支持。
2. 使用缓冲区的相关内容:
a. ob_flush和flush的次序关系:ob_flush是和PHP自身相关的,而flush操作的是Apache的缓冲区。因此,在使用这两个函数时,需要先执行ob_flush,再执行flush。如果PHP还没有把数据刷新到Apache,就调用了flush,则Apache无任何数据返回到浏览器。
b. 有的浏览器,如果接受到的字符太少,则不会把数据显示出来。例如老版的IE(必须要大于256k才显示)。为了避免这种情况,可以在输出数据的后面加上多个空格,以填满数据,确保不会影响浏览器显示。
c. 有些Web服务器,如Nginx,自身的输出缓冲区会有一些限制。当自身的输出缓冲区的内容达到4K才会刷新。为了保证内容的数据,可以添加以下代码,保证内容长度:
```php
```
d. 在Apache中,如果你开启了mod_gzip的压缩模块,可能会导致flush函数刷新不成功。原因是mod_gzip有自己的输出缓冲区。当PHP执行了flush函数,指示Apache刷新输出缓冲区,但是内容需要压缩。Apache就把内容输出到自身的mod_gzip模块,mod_gzip也有自身的输出缓冲区,它也不会马上输出,所以造成了内容不能马上输出。为了改善这个情况,可以关闭mod_gzip模块,或者在httpd.conf增加以下内容,以禁止压缩:
```apache
SetEnv no-gzip dont-vary
```