A user is allowed to enter any mathematical equation they like (with one variable):
x + 5
1 - x/2
(x/3) * (56/13)
My standard answer to this question whenever it crops up:
Don't use eval (especially as you're stating that this is user input) or reinvent the wheel by writing your own formula parser.
Take a look at the evalMath class on PHPClasses. It should do everything that you've listed here.
EDIT
re: Unfortunately evalMath does not handle things like (x > 5)
change lines 177-179 to
$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
change line 184 to
if (preg_match("/[^\w\s+*^\/()\.,-<>=]/", $expr, $matches)) { // make sure the characters are all good
add
case '>':
$stack->push($op1 > $op2); break;
case '<':
$stack->push($op1 < $op2); break;
case '=':
$stack->push($op1 == $op2); break;
after line 321
and evalMath will now handle (x > 5), (x < 5) or (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)'));
Further Edit
Missed line 307 that should be modified to read:
if (in_array($token, array('+', '-', '*', '/', '^', '>', '<', '='))) {
If you're dealing with user input I'd stay away from eval. Write a parser and break the formula into nested arrays.
1 - x/2
becomes
Array
(
[0] => -
[1] => 1
[2] => Array
(
[0] => /
[1] => x
[2] => 2
)
)
It's a little tricky to write the parser, but it's really easy to evaluate a parsed formula.
Depends...
What is the complexity it will accept? Because for common mathematical equations (like the ones you posted), I do not see too much problem in writing a parser. The main problematic question would be round the numbers and place correct parenthesis.
But if the equations are going to accept "advanced" inputs, like {[()]}, or X², X³, or getting further, diferential calculus and college maths, so things can go crazy.
If the complexity reach symbolic handling, try read and search something about CAS (Calculate Algebra Systems).
Of course, I extremely recommend you to make your own system for inputs, validate against it, and evangelize users to tie inputs to it. Nothing too complex, but enough to make you (and others) comfortable and safe to reach what you need.
Even if you pass through eval, you will have to replace x with some number. The kind of strategy I would have is to pass value for x, and see what is the evaluated value. If it is more than 0, then I would try a smaller number, and if it is less than 0, I would try a bigger number recursively until it satisfies an error margin (<>0.001%).
Using eval function is very dangerous when you can't control the string argument.
Try Matex for safe mathematical formulas calculation. It supports also variables and custom functions.
Eval is not Evil!!!!!
Yes it can stuff your system up completely if you write bad code - but recent PHP versions can parse an invalid expression without crashing the whole script. And there are many other ways of exposing your system by writing bad code.
That just leaves the possiblity of code injection attacks - which can easily be avoided by doing a preg_replace on everythnig which is not a safe character (i.e. 0....9, (, ), +, -, *, /, ^, .)