この記事では、PHP の出力バッファについて詳しく紹介 (コード例) しています。一定の参考値があります。必要な友人は参照してください。お役に立てば幸いです。
PHP 出力バッファ:
Buffer: は実際にはメモリ アドレス空間です。速度が同期していないデバイス間や優先度の異なるデバイス間でデータを送信するための領域を格納するために使用されます。バッファリングにより、プロセス間の対話の待ち時間が短縮され、低速デバイスからデータが読み取られるときに、高速デバイスの操作プロセスが中断されなくなります。
PHP の出力ストリームには大量のコンテンツが含まれています。通常、開発者が PHP に出力したいテキストです。これらのテキストのほとんどは、echo または printf() 関数を使用して出力されます
1>コンテンツを出力する関数は出力バッファを使用します
これは通常の PHP スクリプトを指します。PHP 拡張機能を開発している場合、使用される関数 (C 関数) は、出力バッファ層を経由せずに直接出力し、SAPI バッファ層に書き込むことができます (PHP ソースに記述することができます) file main /php_output.h これらの C 関数については、API ドキュメントを参照してください)
2>出力バッファは、出力をバッファするために使用される唯一の層ではなく、実際には多くの層のうちの 1 つにすぎません。バッファレイヤーはSAPI(WebまたはCLI)に関連する使用されているものとは異なり、SAPIが異なる場合があります。
PHP CLI の SAPI は少し特殊です。CLI はコマンド ライン インターフェイスとも呼ばれます。これは、php.ini 設定の Output_buffer オプションを 0 に強制します。これは、デフォルトの PHP 出力バッファが無効になることを意味します。 CLI、デフォルトでは、手動で ob_() クラス関数を呼び出さない限り、出力されるコンテンツは SAPI 層に直接渡され、CLI では implicit_flush の値も 1, に設定されます。注: PHP の implicit_flush の役割はよく混同されます。ソース コードがすべてを物語っています。implicit_flush が on (値 1) に設定されている場合、出力は SAPI バッファに書き込まれるとすぐにフラッシュされます (flush、つまりデータを下位層に書き込むと、バッファがクリアになります)
つまり、データが CLI SAPI に送信されると、CLI SAPI はデータをすぐに次の層 (通常は標準出力) に転送します。 Pipe、write()、fflush() 関数はこれを実行する役割を果たしますデフォルトの PHP 出力バッファ
: PHP-FPM など、CLI とは異なる SAPI を使用する場合、次の 3 つのバッファ関連のものが使用されます php.ini 設定オプション Output_buffering
implicit_flush
Output_handler
まず第一に、ini_set() 関数を使用して値を変更することはできません。これらの値は、PHP プログラムの開始時に変更されるため、実行時にこれらのオプションが変更されます。スクリプトを実行する前に解析されるため、ini_set() を使用して実行時に値を変更できますが、有効になりません。 。それらの値を変更できるのは、php.ini ファイルを編集するか、PHP プログラムの実行時に -d オプションを使用することによってのみです。
Web アプリケーション環境で出力コンテンツにバッファを使用すると、パフォーマンスが向上します。:
デフォルトの 4K 設定は、適切な値です。まず 4096 個の ASCLL 文字を記述し、次にその下の SAPI 層と通信し、Web アプリケーション環境のソケットを介してメッセージをバイト単位で送信できます。この方法はパフォーマンスに良くありません。より良い方法は、すべてのコンテンツを転送することです。一度にサーバーに送信するか、少なくとも部分的に転送します レイヤー間のデータ交換の数が少ないほど、パフォーマンスは向上しますが、これは常に維持される必要があります 出力バッファーが利用可能であり、PHP がその内容の送信を担当しますリクエストの完了後にエンド ユーザーに送信されます。開発者は何もする必要はありません
Implicit_flush はデフォルトでオフに設定されているため、新しいデータの書き込みによって SAPI はリフレッシュされません。FastCGI プロトコルの場合、リフレッシュ操作は各書き込みの後に FastCGI 配列パケットを送信することです。FastCGI 配列パケットが送信前に送信される場合は、データパケット バッファがいっぱいの方が良いでしょう。 SAPI バッファを手動でリフレッシュする必要がある場合は、flush() 関数を使用します。リフレッシュを書き込む場合は、implicit_flush を設定するか、ob_implicit_flush() 関数を 1 回呼び出します。
推奨構成:
Output_buffering=4096
implicit_flush = Off/no
出力バッファのサイズを変更するには、必ず 4/8 の倍数 (それぞれ 32/64 ビット オペレーティング システム) の値を使用してください
Output_handler は、バッファを更新する前にバッファ内の内容を変更するで使用できるコールバック関数です。
バッファ内の内容は、コールバック関数に渡されます。コンテンツ変換を実行するための選択肢 (1 つだけ使用可能) なので、PHP が Web サーバーとユーザーに提供するコンテンツを取得したい場合は、出力バッファー コールバックを使用できます。出力は、メッセージ ヘッダー、メッセージ ヘッダーを参照します。 。 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 バイトが書き込まれます。 byte. , このバイトはバッファを埋めます, それはすぐにそれ自体をリフレッシュし, 内部のデータを SAPI 層のバッファに渡します. implicit_flush が 1 に設定されているため, SAPI 層のバッファもすぐに次の層に更新されます, aa...b を出力し、sleep し、バイトを出力します。この時点で、バッファには 31 の空のバイトがありますが、スクリプトが実行されるため、このバイトを含むバッファはすぐに更新され、次の日に出力されます。 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 に格納されますが、1 文字が欠けています。2 番目の echo ステートメントが 'o' を出力すると、バッファ 2 はいっぱいになります, それでフラッシュします。 ob_start() のコールバック関数はリフレッシュの前に呼び出されます。この関数はバッファの最初の文字を大文字に変換するため、出力は 'Foo' となり、バッファ 1 に保存され、3 番目の出力は 'barbazz' になります。この文字列は引き続きバッファ 2 に最初に配置されます。この文字列は 7 バイトです。バッファ 2 がオーバーフローしたため、すぐに更新されます。コールバック関数を呼び出して取得した構造体は 'Barbazz' で、バッファ 1 に渡されます。 「FooBarbazz」の文字はバッファ 1 に保存されます。バッファ 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_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 中国語 Web サイトの他の関連記事を参照してください。