在php中处理数学方程式

时间:2010-10-25 22:35:12

标签: php math eval

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

x + 5

1 - x/2

(x/3) * (56/13)

它们作为字符串存储在数据库中。当它们被检索时,我需要用'x'代替数字并检查方程的值。

我怎么能这样做?

我正在考虑编写一个解析器来解构字符串并将它们转换为方程式,但这听起来既昂贵又有问题。另一个选择是通过eval传递它们(但如果我可以帮助它,我不是使用eval的忠实粉丝。)

有什么想法吗?

更新: 我还需要能够获得类似“(x> 5)”的布尔值。使用evalMath

是不可能的

更新2: 我需要解雇这些批次。我一直在研究php中的eval,但是不能让它为(5> 4)返回一个布尔值但是我注意到js会这样做...也许我应该调查node.js ......

更新3: 在尝试了node.js(并让它工作)之后,我回过头来获得eval以便在PHP中工作,请参阅:Can php eval return a boolean value?

所以我将使用eval,在用户输入上使用非常非常核心的过滤器。

8 个答案:

答案 0 :(得分:12)

这个问题的标准答案:

不要使用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 <5)或(x = 5)

// 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('+', '-', '*', '/', '^', '>', '<', '='))) {

答案 1 :(得分:4)

Eval不是邪恶!!!!!

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

这就留下了代码注入攻击的可能性 - 通过在everythnig上执行不是安全字符的preg_replace可以很容易地避免这种情况(即0 .... 9,(,),+, - ,*,/ ,^,。)

答案 2 :(得分:1)

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

1 - x/2

变为

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

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

答案 3 :(得分:1)

如果你在Linux机器上运行你的代码,那么稍微冒险的可能性就是使用bc命令(确保在将它输入到系统cmd之前正确地转义输入)。我不能说使用系统比eval的风险要好得多,所以我期待在这里有一些贬值。

答案 4 :(得分:0)

即使你通过eval,你也必须用一些数字替换x。我将采用的策略是为x传递值,并查看评估值是什么。如果它大于0,那么我会尝试一个较小的数字,如果它小于0,我会递归地尝试一个更大的数字,直到它满足误差范围(&lt;&gt; 0.001%)。

答案 5 :(得分:0)

...取决于

它会接受多少复杂性?因为对于常见的数学方程式(就像你发布的那些),我在编写解析器时没有看到太多问题。主要问题是围绕数字并放置正确的括号。

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

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

当然,我非常建议您制作自己的输入系统,对其进行验证,并向用户传播,以便将输入与输入联系起来。没有什么太复杂,但足以让你(和其他人)舒适和安全地达到你所需要的。

答案 6 :(得分:-1)

<强>的eval()

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

<强> PHPClasses

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

答案 7 :(得分:-1)

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

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