Решение математической задачи / выражения, которое является строкой, в PHP - PullRequest
5 голосов
/ 15 мая 2010

Пользователь может ввести математическую задачу (выражение), например 5 + 654, 6 ^ 24, 2!, sqrt(543), log(54), sin 5, sin(50). После некоторого переформатирования (например, изменения sin 5 в sin(5)) и выполнения eval PHP дает мне правильный результат:

$problem = "5 + 5324";
eval("$result = " . $problem);
echo $problem . " = " . $result;

Однако это довольно небезопасно:

/* If you read this, please, plz don't be stupid and DO NOT EXECUTE this code!!!!! */
$problem = "shell_exec('rm -rf /')";
eval("$result = " . $problem); /* Nukes system */
echo $problem . " = " . $result;

Может ли кто-нибудь указать мне правильное направление, разбирая и решая математический вопрос, подобный приведенным выше, который безопасен? Спасибо.


Кстати, не является ли eval обычным опечаткой evil?

Ответы [ 3 ]

1 голос
/ 15 мая 2010

Взгляните на механизм вычислений в PHPExcel ... он реализует безопасный синтаксический анализатор формул, который может обрабатывать большинство формульных выражений (включая такие функции, как LOG () и 2 ^ 3 как степень, а не бинарный оператор), которые может быть рассчитан в самом Excel.

1 голос
/ 15 мая 2010

В идеале, я думаю, вам нужно было бы создать какой-нибудь движок грамматического синтаксического анализатора / лексера, который мог бы разобрать формулу на ее части и затем выполнить уравнение для этого.

Таким образом, любые мошеннические функции будут просто игнорироваться, и система может вернуть ошибку.

0 голосов
/ 15 мая 2010

Что ж, в этом случае вам, скорее всего, потребуется реализовать собственный калькулятор - я получил его на собеседовании, так что вот мой код. Помните, что это действительно унаследованные вещи для меня, но я подумал, что это может дать вам несколько идей:

<code><?php
if(isset($_POST['inp'])) {
    $time_start = microtime(true);

    $inp = preg_replace(array('/\s+/', '/Pi/', '/e/', '/T/', '/G/', '/M/', '/k/', '/m/', '/u/', '/n/', '/p/', '/f/'), 
                        array('', M_PI, exp(1), '*'. 1e12, '*'. 1e9, '*'. 1e6, '*'. 1e3, '*'. 1e-3, '*'. 1e-6, '*'. 1e-9, '*'. 1e-12, '*'. 1e-15),
                         $_POST['inp']);


    function rectify($exp, $mod = "+") {

        $res = recCalc($exp);
        debug("Pre rectify", $res);
        if($mod == '-') {
            $res *= -1;
        }
        debug("Post rectify", $res);
        return $res;
    }


    function do_error($str) {
        die($str);
        return false;
    }


    function recCalc($inp) {
        debug("RecCalc input", $inp);   

        $p = str_split($inp);
        $level = 0;

        foreach($p as $num) {
            if($num == '(' && ++$level == 1) {
                $num = 'BABRAX';

            } elseif($num == ')' && --$level == 0) {
                $num = 'DEBRAX';
            }
            $res[] = $num;

        }

        if($level != 0) {
            return do_error( 'Chyba: špatný počet závorek');
        }

        $res = implode('', $res);

        $res = preg_replace('#([\+\-]?)BABRAX(.+?)DEBRAX#e', "rectify('\\2', '\\1')", $res);

        debug("After parenthesis proccessing", $res);
        preg_match_all('#[+-]?([^+-]+)#', $res, $ar, PREG_PATTERN_ORDER);

        for($i = 0; $i <count($ar[0]); $i++) {
              $last = substr($ar[0][$i], -1, 1); 
              if($last == '/' || $last == '*' || $last == '^' || $last == 'E') {
                    $ar[0][$i] = $ar[0][$i].$ar[0][$i+1];
                    unset($ar[0][$i+1]);
              }
        }

        $result = 0;
        foreach($ar[0] as $num) {
            $result += multi($num);
        }
        debug("RecCalc output", $result);
        return $result;
    }

            function multi($inp) {
        debug("Multi input", $inp);

        $inp = explode(' ', ereg_replace('([\*\/\^])', ' \\1 ', $inp));

        foreach($inp as $va) {
            if($va != '*' && $va != '/' && $va != '^') {
                $v[] = (float)$va;
            } else {
                $v[] = $va;
            }
        }
        $inp = $v;
        //predpokladame, ze prvni prvek je cislo, ktere budeme dale nasobit
        $res = $inp[0];
        for($i = 1; $i< count($inp); $i++) {

            if($inp[$i] == '*') {
                $res *= $inp[$i + 1];
            } elseif($inp[$i] == '/') {
                if($inp[$i + 1] == 0) do_error('Dělení nulou');

                $res /= $inp[$i + 1];
            } elseif($inp[$i] == '^') {
                $res = pow($res, $inp[$i + 1]);
            }
        }
        debug("Multi output", $res);
        return $res;
    }


    function debug($msg, $var) {
        if(isset($_POST['out']) && $_POST['out'] == '1') {
            echo "\n".$msg.": ".$var;
        }
    }
    echo '<pre>';


    if(eregi('(^[\*\/\+\^])|[a-dg-z \?<>;:"\'\\|\}\{_]|([\*\/\+\-\^]$)', $inp)) {
        do_error('Nalezen neplatný či nesmyslný znak. Překontorlujte si prosím syntax.');
    }

    $result = recCalc($inp);

    $time_end = microtime(true);
    $time = ($time_end - $time_start) *1000;
    $time .= 'ms';

    echo "\n<strong>".$result."</strong>";
    debug('Execution time', $time);
    echo '
'; } еще { ?> <! DOCTYPE html PUBLIC "- // W3C // DTD XHTML 1.0 Strict // EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> Калькулятор <! - <script src = "scripts / spry / xpath.js"> -> <! - функция updateResultDiv (req) { Spry.Utils.setInnerHTML ('result', req.xhRequest.responseText); } function submitit () { if (document.getElementById ('auto'). check) { Spry.Utils.submitForm (document.getElementById ('calc'), updateResultDiv); } } ->

Калькулятор



Также я написал это в чеке, так что извините за странные комментарии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...