Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

出力バッファと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にする」と覚えておけばヨサゲです。