日別アーカイブ: 2014年3月19日

標準出力のバッファリングを有効化するPHP関数ob_start

PHPは、WEBブラウザという手近な実行環境を持つプログラミング言語です。そして、WEBブラウザ以外にも実行環境を持つプログラミング言語です。WEB制作の目的でPHPを習得しており、かつPHPが初めてのプログラミング言語である場合などには気付かないかもしれませんが、コマンドライン上でPHPを動かすことも可能なわけです。
したがって、echo命令等が出力する先は、常にWEBブラウザのウィンドウというわけではありません(そもそもWEBブラウザで動いていないPHPがどこのウィンドウに出力するというのでしょう)。echo命令のマニュアルに書いてあるように、出力先はあくまで”標準出力”となっています。

標準出力をバッファリングする

さて、WEBブラウザ上でPHPを実行する場合、この”標準出力”として、WEBブラウザ上のウィンドウが指定してあります。そのおかげあってecho命令はいつもブラウザ上に文字列を吐き出すわけですが、echo命令が吐き出す内容を一時的に変数に入れたい場合や、ファイルに保存したい場合などは、プログラム中でタイミングを指定して、標準出力のバッファリングを行うことが可能です。
標準出力バッファリングのための関数には、output bufferingの略、ob_という接頭辞がついています。プログラム中のバッファリングを開始したいポイントでob_start関数をコールし、バッファリングを停止したいポイントでob_end_clean関数ないしob_end_flush関数を呼びます。

<?php
echo "a";
ob_start();
echo "b";
echo "c";
ob_end_clean();
echo "d";
?>

このプログラムをWEBブラウザで走らせると、ブラウザのウィンドウには”ad”と出力されます。ob_startとob_end_clean関数に挟まれている部分で出力されるべき”bc”は、その間標準出力がジャックされているので表示されません。
一方、終了時の関数をob_end_cleanではなくob_end_flushにした場合、バッファリングの終了時に貯めていた出力内容を標準出力に開放します。そこで、ブラウザの出力は”abcd”となります。

バッファリングした内容を変数に格納する

バッファリングした内容を、そのまま破棄あるいは標準出力に出力してしまうのも面白くありません。ob_get_contentsという関数を使い、変数に格納して取り出してみましょう。

<?php
echo "a";
ob_start();
echo "b";
$buffer = ob_get_contents();
echo "c";
ob_end_clean();
echo "d";
echo $buffer;
?>

このプログラムの出力結果は”adb”となります。ob_get_contents関数が呼ばれた時点でのバッファを変数に格納し、echo “d”;の後に吐き出した結果ですね。

応用:他ファイルでのPHPスクリプト実行結果を読み込む

このようなバッファリングが役に立つケースとして、他ファイルでのPHPスクリプトの、”実行結果”を読み込み、変数に格納しておきたい場合が挙げられます。
たとえばWEBページのコンテンツ部分のHTMLを、PHPスクリプトを使って動的に生成する場合について考えます。仮に、contents.phpというファイルを作成していた場合、呼び出し側の記述は以下のようになります。

<!DOCTYPE html>
<html lang="ja">
<head>
.
.
</head>
<body>
.
.
<div id="contents">
<?php include("contents.php"); ?>
</div>
.
.
</body>
</html>

この例の場合、includeは1回で済んでいますが、contents.phpの生成内容を同じページ内で何度も繰り返す必要がある場合、都度includeするのではなく、変数に格納しておいた方がよいということになります。

<?php
ob_start();
include("contents.php");
$contents = ob_get_contents();
ob_end_clean();
?>
<!DOCTYPE html>
<html lang="ja">
<head>
.
.
</head>
<body>
.
.
<div id="contents1">
<?php echo $contents; ?>
</div>
<div id="contents2">
<?php echo $contents; ?>
</div>
.
.
</body>
</html>

また、ページによってどのファイルを呼び出すか、分岐の可能性がある場合などには、この書き方ではincludeの周辺が分岐ロジックの記述などで膨れ上がってしまい、呼び出し側のHTMLの視認性を損ないます。それを避けるため、コンテンツを呼び出す部分では変数の出力にとどめ、変数に入れる値をPHPのロジック部分にてあらかじめ求めておくといった形にする方がベターです。その場合にも、出力バッファ関数が役に立ちます。

<?php
ob_start();
if("条件式"){
include("contents1.php");
} else {
include("contents2.php");
}
$contents = ob_get_contents();
ob_end_clean();
?>
<!DOCTYPE html>
<html lang="ja">
<head>
.
.
</head>
<body>
.
.
<div id="contents">
<?php echo $contents; ?>
</div>
.
.
</body>
</html>

その他、色々とアクロバティックなコードを書くときに使えたりします。もちろん推奨はしませんが(笑)。