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

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

このところ、暑かったり寒かったりですね。私はすっかり風邪気味です^^;
みなさん体には気を付けてくださいませ。

さて、今回も始めましょう。今プロPHP第8回です。
今回はテンプレートエンジンのセキュリティを強化してみます。


テンプレートファイルにPHPコードが入ったらエラーにする

一般的にテンプレートファイルは制作者側が作るものなので、上記タイトルのような処理はあまり必要ではないかもしれませんが、勉強の為やってみましょう。

禁止する関数の一覧を作成する

$funcs = get_defined_functions();
$funcsArray = array_merge($funcs['internal'],$funcs['user']);
unset ($funcs);
$safePhpCode = array('isset', 'count', 'is_array', 'is_file', 'is_dir', 'time', 'empty');
foreach ($safePhpCode as $safeCode) {
	unset($funcsArray[array_search($safeCode, $funcsArray)]);
}

上記のコードは定義済関数を取得する処理を書いています。
では順を追ってみてみましょう

  • 1行目
    get_defined_function()関数は全ての定義済み関数を配列で出力します。
  • 2行目
    array_merge()関数は複数の配列をマージ(結合)します。
    get_defined_function関数で出力される結果はinternal(PHPで定義されている関数)とuser(ユーザー定義関数)の2種類あるので、この処理で結合しています。
  • 3行目
    unset()関数は変数を破棄する関数です。
    同じ変数名を再利用する場合や、メモリ軽減の為に使ったりします。
    ※unsetを使用しなくても、PHPは全ての処理が終わった後はメモリを開放します。
  • 4行目
    $safePhpCode変数に使用を許可する関数名を配列で代入しています。
  • 5行目
    foreach文で$safePhpCode変数の値を1つづつ取り出します
  • 6行目
    unset()関数を使って$funcsArray変数から使用を許可する関数名を取り除きます。
    array_search()関数は指定した値(この場合$safePhpCodeの値)を配列から検索して見つけた場合はキーを返します。
    array_search関数で見つけたキーを使って使用する関数名を取り除いています。

これで禁止したい関数の一覧が作れました。
次はテンプレートファイル内にPHPコードが書かれているかどうかを判別する処理を考えてみましょう。

PHPコードの判別

テンプレートファイルの中にPHPコードが入る条件としてまず考えられるのが、PHPの開始タグが入っていることです。
なので、「PHPの開始タグがあればエラー」となるような処理を作ってみます。

$file = "template.html";
$data = file_get_contents($file);
if (preg_match("/<\?php/", $data)) {
	$error = "can't use php code.";
	exit($error);
}

上記の処理はほとんど習った関数ばかりですが、preg_match()関数はまだ習っていなかったかな。
preg_match()関数は正規表現を使用して、変数内に指定した文字列が存在するかを調べる関数です。
返り値は0(マッチせず)または1(1回マッチした)となります。
マッチした値を取得したい場合は第3引数を指定します。(詳しくはリンク先を参照してください)

テンプレートエンジン用関数内でのPHPコード

PHPコードはif文の評価式の中にも入れることができるので、その場合の処理も作ってみましょう。

$funcs = get_defined_functions();
$funcsArray = array_merge($funcs['internal'],$funcs['user']);
unset ($funcs);
$safePhpCode = array('isset', 'count', 'is_array', 'is_file', 'is_dir', 'time', 'empty');
foreach ($safePhpCode as $safeCode) {
	unset($funcsArray[array_search($safeCode, $funcsArray)]);
}
//-----
$file = "template.html";
$data = file_get_contents($file);
preg_match_all("/\{if[ ]+?(.+?)\}/", $data, $matches);
if (count($matches[1])) {
	foreach ($matches[1] as $code) {
		foreach ($funcsArray as $func) {
			if(strpos($code,$func) !== false) {
				$error =  "can't use php function '".$func."()' inside 'if tag'.";
				exit($error);
			}
		}
	}
}

上記処理の1行目~7行目は最初に作成した定義済み関数を取得する処理です。
では8行目以降を見てみましょう。

  • 8行目
    コメント行
  • 9行目~10行目
    file_get_contentsでテンプレートファイルを取得
  • 11行目
    preg_match_all()関数を使用してテンプレートファイル内の全てのif文を取得します。
    preg_match関数が1度しか処理をしないのに比べ、preg_match_all関数は繰返し処理をするのに注意しましょう。
  • 12行目
    count()関数は変数が配列の場合、配列数を返します。
    ここではでマッチしたかどうかを調べ、マッチした場合13行目に処理が続きます
  • 13行目
    マッチしたif文の評価式を1つづつ処理します。
  • 14行目
    定義済み関数をforeach文で1つづつ処理します。
  • 15行目
    strpos()関数は指定した文字列が最初にあらわれる場所を見つけます。
    評価式の「!==」は開始位置が0の場合とFalseの場合を区別するための物です。(「!=」とすると開始位置0に指定文字列があった場合に正しく処理が行えません)
  • 16行目~17行目
    評価式の中に禁止されているPHP関数があった場合、エラーを出力します。

これでPHPコードのチェック処理ができました。
次回は今回作ったコードをテンプレートエンジンに組み込んでいきますよ。

それでは、また来週~~ノシ

コメントをどうぞ!