今回はPHPでeval関数について解説していきます。eval関数は文字列を PHP コードとして実行するための関数です。
eval関数に命令コードを文字列として渡すとそのコード通りに実行してくれます。一面では非常に便利な関数である一方で、使い方によっては非常に危険な機能とも言えます。この記事ではeval関数の便利な面と危険な面について解説していくので、正しい使い方を学んでいきましょう。
eval関数の基本的な使い方
以下のような文字列があったとします。$codeという変数に「echo “eval\n”」という文字列が格納されています。「echo “eval\n”」はPHPでは”eval”という文字列を出力することを意味しますが単なる文字列なのでecho関数が機能し文字列を出力することはありません。
1 |
$code = 'echo "eval\n";'; |
しかし、この文字列を以下のようにeval関数に渡すとどうなるでしょうか。
1 2 |
$code = 'echo "eval\n";'; eval($code); |
出力結果はこちらです。なんと文字列の中の命令コードを理解して実行してくれるのです。
1 |
eval |
以下のように複数行の命令コードも文字列を連結することで実行することができます。
1 2 3 4 5 |
$code = '$arr = array("北海道", "東京都", "大阪府", "福岡県");'; $code .= 'foreach($arr as $v){'; $code .= ' echo $v ."\n";'; $code .= '}'; eval($code); |
実行結果は以下のとおりです。
1 2 3 4 |
北海道 東京都 大阪府 福岡県 |
また、実行結果が返り値となるため結果を変数に入れることも可能です。
以下は実行結果を変数$resultに入れている…つもりで実は誤った使い方をしています。少しわかりにくいですがarrayは確かに配列を返してくれる関数ですが、このコードを実行するだけでは何も起きないのでeval関数内で実行しても何も返ってきません。
1 2 3 |
// 誤った例 $result = eval('array("北海道", "東京都", "大阪府", "福岡県");'); print_r($result); |
配列を$resultに返したい場合は、頭に「return」を入れることで可能になります。
1 2 |
$result = eval('return array("北海道", "東京都", "大阪府", "福岡県");'); print_r($result); |
以下の通り、うまく$resultに配列が返ってその後のprint_r文で出力させることができました。
1 2 3 4 5 6 7 |
Array ( [0] => 北海道 [1] => 東京都 [2] => 大阪府 [3] => 福岡県 ) |
数式を解析して答えを出してみよう
eval関数の効果を知る上で最もわかりやすい例の1つが数式解析ではないでしょうか。
例えば、“11+23-10”という計算式を表した文字列があったとします。答えは24ですが、まずはこれをeval関数を使わずにプログラムを実装してみましょう。
数式解析の方法はいろいろあるかと思いますが、力技で以下のようなプログラムを作ってみました。数式文字列を1文字ずつ評価し数値と演算子を仕分けて配列化した後に順に計算させていく手順となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
$str = "11+23-10"; $cal = array(); $val=""; $i=0; //計算式解析 $str .= "E"; foreach(str_split($str) as $a){ // echo $a . "\n"; if (is_numeric($a)) { $val .= $a; } else { $cal[$i] = $val; if($a=='E') break; $cal[$i+1] = $a; $val=""; $i+=2; } } //計算 $result = $cal[0]; for($i=1; $i<count($cal); $i++){ if($cal[$i]=="+") $result = $result + $cal[$i+1]; if($cal[$i]=="-") $result = $result - $cal[$i+1]; } echo $result ."\n"; |
実行結果はご覧の通り24と出力できました。
1 |
24 |
このプログラムは不完全です。あくまで頭から順に計算しているだけなので、*や/、( )括りなどの優先的に算出しなければならないケースには対応できていません。それらを実装するには更に複雑なプログラムになりますが、ここでは数式解析はなかなかしんどいという点を理解いただければと思います。
このような数式解析ですが、eval関数なら以下のように1行で終わりです。
1 2 3 4 |
$result = eval("return 11+23-10;"); echo $result ."\n"; //出力結果 //24 |
掛け算が含まれていてもちゃんと計算してくれます。
1 2 3 4 |
$result = eval("return 11+23*10;"); echo $result ."\n"; //出力 //241 |
括弧書きが入ってもちゃんと解析して優先的に計算してくれます。
1 2 3 4 |
$result = eval("return (11+23)*10;"); echo $result ."\n"; //出力 //340 |
おわかりいただけたでしょうか。eval関数は使い方によっては非常に大きな効果を発揮ししてくれます。
eval関数の危険性
便利な一方でeval関数の使いどころを誤ると大変な問題を起こします。
例えば、以下のコードを実行すると同じフォルダ内に「eval.txt」が生成されてしまいます。
1 |
eval("touch('eval.txt');"); |
同様に以下のコードを実行すると今度は「eval.txt」が削除できます。
1 |
eval("unlink('eval.txt');"); |
このようにPHPの命令コードを実行してくれるので、やり方によってはとても危険な機能とも言えます。システムファイルを削除することもできますし、マルウェアを勝手にダウンロードすることも可能です。
もちろん自分で使う分には自分さえ気をつければいいのですが、危険なのはWebアプリケーションのような不特定多数のユーザーが使うサービスです。前述のように計算式解析を利用して電卓アプリを作ったとしましょう。テキストボックスに計算式を入力することで答えを出力するようなアプリを公開した場合、悪意のあるユーザーが危険なコードを入力しないとも限りません。
もちろん、受け取る側で危険なコードを検知するような処理をすることでブロックすることは可能ですが、すべてを網羅することは不可能でしょう。悪意のあるユーザーは必ず隙きを突いて突破してくると考えたほうが良いと思います。
不特定多数のユーザーが使うようなアプリ開発ではeval関数は禁止とすべきです。あくまで個人が使うようなスクリプトにのみに留めておいてください。
まとめ
いかがでしょうか。今回はPHPでeval関数について解説しました。
eval関数は文字列を PHP コードとして実行するための便利な関数である一方で、使い方によっては非常に危険な機能ということがおわかりいただけたかと思います。正しい使い方を学んで効率的なプログラム開発を目指してください。
ざっくりPHP入門(初心者〜中級者向け)