views:

421

answers:

6

I have a large mathematical expression that has to be created dinamically. So, for example, once I have parsed "something" the result will be a string like: "$foo+$bar/$baz";.

So, for calculating the result of that expression I'm using the eval function... something like this:

eval("\$result = $expresion;");
echo "The result is: $result";

The problem here is that sometimes I get errors that says there was a division by zero, and I don't know how to catch that Exception. I have tried things like:

eval("try{\$result = $expresion;}catch(Exception \$e){\$result = 0;}");
echo "The result is: $result";

Or:

try{
    eval("\$result = $expresion;");
}
catch(Exception $e){
    $result = 0;
}
echo "The result is: $result";

But it does not work. So, how can I avoid that my application crashes when there is a division by zero?

Edit:

First, I want to clarify something: the expression is built dinamically, so I can't just eval if the denominator is zero. So... with regards to the Mark Baker's comment, let me give you an example. My parser could build something like this:

"$foo + $bar * ( $baz / ( $foz - $bak ) )"

The parser build the string step by step without worring about the value of the vars... so in this case if $foz == $bak there's in fact a division by zero: $baz / ( 0 ).

On the other hand as Pete suggested, I tried:

<?php
$a = 5;
$b = 0;

if(@eval(" try{ \$res = $a/$b; } catch(Exception \$e){}") === FALSE)
        $res = 0;
echo "$res\n";
?> 

But it does not print anything :(

+5  A: 

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

Rather than use eval, which is highly dangerous if you're using user-input within the evalled expression, why not use a proper parser such as evalmath on PHPClasses, and which raises a clean exception on divide by zero

Mark Baker
No... as I said the expression is built dinamically. If it were a static expression it'd be really easy, but it's not.
Cristian
How is it built dynamically? It must be "constructed" somehow by your "parser"... and at that point you should be able to identify whether the divisor is 0 or not!
Mark Baker
This should work if you replace $baz with eval("\$result = $baz;") and then test is $result is 0 or not.
Anon
@Mark I have updated the post with more information that answers your question. Thanks for your help.
Cristian
Added link to an evaluation class which might save you a lot of grief in the future
Mark Baker
Thank you... that's much better that reinvent the wheel :D That class worked fine.
Cristian
+1  A: 
if(@eval("\$result = $expresion;")===FALSE){
  $result=0;
}

Won't just catch divide by 0 errors though.

Pete
+1  A: 

Here's another solution:

<?php

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

set_error_handler('e');

eval('echo 1/0;');

See set_error_handler()

Bill Karwin
"It is not possible to catch a parse error in eval() using set_error_handler()"http://www.php.net/manual/en/function.eval.php
Pete
@Pete: Right, but the OP was asking about division by zero errors, not parse errors. I tested the above script and it catches the error.
Bill Karwin
A: 

Use a @ (An error control operator.) This tells php to not output warnings in case of errors.

eval("\$result = @($expresion);");
if ($result == 0) {
    // do division by zero handling 
} else {
    // it's all good
}
ghoppe
A: 

As others have mentioned, consider trying a solution that will let you check if the denominator is 0.

Since that advice seems useless your purpose, here's a little background on PHP error handling.

Early versions of PHP didn't have exceptions. Instead, error messages of various levels were raised (Notices, Warnings, Etc). A Fatal error stops execution.

PHP5 brought exceptions to the table, and newer PHP provided libraries (PDO) will throw exceptions when bad/unexpected things happen. Hoever, the core codebase was NOT rewritten to use exception. Core functions and operations still rely on the old error system.

When you divide by 0, you get a Warning, not an exception

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

If you want to "catch" these, you'll need to set a custom error handler that will detect division by zero errors and do something about them. Unfortunately, custom error handlers are a catch all, which means you'll also need to write some code to do something appropriate with all other errors.

Alan Storm
+2  A: 

I was facing that problem as well (dynamic expressions). Idid it that way which might not be the nicest way but it works. Instead of throwing an Exception you can of course return null or false or whatever you wish. Hope this helps.

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;
}
Christopher Fox