我有一个必须动态创建的大型数学表达式。例如,一旦我解析了“某事”,结果将是一个字符串,如:"$foo+$bar/$baz";
。
所以,为了计算那个表达式的结果我正在使用eval
函数......就像这样:
eval("\$result = $expresion;");
echo "The result is: $result";
这里的问题是,有时我会得到错误,表示存在除零的错误,而且我不知道如何捕获该异常。我尝试过这样的事情:
eval("try{\$result = $expresion;}catch(Exception \$e){\$result = 0;}");
echo "The result is: $result";
或者:
try{
eval("\$result = $expresion;");
}
catch(Exception $e){
$result = 0;
}
echo "The result is: $result";
但它不起作用。那么,当除零时,如何避免我的应用程序崩溃?
修改
首先,我想澄清一下:表达式是动态构建的,所以如果分母为零,我不能只评估它。那么......关于Mark Baker的评论,让我举个例子。我的解析器可以构建这样的东西:
"$foo + $bar * ( $baz / ( $foz - $bak ) )"
解析器逐步构建字符串而不用担心变量的值...所以在这种情况下如果$foz == $bak
实际上除以零:$baz / ( 0 )
。
另一方面,正如皮特建议的那样,我试过了:
<?php
$a = 5;
$b = 0;
if(@eval(" try{ \$res = $a/$b; } catch(Exception \$e){}") === FALSE)
$res = 0;
echo "$res\n";
?>
但它没有打印任何东西。
答案 0 :(得分:16)
if ($baz == 0.0) {
echo 'Divisor is 0';
} else {
...
}
如果你在evalled表达式中使用用户输入,那么使用eval是非常危险的,而不是使用eval,为什么不使用evalmath on PHPClasses之类的正确解析器,并在除以零时引发一个干净的异常< / p>
答案 1 :(得分:6)
这是另一种解决方案:
<?php
function e($errno, $errstr, $errfile, $errline) {
print "caught!\n";
}
set_error_handler('e');
eval('echo 1/0;');
答案 2 :(得分:6)
您只需要设置一个错误处理程序,以便在出现错误时抛出异常:
set_error_handler(function () {
throw new Exception('Ach!');
});
try {
$result = 4 / 0;
} catch( Exception $e ){
echo "Divide by zero, I don't fear you!".PHP_EOL;
$result = 0;
}
restore_error_handler();
答案 3 :(得分:3)
正如其他人所提到的,请考虑尝试一种解决方案,让您检查分母是否为0。
由于这个建议对你的目的似乎毫无用处,所以这里有一些关于PHP错误处理的背景知识。
早期版本的PHP没有例外。而是提出了各种级别的错误消息(Notices,Warnings,Etc)。致命错误会停止执行。
PHP5为表带来了异常,而较新的PHP提供的库(PDO)将在发生错误/意外事件时抛出异常。但是,核心代码库没有被重写为使用异常。核心功能和操作仍然依赖于旧的错误系统。
除以0时,您会收到警告,而不是例外
PHP Warning: Division by zero in /foo/baz/bar/test.php(2) : eval()'d code on line 1
PHP Stack trace:
PHP 1. {main}() /foo/baz/bar/test.php:0
PHP 2. eval() /foo/baz/bar/test.php:2
如果你想“抓住”这些,你需要set a custom error handler来检测除零错误并对它们采取措施。不幸的是,自定义错误处理程序是一个全部捕获,这意味着您还需要编写一些代码来执行适合所有其他错误的操作。
答案 4 :(得分:2)
if(@eval("\$result = $expresion;")===FALSE){
$result=0;
}
虽然不会仅仅因为0错误而被分开。
答案 5 :(得分:2)
我也面临着这个问题(动态表达式)。尽管它可能不是最好的方式,但它的工作原理。您可以返回null或false或任何您想要的内容,而不是抛出异常。希望这会有所帮助。
function eval_expression($expression)
{
ob_start();
eval('echo (' . $expression . ');');
$result = ob_get_contents();
ob_end_clean();
if (strpos($result, 'Warning: Division by zero')!==false)
{
throw new Exception('Division by zero');
}
else return (float)$result;
}
答案 6 :(得分:1)
在PHP7上,您可以使用DivisionByZeroError
try {
echo 1/0;
}
catch(DivisionByZeroError $e){
echo "got $e";
}
答案 7 :(得分:0)
使用@
(An error control operator。)这告诉php在出错时不输出警告。
eval("\$result = @($expresion);");
if ($result == 0) {
// do division by zero handling
} else {
// it's all good
}
答案 8 :(得分:0)
包含数字和数学运算符+ - * /的字符串作为输入传递。 程序必须评估表达式的值(按照BODMAS)并打印输出。
示例输入/输出: 如果参数为“7 + 4 * 5”,则输出必须为27。 如果参数为“55 + 21 * 11 - 6/0”,则输出必须为“error”(未定义除零)。
答案 9 :(得分:0)
问题:
b=1; c=0;
a=b/c;
// Error Divide by zero
解决方案简单:
if(c!=0) a=b/c;
else // error handling
答案 10 :(得分:0)
我也一直在努力解决这个问题,set_error_handler
解决方案对我不起作用,可能基于PHP版本差异。
我的解决方案是尝试检测关机时的错误:
// Since set_error_handler doesn't catch Fatal errors, we do this
function shutdown()
{
$lastError = error_get_last();
if (!empty($lastError)) {
$GLOBALS['logger']->debug(null, $lastError);
}
}
register_shutdown_function('shutdown');
我不确定为什么除以0会关闭而不是由set_error_handler
处理,但这有助于我超越它而只是默默地死去。
答案 11 :(得分:0)
我意识到这是一个古老的问题,但是今天它很重要,我真的不喜欢这里的答案。
解决此问题的正确方法是实际自己评估表达式-即通过解析表达式,然后逐步评估它,而不是将其编译为PHP。可以使用https://en.wikipedia.org/wiki/Shunting-yard_algorithm完成。
我编写了以下实现,但尚未测试。它基于以上Wikipedia文章。不支持右关联运算符,因此略有简化。
// You may need to do a better parsing than this to tokenize your expression.
// In PHP, you could for example use token_get_all()
$formula = explode(' ', 'foo + bar * ( baz / ( foz - bak ) )');;
$queue = array();
$operators = array();
$precedence = array('-' => 2, '+' => 2, '/' => 3, '*' => 3, '^' => 4);
$rightAssoc = array('^');
$variables = array('foo' => $foo, 'bar' => $bar, 'baz' => $baz, 'foz' => $foz, 'bak' => $bak);
foreach($formula as $token) {
if(isset($variables[$token])) {
$queue[] = $variables[$token];
} else if(isset($precedence[$token])) {
// This is an operator
while(
sizeof($operators) > 0 &&
$operators[sizeof($operators)-1] != '(' && (
$precedence[$operators[sizeof($operators)-1]] > $precedence[$token] ||
(
$precedence[$operators[sizeof($operators)-1]] == $precedence[$token] &&
!in_array($operators[sizeof($operators)-1], $rightAssoc)
)
)
) $queue[] = array_pop($operators);
$operators[] = $token;
} else if($token == '(') {
$operators[] = '(';
} else if($token == ')') {
while($operators[sizeof($operators)-1] != '(') {
$queue[] = array_pop($operators);
}
array_pop($operators);
} else if($token == ')') {
while($operators[sizeof($operators)-1] != ')') {
$queue[] = array_pop($operators);
}
if(null === array_pop($operators))
throw new \Exception("Mismatched parentheses");
}
$queue = array_merge($queue, array_reverse($operators));
$stack = array();
foreach($queue as $token) {
if(is_numeric($token)) $stack[] = $token;
else switch($token) {
case '+' :
$stack[] = array_pop($stack) + array_pop($stack);
break;
case '-' :
// Popped variables come in reverse, so...
$stack[] = -array_pop($stack) + array_pop($stack);
break;
case '*' :
$stack[] = array_pop($stack) * array_pop($stack);
break;
case '/' :
$b = array_pop($stack);
$a = array_pop($stack);
if($b == 0)
throw new \Exception("Division by zero");
$stack[] = $a / $b;
break;
}
}
echo "The result from the calculation is ".array_pop($stack)."\n";
在您的情况下
即使我更喜欢Shunting Yard解决方案-如果我仍然决定使用eval()-version,我也会创建一个custom_division($ leftHandSide,$ rightHandSide)方法,该方法会引发异常。这段代码:
eval("$foo + $bar * ( $baz / ( $foz - $bak ) )");
成为
function custom_division($a, $b) { if($b == 0) throw Exception("Div by 0"); }
eval("$foo + $bar * ( custom_division( $baz, ( $foz - $bak ) )");
答案 12 :(得分:0)
使用intdiv
和DivisionByZeroError
:
try {
$a = 5;
$b = 0;
intdiv($a,$b);
}
catch(DivisionByZeroError $e){
echo "got {$e->getMessage()}";
}
答案 13 :(得分:0)
这是我发现的最佳方法:
error_clear_last(); // Clear any previous error
$result = @(1/0); // Executes the division, suppressing the errors
$e = error_get_last(); // Catches the last error
if ($e !== null && $e['message'] == 'Division by zero') {
// Division by zero occurred, do something here
}