I do not disagree with the use of eval
, the problem is that if you do not say the data processing can occur problems in the input values which will go from being a mathematical operation to being a code injection.
Yet a simple "parser" can solve, an example with preg_match_all
would be so:
preg_match_all('#(\d\.\d+|\d+|[\+\-\/\*])#', $input, $output);
var_dump($output[0]);
It extracts all integer values, with floating point and simple operators, of course it is still possible to inject invalid characters, in case do an input check, thus:
if (preg_match('#^(\d|\d\.\d)([\d\+\-\s\/\*]+|\d+\.\d+)(\d|\d\.\d)+$#', $input) > 0) {
preg_match_all('#(\d\.\d+|\d+|[\+\-\/\*])#', $input, $output);
var_dump($output[0]);
}
We’ll still have the problem of people doing calculations like this:
2 2 3 + - /
But you can solve by checking in the loop the last value, a complete example I did:
<?php
class SimpleMath
{
private static function subcalc($a, $b, $operator)
{
switch ($operator)
{
case '-':
return $a - $b;
break;
case '+':
return $a + $b;
break;
case '*':
return $a * $b;
break;
case '/':
return $a / $b;
break;
}
}
public static function parse($input)
{
$input = trim($input);
if (preg_match('#^(\d|\d\.\d)([\d\+\-\s\/\*]+|\d+\.\d+)(\d|\d\.\d)+$#', $input) > 0) {
preg_match_all('#(\d\.\d+|\d+|[\+\-\/\*])#', $input, $output);
$pre = $output[0];
$j = count($pre);
$operator = null;
$final = null;
for ($i = 0; $i < $j; $i++) {
var_dump($pre[$i]);
switch ($pre[$i]) {
case '-':
case '+':
case '*':
case '/':
if ($op !== null) {
//Se já houver um operador "preparado" e tentar adicionar outro força um erro
throw new Exception('Erro na ordem dos operadores');
}
$op = $pre[$i];
break;
default:
if ($final === null){
$final = $pre[$i];
} else if ($operator === null) {
//Se o anterior não era um operador força um erro
throw new Exception('Erro, falta um operador');
} else if (is_numeric($pre[$i])) {
$final = self::subcalc($final, $pre[$i], $operator);
//Remove operador usado
$operator = null;
} else {
//Se o numero na sequencia for invalido força um erro
throw new Exception('Formato do numero é invalido');
}
}
}
return $final;
} else {
throw new Exception('Input invalido');
}
}
}
var_dump( SimpleMath::parse('2 * 2 - 1') );
It does not work for advanced calculations, but just you adapt and add conditions and or operators, also have the case to use the parentheses as soon as possible I will create an example that supports something like (1 * 2) - (3 /4)
With only
if else
orswitch
example:if($operação === '+')
– Leonardo
Yes, it would facilitate a lot if it existed, because there are several calculations and this way will quadruple the number of lines.
– Rodrigo Segatto
The cited Eval is not very recommendable
– Leonardo
Is there any other way to do this @Ivcs?
– user46523
Better a parser than an Eval
– Bacco
http://answall.com/questions/156657/como-retr-um-resultado-v%C3%A1lido-a-from-an-opera%C3%A7%C3%A3o-em-php
– Daniel Omine