開発畑トップ   »  PHP   »  今日からプログラマー!PHP入門【テンプレートエンジン作成編】   »  今日からプログラマー!PHP入門(第11回)

今日からプログラマー!PHP入門(第11回)

今日もがんばるぞー!

おはようございます、今プロPHP第11回はじめますー。


今回は、前回の続きです。

前回の最後にちょこっと問題的なことを書きましたが、解りましたでしょうか。
今回はその辺りを掘り下げてみようと思います。

include文での処理を確認

では、前回のソースコードを見てみましょう。

	if ($evalMode) {
	ob_start();
	try {
		$return = eval('?>'.$text);
	}catch(Exception $ex) {

	}
	$contents = ob_get_contents();
	if ($return === false && strstr($contents, '<b>Parse error</b>:')) {
		exit($contents);
		}
		ob_end_clean();
	}else{
		$filename = tempnam('/tmp/','bx');
		file_put_contents($filename,$text);
		include $filename;
		unlink($filename);
	}
・
・

上記コードは前回のコードの一部抜粋です。(前回のコード130行目~の部分
このコードの13行目からの処理がinclude文を使用した処理になります。

前回問題として書いたのは、このinclude文を使用した場合にはinclude文の行を実行した時点で結果を出力してしまうということでした。
なぜなら、eval側の処理のようにob_start関数を使ってキャッシュをしていないからです。
キャッシュをしていないので、includeが実行されればそのまま出力してしまいます。
というのが答えでした。

では、include文の方でもデータのキャッシュを取りましょう。

}else{
	ob_start();
	$filename = tempnam('/tmp/','bx');
	file_put_contents($filename,$text);
	include $filename;
	$contents = ob_get_contents();
	ob_end_clean();
	unlink($filename);
}
・
・

1行目と7行目を追加しました。
処理内容はinclude文と同じです。

これで、前回の問題は解決します。
しかし、まだ問題が残っているのです。それはエラー時の処理です。
evalの方はパースエラーが発生してもプログラムが止まることはないので、エラー処理を行って、必要であればそのまま処理を継続することができます。
ところがincludeの方は処理が止まってしまいます。今プロPHP第7回参照

というわけで、includeの場合、エラーで止まってしまうのは仕方が無いと考えて、「エラーが出る前にエラーを検知して処理を継続」させるプログラムを考えてみましょう。

テンプレートファイルのチェック

テンプレートファイルをincludeしてパースエラーが出るということは、タグの閉じ忘れということになります。
それなら、テンプレートファイルをincludeする前に、テンプレートファイルの中身をチェックしてタグの閉じ忘れが無いか調べればいいということです。

では、具体的にどのようなことをすればいいのかをリストにしてみましょう。

  • ダブルクォート(”)やシングルクォート(’)の閉じ忘れをチェック
  • 括弧({、})の閉じ忘れをチェック

この2つだけで大丈夫でしょう。
では、プログラムを作ってみます。

$quote = 0;
$braces = 0;
foreach (token_get_all($code) as $token) {
	switch ($token) {
		case "'":
		case '"':
			$quote++;
			break;
		case '{':
			$braces++;
			break;
		case '}':
			$braces--;
			if ($braces < 0) break 2;
			break;
	}
}
if ($quote % 2 || $braces != 0) {
	$error = true;
}else{
	$error = false;
}

このコードではクォートの数と括弧の数を数えて、クォートの数が偶数(かならず対で使用するため)かどうか、括弧はちゃんと閉じられているかを調べています。
では順を追ってみていきましょう。

  • 1行目、2行目
    変数の初期化を行っています。
    ここで使用する変数はクォートの数と括弧の数をカウントするのに使います。
  • 3行目
    token_get_all()関数を使用して、$code(ここにテンプレートファイルのデータが入っていると仮定して)をPHPトークンに分割します。
    分割することで、配列として括弧やクォートを収納できるので、その配列をforeach文でループさせて数を数えます。
  • 4行目
    switch文を使って分割したPHPトークンをチェックしていきます。
  • 5行目~8行目
    シングルクォートやダブルクォートがある場合、$quote変数の値を増やします。
  • 9行目~11行目
    括弧がある場合、$braces変数の値を増やします。
  • 12行目~15行目
    閉じ括弧がある場合、$braces変数の値を減らします。
    これにより、値が0になれば括弧は対で使用されているということになります。
    また、$brases変数の値がマイナスの場合は括弧の数がおかしい(閉じ括弧が多い)ので、break 2でforeachの処理を抜けます。
    break 2についてはリンク先breakを参照してください。
  • 19行目~23行目
    if文で以下の場合$error変数にtrueを代入します。
    ・$quote変数を2で割った余りが1の場合
    ・$braces変数の値が0以外の場合

以上がパースエラーチェックの処理となります。

次回はこの処理をテンプレートエンジンに組み込んでいきます。
この処理自体はeval側の処理でも使えるので、共有の処理として使用してもいいですね。

コメントをどうぞ!