出力バッファとflush()・ob_flush()
しばらく使わないとすっかり忘れてしまうので、まとめてメモ。
ありがちなネタとして、
- プログレスバー(進捗状況をリアルタイムに表示)
- 「お待ちください」と表示させて裏で処理を実行し、処理が終わったら結果を表示する
などがありますが、これらを行うには出力データのバッファリング/フラッシュを行うことになります。
で、PHP的にはob_*関数やflush関数を使うことになりますが、[PHP-users 18135]プログレスバーは実現可能か?のスレッドから
- flush()が動作する条件は、出力バッファが無いこと
- ob_flush()が動作する条件は、出力バッファのネストレベルが1であること
- 出力バッファのネストレベルはob_get_level()で取得できる
- output_handler、output_buffer(> 0)を指定すると、ネストレベルが変わります
という注意点が挙げられます。
たとえば、[PHP-users 18141]Re: プログレスバーは実現可能か?にあるコードを元にした以下のようなコード
<?php echo 'ob_get_level()=' . ob_get_level() . '<br>'; ob_end_flush(); echo 'ob_get_level()=' . ob_get_level() . '<br>'; ob_start(); echo 'ob_get_level()=' . ob_get_level() . '<br>'; for ( $i = 1; $i <= 100000; $i++ ) { if ($i % 100 == 0) { //DATA_UNIT=100 echo "■"; if (($i % 1000 == 0)) { echo "|"; } if ($i % 5000 == 0) { echo "<br>"; usleep(200000); } ob_flush(); flush(); } } echo 'finish';
の場合、output_handler=none、output_buffer > 0の場合や、mbstring.http_outputが適切に設定されている場合の出力結果は
ob_get_level()=1 ob_get_level()=0 ob_get_level()=1 ...
となり、ネストレベルが1なので、期待通りバッファリング/フラッシュされます。
ただし、mbstring.internal_encoding(mbstring.script_encodingを指定している場合はこれ)とmbstring.http_outputが異なる場合、ネストレベルが一度0になってしまうと文字化けを起こす場合があります。この場合は、
<?php //ob_end_flush(); //ob_start();
とするか
<?php ob_end_flush(); ob_start('mb_output_handler');
とすればOKです。
まあ、いずれにしても「出力バッファのネストレベルを1にする」と覚えておけばヨサゲです。