3

ユーザー入力が提供する計算を実行する必要があるシステムがあります。

これらの計算の 1 つを実行するために私が見つけた最も簡単な方法は、eval です。次のパーサーを見つけようとしています。

(3 + 6 ) / 2 + 27 * 5 / 2

ただ難しそうです。誰かがこれに対する解決策を持っているなら、私はそれを聞いてうれしいです.

あなたが EVAL を使用していると仮定すると (私はそれが恐ろしい機能であることを知っています)、彼らがそのボックスに好きなように入力できるようにすることは大きな不安です。

したがって、数字、標準演算子(+ - / *)、および括弧以外のすべてを削除する正規表現を行った場合、次のような質問をします。

$equation = preg_replace( '/[^0-9+-\/*()]/', '', $input_equation );
$result = eval( $equation );

システムに害が及ぶ可能性はありますか?

4

2 に答える 2

4

最近、BCMath関数を使用するPDMAS 準拠のインタープリターをコーディングしました。

function BC($string, $precision = 32)
{
    if (extension_loaded('bcmath') === true)
    {
        if (is_array($string) === true)
        {
            if ((count($string = array_slice($string, 1)) == 3) && (bcscale($precision) === true))
            {
                $callback = array('^' => 'pow', '*' => 'mul', '/' => 'div', '%' => 'mod', '+' => 'add', '-' => 'sub');

                if (array_key_exists($operator = current(array_splice($string, 1, 1)), $callback) === true)
                {
                    $x = 1;
                    $result = @call_user_func_array('bc' . $callback[$operator], $string);

                    if ((strcmp('^', $operator) === 0) && (($i = fmod(array_pop($string), 1)) > 0))
                    {
                        $y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string = array_shift($string), $x, $i = pow($i, -1)));

                        do
                        {
                            $x = $y;
                            $y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string, $x, $i));
                        }

                        while (BC(sprintf('%s > %s', $x, $y)));
                    }

                    if (strpos($result = bcmul($x, $result), '.') !== false)
                    {
                        $result = rtrim(rtrim($result, '0'), '.');

                        if (preg_match(sprintf('~[.][9]{%u}$~', $precision), $result) > 0)
                        {
                            $result = (strncmp('-', $result, 1) === 0) ? bcsub($result, 1, 0) : bcadd($result, 1, 0);
                        }

                        else if (preg_match(sprintf('~[.][0]{%u}[1]$~', $precision - 1), $result) > 0)
                        {
                            $result = bcmul($result, 1, 0);
                        }
                    }

                    return $result;
                }

                return intval(version_compare(call_user_func_array('bccomp', $string), 0, $operator));
            }

            $string = array_shift($string);
        }

        $string = str_replace(' ', '', str_ireplace('e', ' * 10 ^ ', $string));

        while (preg_match('~[(]([^()]++)[)]~', $string) > 0)
        {
            $string = preg_replace_callback('~[(]([^()]++)[)]~', __METHOD__, $string);
        }

        foreach (array('\^', '[\*/%]', '[\+-]', '[<>]=?|={1,2}') as $operator)
        {
            while (preg_match(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), $string) > 0)
            {
                $string = preg_replace_callback(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), __METHOD__, $string, 1);
            }
        }
    }

    return (preg_match('~^[+-]?[0-9]++(?:[.][0-9]++)?$~', $string) > 0) ? $string : false;
}

次の演算子をサポートしています。

  • ^(捕虜)
  • *
  • /
  • %(係数)
  • +
  • -
  • =, ==, <, <=, >, >=(比較)

そして、あなたはそれを次のように呼びます:

echo BC('(3 + 6 ) / 2 + 27 * 5 / 2');

私はこれを行ったので、任意の長さの計算を実行する簡単な方法がありましたが、あなたの場合は、すべての空白を取り除き、次の正規表現を使用して文字を検証することもできます:

if (preg_match('~^(?:[0-9()*/%+-<>=]+)$~', $expression) > 0) {
    // safe to eval()
}
于 2012-06-01T03:56:40.053 に答える
0

これは私には簡単に思えましたが、ネイティブの PHP ソリューションを探していませんでした。念のため投稿しておきます。

を使用する代わりにeval、最初に使用することを考えたのはexec、 を呼び出しbcて、bcすべての作業を行ってもらうことです (Linux マシンを使用していると仮定します)。

したがって、次のようにします (非常に未テスト):

$user_input = '(3 + 6 ) / 2 + 27 * 5 / 2';
$return = exec( 'echo "scale=1; ' . escapeshellarg( $user_input) . '" | bc', $output, $retval);

$calculation = $output[0];
if( !is_numeric( $calculation)) {
    echo "Invalid input!";
}
echo $calculation; // Outputs 72
于 2012-06-01T03:37:10.003 に答える