PHPでプログレス表示を行うには?
[臼井 友章]
プロフィールにも書いてありますが、私は法務だけでなく、システムの開発なども行っています。PHP+MySQL の環境で社内用のシステムを開発することが多いのですが、今回のエントリーはそうした開発の祭に学んだことを書いてみたいと思います。
そうした趣旨ですので、システム開発と関係のない読者の方には、以下の内容は“宇宙語”になってしまうかもしれません。続きを読む方は、その点をご了承ください。
それでは本題です。
システムを開発していると、メールの送信やデータベースへのデータの一括登録など、ブラウザへの表示を伴わないバックグラウンドでの処理を行わせることがあります。そうした際に、画面に何も表示されないとユーザーさんが心配になったり、ブラウザがタイムアウトしてしまい、処理が完了できないことがあります。そうした場合の対応として、プログレス表示を行う方法があります。
処理を開始します。しばらくお待ちください...
10件の処理を完了しました。
20件の処理を完了しました。
30件の処理を完了しました。
40件の処理を完了しました。
50件の処理を完了しました。
処理が完了しました。
といった表示を10件の処理が終わるごとに表示していくというものです。
しかし、こうした表示を行うのは、意外に難しいのです。
<?php
echo "処理を開始します。しばらくお待ちください...<br />\n";for ( $i = 1; $i <= 10; $i++ ) {
sleep( 5 ); // 時間がかかる処理
echo $i * 10 ."件の処理を完了しました<br />\n";
}echo "処理が完了しました<br />\n";
?>
こんな感じで書けば、期待通りの表示がされそうな気がするのですが、実はうまくいきません。上記のスクリプトを実行すると、何も表示されない状態が50秒続いた後、一度にすべてが表示されると思います。
そこで、いろいろ工夫するわけですが、先に解法を示すと、以下のような感じになります。
<?php
echo "処理を開始します。しばらくお待ちください...<br />\n";
echo str_pad(" ",4096)."<br />\n";ob_end_flush();
ob_start('mb_output_handler');for ( $i = 1; $i <= 10; $i++ ) {
sleep( 5 ); // 時間がかかる処理
echo $i * 10 ."件の処理を完了しました<br />\n";ob_flush();
flush();
}
echo "処理が完了しました<br />\n";
?>
書いてしまうと簡単なのですが、少し説明すると、
echo str_pad(" ",4096)."<br />\n";
は、
処理を開始します。しばらくお待ちください...
を処理が行われる前に表示するために必要です。
多くのブラウザは、ある程度の文字列をバッファしてから表示を開始するため、ob_flush() や flush() によって、出力バッファをフラッシュしても、ブラウザのバッファに蓄積されてしまい、表示されません。そこで、空白文字列をあらかじめ送っておくことで、この問題を回避しています。
次に、
ob_end_flush();
ob_start('mb_output_handler');
は、バッファリング・フラッシュを行う前準備として必要な記述です。Do You PHP はてなさんにも書かれているように、バッファリング・フラッシュを行う際の注意点として、
・flush()が動作する条件は、出力バッファが無いこと
・ob_flush()が動作する条件は、出力バッファのネストレベルが1であること
・出力バッファのネストレベルはob_get_level()で取得できる
というものがあり、また、文字化けを防ぐためには、単に、ob_start() とするのではなく、ob_start('mb_output_handler') とする必要があります。
最後に、
ob_flush();
flush();
ですが、マニュアルの flush のページにもあるように、
flush() は、Webサーバまたはクライアント側の ブラウザのバッファリングの手法に影響を与えません。 そのため、出力バッファをフラッシュするには ob_flush() および flush() の両方をコールする必要があります。
とのことなので、両方をコールする必要があるわけです。
以上が説明となります。上記のコードについては、当社のサーバに設置した上で、Windows 2000 上の Internet Explorer 6 と Mozilla Firefox 2.0.0.6 で動作確認をしていますが、サーバに mod_gzip が導入されていたり、クライアントにセキュリティソフトが導入されている場合には、それらによってもバッファリングが行われるため、期待通りの動作をしない可能性があります。その点はご了解いただきたいと思います。
コメント
はじめまして。PHPプログラムの進み具合を表示したくて調べていました。
とても分かりやすく参考になりました。ありがとうございました。
投稿者: yamazon | 2010年02月20日 22時08分