【问题标题】:Process mathematical equations in php在 php 中处理数学方程
【发布时间】:2011-04-30 11:37:20
【问题描述】:

允许用户输入他们喜欢的任何数学方程(带有一个变量):

x + 5

1 - x/2

(x/3) * (56/13)

这些以字符串形式存储在数据库中。当它们被检索到时,我需要用“x”替换一个数字并检查方程的值。

我该怎么做?

我正在考虑编写一个解析器来解构字符串并将它们转换为方程式,但这听起来很昂贵且有问题。另一种选择是通过 eval 传递它们(但如果我能提供帮助,我不太喜欢使用 eval)。

有什么想法吗?

更新: 我还需要能够获得诸如“(x > 5)”之类的布尔值。 evalMath 无法做到这一点

更新 2: 我需要一秒钟发射 lots 的这些。我一直在研究 php 中的 eval 但无法让它返回 (5 > 4) 的布尔值但是我注意到 js 会这样做...也许我应该调查 node.js...

更新 3: 在尝试了一些有趣的 node.js(并让它工作)之后,我回去让 eval 在 PHP 中工作,请参阅:Can php eval return a boolean value?

因此,我将使用 eval 对用户输入进行非常非常硬核的过滤器。

【问题讨论】:

  • 如果你需要做更复杂的事情,wolframalpha.com/developers.html">WolframAlpha为开发者提供了一个API。
  • @TheMagician 好主意,但不幸的是这些需要经常触发(每秒数千次),因此外部 api 不起作用。

标签: php math eval


【解决方案1】:

每当这个问题出现时,我的标准答案是:

不要使用 eval(尤其是当您声明这是用户输入时)或通过编写自己的公式解析器来重新发明轮子。

看看 PHPClasses 上的 evalMath 类。它应该完成您在此处列出的所有操作。

编辑

re: 不幸的是 evalMath 不能处理 (x > 5) 之类的事情

将第 177-179 行更改为

$ops   = array('+', '-', '*', '/', '^', '_', '>', '<', '=');
$ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>0, '>' => 0, '<' => 0, '=' => 0); // right-associative operator?
$ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2, '>' => 0, '<' => 0, '=' => 0); // operator precedence

将第 184 行更改为

if (preg_match("/[^\w\s+*^\/()\.,-<>=]/", $expr, $matches)) { // make sure the characters are all good

添加

case '>':
     $stack->push($op1 > $op2); break;
case '<':
     $stack->push($op1 < $op2); break;
case '=':
     $stack->push($op1 == $op2); break;

第 321 行之后

而 evalMath 现在将处理 (x > 5)、(x

// instantiate a new EvalMath
$m = new EvalMath;
$m->suppress_errors = true;
// set the value of x
$m->evaluate('x = 3');
var_dump($m->evaluate('y = (x > 5)'));

进一步编辑

错过了应该修改为的第 307 行:

if (in_array($token, array('+', '-', '*', '/', '^', '>', '<', '='))) {

【讨论】:

  • 不幸的是 evalMath 不能处理 (x > 5) 之类的事情
  • @ae - 让 evalMath 处理 (x > 5) 等的工作并不多。尽管有危险,您似乎更愿意使用 eval(),但请参阅我的编辑所涉及的更改。
  • 太好了!我再等一会儿,看看进展如何。
【解决方案2】:

Eval 不是邪恶的!!!!!!

是的,如果你编写了糟糕的代码,它会完全填满你的系统——但是最近的 PHP 版本可以解析一个无效的表达式,而不会使整个脚本崩溃。还有许多其他方法可以通过编写不良代码来暴露您的系统。

这只是留下了代码注入攻击的可能性——可以通过对每个不是安全字符(即 0....9、(、)、+、-、*、/ , ^, .)

【讨论】:

  • preg_replace 使表达式“安全”如果表达式可以包含诸如 log()、sin() 之类的函数会变得更加尴尬... OP 没有说明是否是这种情况。
  • 同意 - 它更尴尬 - 但远非不可能实现。重要的是使用白名单而不是黑名单。
  • 感谢 Eval is not Evil 回复 :)
  • 但是 - 我无法让 eval 为 '(5 > 12)' 返回布尔值 - 有什么想法吗?
  • @ae 可以是$myBool = eval("return !!(5 &gt; 12);");
【解决方案3】:

如果您正在处理用户输入,我会远离 eval。编写解析器并将公式分解为嵌套数组。

1 - x/2

变成

Array
(
    [0] => -
    [1] => 1
    [2] => Array
        (
            [0] => /
            [1] => x
            [2] => 2
        )
)

编写解析器有点棘手,但评估已解析的公式确实很容易。

【讨论】:

    【解决方案4】:

    如果您在 linux 机器上运行代码,则使用 bc 命令(确保在将其提供给系统 cmd 之前正确地转义您的输入)可能会有点风险。我不能说使用 system 比 eval 的风险要好得多,所以我在这里期待一些反对意见。

    【讨论】:

      【解决方案5】:

      即使你通过了 eval,你也必须将 x 替换为某个数字。我将采用的策略是为 x 传递值,然后查看评估值是多少。如果大于 0,则尝试较小的数字,如果小于 0,则递归尝试较大的数字,直到满足误差范围(0.001%)。

      【讨论】:

        【解决方案6】:

        取决于...

        它将接受什么复杂度?因为对于常见的数学方程(如您发布的那些),我在编写解析器时没有看到太多问题。主要有问题的问题是对数字进行四舍五入并放置正确的括号。

        但是,如果方程要接受“高级”输入,例如 {[()]} 或 X²、X³,或者更进一步,微积分和大学数学,那么事情就会变得疯狂。

        如果复杂性达到符号处理,请尝试阅读并搜索有关 CAS(计算代数系统)的内容。

        当然,我极力推荐您制作自己的输入系统,针对它进行验证,并宣传用户将输入与它联系起来。没有什么太复杂,但足以让您(和其他人)舒适、安全地获得所需的东西。

        【讨论】:

          【解决方案7】:

          eval()

          取决于你必须做什么,但无论如何,最便宜的方法是使用变量的替换函数,然后使用 eval() 运行表达式。
          当然,您首先需要确保您的公式是 php 语法。
          好处是你可以使用 php 支持的任何数学函数,坏处是,使用 eval() 从来都不好 :)

          PHP类

          另一个不错的选择是在网上冲浪,直到找到解析器:P
          http://www.phpclasses.org/package/2695-PHP-Safely-evaluate-mathematical-expressions.html

          【讨论】:

          • -1 不错!!!用户输入是非常危险的,正如这个问题中的要求一样。
          • 不幸的是 evalMath 不能处理返回布尔值 (5 > 3) 之类的事情
          【解决方案8】:

          当你无法控制字符串参数时,使用eval函数是非常危险的。

          尝试Matex 进行安全的数学公式计算。它还支持变量和自定义函数。

          【讨论】:

            猜你喜欢
            • 2011-07-25
            • 1970-01-01
            • 1970-01-01
            • 2015-08-26
            • 2014-02-09
            • 1970-01-01
            • 2014-07-08
            • 2017-03-26
            • 1970-01-01
            相关资源
            最近更新 更多