40

我有一个必须动态创建的大型数学表达式。例如,一旦我解析了“某事”,结果将是一个字符串,如:"$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";

但它不起作用。那么,当除以零时,如何避免我的应用程序崩溃?

编辑:

首先,我想澄清一点:表达式是动态构建的,所以如果分母为零,我不能只求值。所以...关于马克贝克的评论,让我举个例子。我的解析器可以构建这样的东西:

"$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";
?> 

但它不打印任何东西。

4

14 回答 14

17
if ($baz == 0.0) {
    echo 'Divisor is 0';
} else {
    ...
}

而不是使用 eval,如果您在 evalled 表达式中使用用户输入,这将是非常危险的,为什么不在PHPClasses 上使用适当的解析器,例如 evalmath,它会在除以零时引发干净的异常

于 2010-06-18T15:44:34.540 回答
13

您只需要设置一个错误处理程序以在发生错误时抛出异常:

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();
于 2015-02-02T15:56:58.653 回答
13

在 PHP7 上,您可以使用DivisionByZeroError

try {
    echo 1/0;
} catch(DivisionByZeroError $e){
    echo "got $e";
} catch(ErrorException $e) {
    echo "got $e";
}
于 2017-09-28T16:10:09.233 回答
6

这是另一个解决方案:

<?php

function e($errno, $errstr, $errfile, $errline) {
    print "caught!\n";
}

set_error_handler('e');

eval('echo 1/0;');

set_error_handler()

于 2010-06-18T15:47:45.173 回答
4

正如其他人所提到的,考虑尝试一种解决方案,让您检查分母是否为 0。

由于该建议似乎对您的目的毫无用处,因此这里有一些关于 PHP 错误处理的背景知识。

PHP 的早期版本没有例外。相反,引发了各种级别的错误消息(通知、警告等)。致命错误停止执行。

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

如果你想“捕捉”这些,你需要设置一个自定义错误处理程序,它将检测除以零错误并对它们做一些事情。不幸的是,自定义错误处理程序包罗万象,这意味着您还需要编写一些代码来对所有其他错误进行适当的处​​理。

于 2010-06-18T16:23:44.443 回答
2
if(@eval("\$result = $expresion;")===FALSE){
  $result=0;
}

虽然不会只捕获除以 0 错误。

于 2010-06-18T15:45:18.730 回答
2

使用@错误控制运算符。)这告诉 php 在发生错误时不输出警告。

eval("\$result = @($expresion);");
if ($result == 0) {
    // do division by zero handling 
} else {
    // it's all good
}
于 2010-06-18T15:53:25.740 回答
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;
}
于 2010-08-22T11:48:38.353 回答
2

我也一直在为此苦苦挣扎,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但这帮助我超越它只是默默地死去。

于 2016-12-12T21:54:10.760 回答
1

包含数字和数学运算符 + - * / 的字符串作为输入传递。程序必须评估表达式的值(根据 BODMAS)并打印输出。

输入/输出示例:如果参数为“7 + 4*5”,则输出必须为 27。如果参数为“55 + 21 * 11 - 6/0”,则输出必须为“错误”(除以零是没有定义的)。

于 2013-07-17T12:07:12.563 回答
1

问题:

b=1; c=0; a=b/c; // Error Divide by zero

解决方案简单:

if(c!=0) a=b/c;
else // error handling
于 2015-11-19T15:06:53.770 回答
0

我意识到这是一个老问题,但它在今天很重要,我不太喜欢这里的答案。

解决此问题的正确方法是自己实际评估表达式 - 即通过解析表达式,然后逐步评估它,而不是将其转换为 PHP。这可以使用https://en.wikipedia.org/wiki/Shunting-yard_algorithm来完成。

我编写了以下实现,但我还没有测试过。它基于上面的维基百科文章。不支持右关联运算符,因此稍微简化了一些。

// 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";

在您的特定情况下

即使我更喜欢 Shutting Yard 解决方案 - 如果我仍然决定使用 eval() 版本,我会创建一个 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 ) )");
于 2018-10-21T15:41:52.410 回答
0

使用intdivDivisionByZeroError

try {
    $a = 5;
    $b = 0;
    intdiv($a,$b);
}
catch(DivisionByZeroError $e){
    echo "got {$e->getMessage()}";
}
于 2019-05-25T20:18:46.703 回答
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
}
于 2019-10-16T12:31:26.650 回答