이 기사는 PHP 출력 버퍼에 대한 자세한 소개(코드 예제)를 제공합니다. 이는 특정 참조 값을 가지고 있으므로 도움이 될 수 있습니다.
PHP 출력 버퍼:
버퍼: 실제로는 메모리 주소 공간입니다. 속도가 동기화되지 않은 장치 또는 우선 순위가 다른 장치 간의 데이터 전송을 위한 영역을 저장하는 데 사용됩니다. 버퍼링을 사용하면 프로세스 간의 상호 작용 시간을 줄일 수 있으므로 느린 장치에서 데이터를 읽을 때 빠른 장치의 작업 프로세스가 중단되지 않습니다.
PHP의 출력 스트림에는 일반적으로 개발되는 많은 콘텐츠가 포함되어 있습니다. PHP 출력 텍스트, 이러한 텍스트의 대부분은 echo 또는 printf() 함수를 사용하여 출력됩니다
1> 콘텐츠를 출력하는 모든 함수는 출력 버퍼를 사용합니다
이것은 일반적인 PHP 스크립트를 의미합니다. PHP 확장에 사용된 함수(C 함수)는 출력 버퍼 계층을 거치지 않고 SAPI 버퍼 계층에 직접 출력하고 쓸 수 있습니다(PHP 소스 파일 main/php_output.h에서 이러한 C 함수에 대한 API 문서에 대해 알아볼 수 있습니다).
2>출력 버퍼는 출력 버퍼링에 사용되는 유일한 레이어가 아니며 실제로는 여러 레이어 중 하나일 뿐입니다. 출력 버퍼 레이어의 동작은 사용되는 SAPI(웹 또는 CLI)와 관련이 있습니다. Behaviors
3 & gt; 우리가 일반적으로 알고 있는 SAPI는 출력 바이트가 떠날 때 PHP의 레이어입니다. 컴퓨터 아키텍처에 더 깊이 들어가면 버퍼가 계속해서 나타납니다(터미널 버퍼, fast-cgi 버퍼, 웹 서버 버퍼, 운영 체제 버퍼, TCP/IP 스택 버퍼 등).
PHP CLI의 SAPI는 약간 특별합니다. CLI는 명령줄 인터페이스라고도 합니다. 이는 php.ini 구성의 출력_버퍼 옵션을 0으로 설정하므로 CLI에서 기본 PHP 출력 버퍼가 비활성화됩니다. , 기본적으로 ob_() 클래스 함수를 수동으로 호출하지 않는 한 콘텐츠는 SAPI 레이어로 직접 전달되며 CLI에서는 implicit_flush 값도 1로 설정됩니다.참고: 우리는 종종 implicit_flush의 역할을 혼동하면 PHP의 소스 코드에서 모든 것을 설명했습니다. implicit_flush가 on(값 1)으로 설정되면 출력이 SAPI 버퍼에 기록되면 즉시 플러시됩니다(플러시, 데이터가 다음에 기록됨을 의미). 버퍼는 비워집니다)
또한 즉, 어떤 데이터가 CLI SAPI에 입력되면 CLI SAPI는 즉시 데이터를 다음 레이어, 즉 일반적으로 두 가지 함수인 write()로 전송합니다. 및 fflush()가 이 작업을 담당합니다.기본 PHP 출력 버퍼
: PHP-FPM과 같이 CLI와 다른 SAPI를 사용하는 경우 다음 3가지 버퍼 관련 php.ini 구성 옵션이 있습니다. Used
output_buffering
implicit_flush
우선 런타임에 이러한 옵션의 값을 수정하기 위해 ini_set() 함수를 사용할 수 없습니다. 왜냐하면 이러한 값은 스크립트가 실행되기 전에 PHP 프로그램이 시작될 때 구문 분석되기 때문입니다. 실행하면 ini_set()을 사용하여 런타임에 값을 변경할 수 있지만 적용되지는 않습니다. php.ini 파일을 편집하거나 PHP 프로그램을 실행할 때 -d 옵션을 사용해야만 해당 값을 변경할 수 있습니다. 기본적으로 PHP 배포판은 해당 값이 php.ini에서 4096바이트로 설정됩니다. ON, 기본 출력 버퍼 크기는 16KB입니다. 웹 애플리케이션 환경에서 출력 콘텐츠에 대한 버퍼를 사용하는 것이 성능에 좋습니다
:
기본 4K 설정이 적절한 값이므로 먼저 4096 ASCLL 문자를 쓸 수 있으며, 그런 다음 아래의 SAPI 계층과 통신하며 웹 응용 프로그램 환경에서는 Socket을 통해 메시지를 바이트 단위로 전송하는 방식이 성능에 좋지 않습니다. 모든 것을 서버에 한 번에 전송하거나 최소한 블록 단위로 전송하는 것이 좋습니다. 레이어 간 데이터 교환이 적을수록 출력 버퍼는 항상 사용 가능한 상태로 유지되어야 하며, 요청이 완료된 후 해당 내용이 최종 사용자와 개발자에게 전송됩니다. 아무것도 하지 않아도 돼요
Implicit_flush는 기본적으로 off로 설정되어 있으므로 새 데이터가 기록될 때 SAPI가 새로 고쳐지지 않습니다. FastCGI 프로토콜의 경우 새로 고침 작업은 FastCGI 버퍼가 먼저 전송된 후 FastCGI 배열 패킷을 보내는 것입니다. 데이터 패킷을 보내는 것이 더 좋을 것입니다. SAPI 버퍼를 수동으로 새로 고쳐야 하는 경우, 새로 고침을 작성하려면 implicit_flush를 설정하거나 ob_implicit_flush() 함수를 한 번 호출하면 됩니다. 권장 구성:
output_buffering=4096
implicit_flush = Off/ no
출력 버퍼의 크기를 수정하려면 사용된 값이 4/8의 배수인지, 각각 32/64비트 운영 체제인지 확인하세요
Output_handler는 버퍼의 내용을 수정하기 전에 버퍼의 내용을 수정할 수 있는 콜백 함수입니다. 버퍼가 새로 고쳐집니다
메시지 헤더 및 메시지 본문 : 실제로 메시지 헤더 출력과 관련된 모든 PHP 함수(header(), setcookie(), session_start())는 내부 sapi_header_op를 사용합니다. 함수를 사용하면 이 함수는 메시지 헤더 버퍼에만 내용을 씁니다. printf() 함수를 사용하면 내용이 먼저 출력 버퍼(여러 개일 수 있음)에 기록됩니다. 출력 링 영역의 내용을 전송해야 할 때 PHP는 먼저 메시지 헤더를 보낸 다음 메시지 본문을 보냅니다. . PHP에서 모든 것을 처리하려면 직접 수행하려는 경우 출력 버퍼만 비활성화하면 됩니다.
/*launched via php -d output_buffering=32 -d implicit_flush=1 * */ echo str_repeat('a',31); sleep(3); echo 'b'; sleep(3); echo 'c'; ?>
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";
출력 버퍼 메커니즘: 주로 5.4 이후에 전체 버퍼 계층이 다시 작성되었기 때문입니다. 자체 PECL 확장을 개발할 때 자체 출력 버퍼 콜백 메서드를 선언하여 다른 PECL 확장과 구별할 수 있습니다.
输出缓冲区的陷阱:
有些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_flush
和flush
前,输出一段达到4k的内容,例如:
echo
str_repeat
(‘ ‘, 1024*4);
到此,PHP就可以正常通过ob_flush
和flush
逐行输出需要的内容了。
输出缓冲区实践
1> 通过ob_start()函数手动处理PHP缓冲区机制,这样即便输出内容超过配置参数大小,也不会把数据传输给浏览器,ob_start()将PHP缓冲区空间设置到足够大,只有脚本执行结束后或调用ob_end_flush()函数,才会把数据发送给浏览器
for($i=0;$i<10;$i++){ echo $i.'<br/>'; sleep($i+1); }
执行之后不会每隔几秒就有输出,知道脚本循环结束后,才会一次性输出,这是因为数据量太小,输出缓冲区没有写满
2>当修改output_buffering=0
for($i=0;$i<10;$i++){ echo $i.'<br/>'; flush(); sleep($i+1); }
因为缓冲区的容量设置为0,禁用PHP缓冲区机制,这是我们在浏览器看到断断续续输出,而不必等到脚本执行完毕才看到输出,这是因为数据没有在缓存中停留
3>我们把参数修改为output_buffering=4096,输出数据大于一个缓冲区,不调用ob_start()函数
首先先输出一个4k的内容记下来加本来输出的内容:
for($i=0;$i<10;$i++){ echo echo str_repeat(' ', 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(' ', 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('Y-m-d h:i:s'); $output=ob_get_contents(); ob_end_flush(); echo '<!output>'.$output;
后者是从ob_get_contents取的缓冲区的内容
ob_end_flush()与ob_end_clean(0这两个函数都会关闭输出缓冲,区别是前者只是把PHP缓冲中的数据发生给客户端浏览器,而后者将PHP缓冲区中的数据删掉,但不发送给客户端,前者调用之后数据依然存在,ob_get_contents()依然可以获取PHP缓冲区中的数据拷贝
输出缓冲与静态页面:
大家都知道静态页面的加载速度快,不用请求数据库,以下就是生成静态页面的脚本代码
echo str_pad('',1024);//使缓冲区溢出 ob_start();//打开缓冲区 $content=ob_get_contents();//获取缓冲区内容 $f=fopen('./index.html','w'); fwrite($f,$content);//写入到文件 fclose($f); ob_end_clean()清空并关闭缓冲区
在一些模板引擎和页面文件缓冲中ob_start()函数被使用,如 WP、Drupal、Smarty,例如:
在Wp经常在主题目录header.php看到类似的代码:
只有一个flush(),它的所在的位置就是告诉浏览器那一部分的缓存需要更新,即页面头部以上部分需缓存
以及Wp的部分代码,可以看到,在缓冲区开启时,加入自己的回调方法
内容压缩输出:就是把输出到客户端浏览器的内容进行压缩
好处:降低客户端对服务器出口带宽的占用,提升带宽的利用率。降低Web服务器(如Nginx、Apache、Tomcat等)处理文本时引入的开销,用户端可以减少网络传输延时对用户体验的影响,降低浏览器加载页面内容时占用的内存,有利于改善浏览器稳定性
ob_start('ob_gzhandler');//使用gz格式压缩输出 print'my contents'; 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等前端内容,特别是限制前端框架也越来越来,让它使用户反应速度更快,从而有效提高系统性能
위 내용은 PHP 출력 버퍼에 대한 자세한 소개(코드 예)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!