在 PHP 中解决数学问题/表达式(字符串)

发布于 2024-09-02 01:30:55 字数 734 浏览 11 评论 0原文

用户可以输入数学问题(表达式),例如 5 + 6546 ^ 242!sqrt(543)log(54)sin 5sin(50)。经过一些重新格式化(例如将 sin 5 更改为 sin(5))并进行评估后,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 的常见拼写错误吗?

The user can enter a math problem (expression) like 5 + 654, 6 ^ 24, 2!, sqrt(543), log(54), sin 5, sin(50). After some reformatting (e.g. change sin 5 into sin(5)), and doing an eval, PHP gives me the right result:

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

However, this is quite unsafe:

/* 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;

Can anyone point me in the right direction parsing and solving a math question like the examples above, which is safe? Thanks.


Btw, isn't eval just a common misspelling of evil?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里泪两行 2024-09-09 01:30:55

理想情况下,我认为您必须创建某种语法解析器/词法分析器引擎,可以将公式解析为各个部分,然后对其运行方程式。

这样,任何恶意函数都会被忽略,并且系统可能会返回错误。

Ideally, I think you would have to create some sort of grammar parser/lexer engine that could parse out the formula into its parts and then run the equation on that.

That way any rogue functions would just be ignored, and the system could return an error.

请远离我 2024-09-09 01:30:55

看看 PHPExcel 中的计算引擎...它实现了一个安全的公式解析器,可以处理大多数可以计算的公式表达式(包括 LOG() 等函数,以及 2^3 作为幂而不是二元运算符)由 Excel 本身。

Take a look at the calculation engine in PHPExcel... it implements a safe formula parser that can handle most formulaic expressions (including functions such as LOG(), and 2^3 as a power rather than a binary operator) that can be calculated by Excel itself.

阳光①夏 2024-09-09 01:30:55

好吧,在这种情况下,你几乎需要实现自己的计算器 - 我在一次工作面试中得到了它,所以这是我的代码。请记住,这对我来说确实是遗留下来的东西,但我认为它可能会给你一些想法:

<?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 '</pre>';

} else {


?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Calculator</title>
<style type="text/css">
<!--
body {
    font: 100% Verdana, Arial, Helvetica, sans-serif;
    background: #666666;
    margin: 0; /* it's good practice to zero the margin and padding of the body element to account for differing browser defaults */
    padding: 0;
    text-align: center; /* this centers the container in IE 5* browsers. The text is then set to the left aligned default in the #container selector */
    color: #000000;
}
.oneColElsCtr #container {
    width: 46em;
    background: #FFFFFF;
    margin: 0 auto; /* the auto margins (in conjunction with a width) center the page */
    border: 1px solid #000000;
    text-align: left; /* this overrides the text-align: center on the body element. */
}
.oneColElsCtr #mainContent {
    padding: 0 20px; /* remember that padding is the space inside the div box and margin is the space outside the div box */
}
.noshow {
    display: none;
}
-->
</style>
<link rel="stylesheet" href="styles/COHEN_style.css"/>
<script src="scripts/spry/SpryData.js"></script>
<!--<script src="scripts/spry/xpath.js"></script>-->
<script src="scripts/spry/SpryUtils.js"></script>
<script src="scripts/spry/SpryDOMUtils.js" type="text/javascript"></script>
<script src="scripts/spry/SpryCollapsiblePanel.js" type="text/javascript"></script>

<script type="text/javascript">
<!--
function updateResultDiv(req) 
    {
        Spry.Utils.setInnerHTML('result', req.xhRequest.responseText);
    }
function submitit() {
    if(document.getElementById('auto').checked) {
        Spry.Utils.submitForm(document.getElementById('calc'), updateResultDiv);
    }
}
-->
</script>
<link href="scripts/spry/SpryCollapsiblePanel.css" rel="stylesheet" type="text/css" />
</head>

<body class="oneColElsCtr">

<div id="container">
  <div id="mainContent">
    <h1>Calculator</h1>
    <form method="post" id="calc" onsubmit="return Spry.Utils.submitForm(document.getElementById('calc'), updateResultDiv);">
    <input type="text" name="inp" size="80" value="" onkeyup="submitit()"/><br />
    <input type="checkbox" value="1" name="out" /><label>Debug</label>  <input onclick="Spry.$('#submit').toggleClassName('noshow');" checked="checked" type="checkbox" value="1" id="auto" /><label for="auto">Count automatically</label><br />
    <input class="noshow" value="Počítej" type="submit" id="submit"  />
    </form>
    <div id="result">

    </div>
</div>
</div>
</body>
</html>

另外,我在检查中写了它,对于奇怪的评论感到抱歉。

Well you pretty much need to implement your own calculator in that case - I got it at a job interview once so here is my code. Remember it's really legacy stuff for me but I figured it might give you some ideas:

<?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 '</pre>';

} else {


?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Calculator</title>
<style type="text/css">
<!--
body {
    font: 100% Verdana, Arial, Helvetica, sans-serif;
    background: #666666;
    margin: 0; /* it's good practice to zero the margin and padding of the body element to account for differing browser defaults */
    padding: 0;
    text-align: center; /* this centers the container in IE 5* browsers. The text is then set to the left aligned default in the #container selector */
    color: #000000;
}
.oneColElsCtr #container {
    width: 46em;
    background: #FFFFFF;
    margin: 0 auto; /* the auto margins (in conjunction with a width) center the page */
    border: 1px solid #000000;
    text-align: left; /* this overrides the text-align: center on the body element. */
}
.oneColElsCtr #mainContent {
    padding: 0 20px; /* remember that padding is the space inside the div box and margin is the space outside the div box */
}
.noshow {
    display: none;
}
-->
</style>
<link rel="stylesheet" href="styles/COHEN_style.css"/>
<script src="scripts/spry/SpryData.js"></script>
<!--<script src="scripts/spry/xpath.js"></script>-->
<script src="scripts/spry/SpryUtils.js"></script>
<script src="scripts/spry/SpryDOMUtils.js" type="text/javascript"></script>
<script src="scripts/spry/SpryCollapsiblePanel.js" type="text/javascript"></script>

<script type="text/javascript">
<!--
function updateResultDiv(req) 
    {
        Spry.Utils.setInnerHTML('result', req.xhRequest.responseText);
    }
function submitit() {
    if(document.getElementById('auto').checked) {
        Spry.Utils.submitForm(document.getElementById('calc'), updateResultDiv);
    }
}
-->
</script>
<link href="scripts/spry/SpryCollapsiblePanel.css" rel="stylesheet" type="text/css" />
</head>

<body class="oneColElsCtr">

<div id="container">
  <div id="mainContent">
    <h1>Calculator</h1>
    <form method="post" id="calc" onsubmit="return Spry.Utils.submitForm(document.getElementById('calc'), updateResultDiv);">
    <input type="text" name="inp" size="80" value="" onkeyup="submitit()"/><br />
    <input type="checkbox" value="1" name="out" /><label>Debug</label>  <input onclick="Spry.$('#submit').toggleClassName('noshow');" checked="checked" type="checkbox" value="1" id="auto" /><label for="auto">Count automatically</label><br />
    <input class="noshow" value="Počítej" type="submit" id="submit"  />
    </form>
    <div id="result">

    </div>
</div>
</div>
</body>
</html>

Also I wrote it in check so sorry for weird comments.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文