> 백엔드 개발 > PHP 튜토리얼 > PHP 출력 버퍼에 대한 자세한 소개(코드 예)

PHP 출력 버퍼에 대한 자세한 소개(코드 예)

不言
풀어 주다: 2023-04-05 12:56:01
앞으로
2718명이 탐색했습니다.

이 기사는 PHP 출력 버퍼에 대한 자세한 소개(코드 예제)를 제공합니다. 이는 특정 참조 값을 가지고 있으므로 도움이 될 수 있습니다.

PHP 출력 버퍼:
버퍼: 실제로는 메모리 주소 공간입니다. 속도가 동기화되지 않은 장치 또는 우선 순위가 다른 장치 간의 데이터 전송을 위한 영역을 저장하는 데 사용됩니다. 버퍼링을 사용하면 프로세스 간의 상호 작용 시간을 줄일 수 있으므로 느린 장치에서 데이터를 읽을 때 빠른 장치의 작업 프로세스가 중단되지 않습니다.

PHP의 출력 스트림에는 일반적으로 개발되는 많은 콘텐츠가 포함되어 있습니다. PHP 출력 텍스트, 이러한 텍스트의 대부분은 echo 또는 printf() 함수를 사용하여 출력됩니다

1> 콘텐츠를 출력하는 모든 함수는 출력 버퍼를 사용합니다

이것은 일반적인 PHP 스크립트를 의미합니다. PHP 확장에 사용된 함수(C 함수)는 출력 버퍼 계층을 거치지 않고 SAPI 버퍼 계층에 직접 출력하고 쓸 수 있습니다(PHP 소스 파일 main/php_output.h에서 이러한 C 함수에 대한 API 문서에 대해 알아볼 수 있습니다).

2>출력 버퍼는 출력 버퍼링에 사용되는 유일한 레이어가 아니며 실제로는 여러 레이어 중 하나일 뿐입니다. 출력 버퍼 레이어의 동작은 사용되는 SAPI(웹 또는 CLI)와 관련이 있습니다. Behaviors

图 Caps Logic Relationship의 상단 "출력 버퍼 영역"의 맨 아래 두 개

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

output_handler

우선 런타임에 이러한 옵션의 값을 수정하기 위해 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에서 웹 서버로 콘텐츠를 가져오고 사용자는 출력 버퍼 콜백을 사용할 수 있습니다. 출력은 메시지 헤더, 메시지 헤더를 참조합니다. HTTP 헤더는 출력 버퍼 계층의 일부이기도 합니다.


메시지 헤더 및 메시지 본문 : 실제로 메시지 헤더 출력과 관련된 모든 PHP 함수(header(), setcookie(), session_start())는 내부 sapi_header_op를 사용합니다. 함수를 사용하면 이 함수는 메시지 헤더 버퍼에만 내용을 씁니다. printf() 함수를 사용하면 내용이 먼저 출력 버퍼(여러 개일 수 있음)에 기록됩니다. 출력 링 영역의 내용을 전송해야 할 때 PHP는 먼저 메시지 헤더를 보낸 다음 메시지 본문을 보냅니다. . PHP에서 모든 것을 처리하려면 직접 수행하려는 경우 출력 버퍼만 비활성화하면 됩니다.

사용자 출력 버퍼:

기본 PHP 출력 버퍼 레이어를 사용하려는 경우 CLI를 사용할 수 없습니다. 이 레이어에서는 비활성화되었습니다

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

기본 출력 버퍼 크기는 32바이트로 설정되어 있습니다. 프로그램이 실행되면 먼저 31바이트를 쓴 다음 절전 모드를 거쳐 1바이트를 씁니다. 자체를 새로 고치고 내부 데이터를 SAPI 계층의 버퍼에 전달합니다. implicit_flush가 1로 설정되어 있으므로 SAPI 계층의 버퍼도 즉시 다음 계층으로 새로 고쳐지므로 aa...b를 출력한 다음 절전 모드로 전환됩니다. 그런 다음 바이트를 출력합니다. 이때 버퍼에는 31개의 null 바이트가 있지만 스크립트가 실행되므로 이 바이트가 포함된 버퍼가 즉시 새로 고쳐지고 c

가 화면에 출력됩니다. ob_start() 생성, 이러한 버퍼를 여러 개 생성할 수 있습니다(메모리가 소진될 때까지). 이러한 버퍼는 스택 구조를 형성하며, 각각의 새 버퍼는 이전 버퍼에 쌓이고, 채워지거나 오버플로될 때마다 새로 고침 작업을 수행합니다. , 그리고 데이터를 다음 버퍼로 전달합니다

  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";
로그인 후 복사

스택의 선입선출 원칙에 따라 모든 출력은 먼저 버퍼 2에 저장됩니다. 버퍼 2의 크기는 3바이트이므로 첫 번째 echo 문이 'fo' 문자열을 출력하면 먼저 버퍼 2에 저장되고 두 번째 echo 문이 'o'를 출력하면 버퍼 2가 가득 찼습니다. , 그래서 플러시됩니다. 새로고침하기 전에 ob_start() 의 콜백 함수가 호출됩니다. 이 함수는 버퍼의 첫 글자를 대문자로 변환하므로 출력은 'Foo'가 되며, 이후 버퍼 1에 저장되고 세 번째 출력은 'barbazz' 가 됩니다. 여전히 버퍼 2에 먼저 배치됩니다. 이 문자열은 7바이트입니다. 버퍼 2가 오버플로되었으므로 즉시 새로 고쳐집니다. 콜백 함수를 호출하여 얻은 구조는 'Barbazz'이며, 이후 버퍼 1에 전달됩니다. 'FooBarbazz'의 문자는 버퍼 1에 저장됩니다. 마찬가지로, ob_start()의 콜백 함수가 먼저 호출됩니다. 버퍼 1의 콜백 함수는 모델을 문자열 서명에 추가합니다. is '0-FooBarbazz',

마지막 echo 문은 3자보다 큰 문자열 'hello'를 출력하므로 버퍼 2가 이때 스크립트가 실행되므로 버퍼가 즉시 새로 고쳐집니다. 1, '1-Hello'를 얻습니다.

따라서 echo 함수를 사용하는 것은 이렇게 간단한 일이지만, 버퍼와 성능을 고려하면 복잡하기도 하므로, echo를 사용하는 출력 내용의 크기에 주의하세요. 버퍼 구성이 출력 콘텐츠보다 작고 출력 콘텐츠를 애플리케이션에서 분할해야 하는 경우 성능이 더 좋아집니다.

출력 버퍼 메커니즘: 주로 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_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等前端内容,特别是限制前端框架也越来越来,让它使用户反应速度更快,从而有效提高系统性能

위 내용은 PHP 출력 버퍼에 대한 자세한 소개(코드 예)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
php
원천:csdn.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿