PHPで一秒ごとにカウントを出力する【進捗表示】

この記事について

環境PHP、Apache
目的処理の進行状況を一秒ごとにブラウザへ出力する
結果ob_end_flush()でバッファリングをオフにし、ループ内でflush()する

つまずきポイント要約

  • PHPは出力バッファリングが有効なことが多く、そのままでは毎秒出力されない
  • ブラウザ側もバッファするので、str_pad(”,4096)で空文字を先に送る
  • それでもダメならmod_gzipをOffにしてみる

PHPで進行状況をその都度出力したかったので、テストコードを書いた。

毎秒カウントアップ表示するサンプルコード

<?php
 
for ($i = 0; $i < 10; $i++) {
    echo $i;
    sleep(1);
}
 
?>

これで1秒ごとにカウントして経過時間が表示されるはず。
そう思ったがうまくいかない。

調べてみると、PHPは出力のバッファリングがオンになっていることが多く、先ほどのようなスクリプトを実行しても毎秒ごとに出力してくれないらしい。

その都度出力は行われず、 代わりに内部バッファに保存される。そして後でまとめて表示される。ループのたびに出力するには「ob_end_flush()」でバッファリングをオフにすればいい。

先ほどのコードを修正

<?php

echo str_pad('',4096)."\n"; //←空文字を送る

ob_end_flush(); //バッファリングをオフ

for ($i = 0; $i < 10; $i++) {
    echo $i;
    flush(); // ← フラッシュする
    sleep(1);
}
 
?>

テスト表示のとき意外と重要なのが「str_pad(”,4096)」の部分。
多くのブラウザは、ある程度の文字列をバッファしてから表示を開始するため、ob_flush() や flush() によって、出力バッファをフラッシュしても、ブラウザのバッファに蓄積されてしまい、表示されない。
そこで、空白文字列をあらかじめ送ることで、この問題を回避している。

これでもうまくいかないときは、PHP以外の何かがバッファリングを行っている疑いがある。

打開策のひとつとして、.htaccessに次の記述をする

mod_gzip_on Off

レンタルサーバ等の設定によっては、HTTPの応答がmod_gzipにより圧縮されることがあるのでOFFにする。

タイトルとURLをコピーしました