允许用户输入他们喜欢的任何数学方程式(使用一个变量):
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,在用户输入上使用非常非常核心的过滤器。
答案 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)
答案 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)