允许用户输入他们喜欢的任何数学方程式(使用一个变量):
x + 5
1 - x/2
(x/3) * (56/13)
它们作为字符串存储在数据库中.当它们被检索时,我需要用'x'代替数字并检查等式的值.
我怎么能这样做?
我正在考虑编写一个解析器来解构字符串并将它们转换为方程式,但这听起来既昂贵又有问题.另一个选择是通过eval传递它们(但如果我可以帮助它,我不是使用eval的忠实粉丝).
有任何想法吗?
更新:我还需要能够获得类似"(x> 5)"的布尔值.evalMath无法做到这一点
更新2:我要火很多这些秒.我一直在研究eval在php中但是不能让它为(5> 4)返回一个布尔值但是我注意到js会这样做...也许我应该调查node.js ...
更新3:在尝试了node.js(并让它工作)之后,我回过头来获得eval以便在PHP中工作看到:php eval可以返回一个布尔值吗?
所以我将使用eval和一个非常非常硬核的用户输入过滤器.
Mar*_*ker 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
Run Code Online (Sandbox Code Playgroud)
将第184行更改为
if (preg_match("/[^\w\s+*^\/()\.,-<>=]/", $expr, $matches)) { // make sure the characters are all good
Run Code Online (Sandbox Code Playgroud)
加
case '>':
$stack->push($op1 > $op2); break;
case '<':
$stack->push($op1 < $op2); break;
case '=':
$stack->push($op1 == $op2); break;
Run Code Online (Sandbox Code Playgroud)
在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)'));
Run Code Online (Sandbox Code Playgroud)
进一步编辑
错过的行307应修改为:
if (in_array($token, array('+', '-', '*', '/', '^', '>', '<', '='))) {
Run Code Online (Sandbox Code Playgroud)
埃瓦尔不是邪恶的!!!!!
是的,如果您编写了错误的代码,它可以完全塞满您的系统 - 但是最近的 PHP 版本可以解析无效的表达式而不会导致整个脚本崩溃。还有许多其他方法可以通过编写糟糕的代码来暴露您的系统。
这只是留下了代码注入攻击的可能性 - 通过对不是安全字符(即 0....9, (, ), +, -, *, /, ^, .)