Smarty の math 関数

Smarty の math で、剰余を得る % 演算子を使いたいなと思ってマニュアルを調べると、

<blockquote class="tr_bq">+, -, /, *, abs, ceil, cos, exp, floor, log, log10, max, min, pi, pow, rand, round, sin, sqrt, srans and tan are all valid operators. Check the PHP documentation for further information on these math functions.</blockquote>
としか書かれていない。四則演算はいいけど % はダメなのかと思ったが、実際にコードを書くと問題なく動く。

そこで math 関数の実装を覗いてみた。
http://smarty-php.googlecode.com/svn/trunk/distribution/libs/plugins/function.math.php

許される関数は
<blockquote class="tr_bq">    static $_allowed_funcs = array(
        ‘int’ => true, ‘abs’ => true, ‘ceil’ => true, ‘cos’ => true, ‘exp’ => true, ‘floor’ => true,
        ‘log’ => true, ‘log10’ => true, ‘max’ => true, ‘min’ => true, ‘pi’ => true, ‘pow’ => true,
        ‘rand’ => true, ‘round’ => true, ‘sin’ => true, ‘sqrt’ => true, ‘srand’ => true ,’tan’ => true
    );</blockquote>と決まっている。

まず丸カッコの個数が合っていることを確認する。

<blockquote class="tr_bq">    if (substr_count($equation,”(“) != substr_count($equation,”)”)) {
        trigger_error(“math: unbalanced parenthesis”,E_USER_WARNING);
        return;
    }</blockquote>
次に equation で渡された数式を要素に分ける。16進数または文字列(変数名または関数名)を探して $match に入れる。

<blockquote class="tr_bq">    preg_match_all(“!(?:0x[a-fA-F0-9]+)
([a-zA-Z][a-zA-Z0-9_]*)!”,$equation, $match);</blockquote>
$match[1] には文字列の配列が入っている。正規表現の (?: により16進数はキャプチャされない。文字列が引数として渡された変数名でもなく、$_allowed_funcs で定義された関数名でもなければエラー。

<blockquote class="tr_bq">    foreach($match[1] as $curr_var) {
        if ($curr_var && !isset($params[$curr_var]) && !isset($_allowed_funcs[$curr_var])) {
            trigger_error(“math: function call $curr_var not allowed”,E_USER_WARNING);
            return;
        }
    }</blockquote>
引数として渡された変数が空ではなく、さらに数値であることを確認する。問題なければ $equation 中の変数を変数値で置き換える。

<blockquote class="tr_bq">    foreach($params as $key => $val) {
        if ($key != “equation” && $key != “format” && $key != “assign”) {
            // make sure value is not empty
            if (strlen($val)==0) {
                trigger_error(“math: parameter $key is empty”,E_USER_WARNING);
                return;
            }
            if (!is_numeric($val)) {
                trigger_error(“math: parameter $key: is not numeric”,E_USER_WARNING);
                return;
            }
            $equation = preg_replace(“/\b$key\b/”, “ $params[‘$key’] “, $equation);
        }
    }</blockquote>
最後に eval() で評価する。

<blockquote class="tr_bq">    $smarty_math_result = null;
    eval(“$smarty_math_result = “.$equation.”;”);</blockquote>
ということで、演算子はチェック対象外なので、なんでも通る、ということがわかった。
<div>
</div>