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 entering values which will stop being a mathematical operation to be a code injection.
Even though a simple parser can solve, an example with preg_match_all
would look like this:
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 it does an input check, like this:
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 the person doing calculations like this:
2 2 3 + - /
But you can solve by checking the last value in the loop, a complete example that 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 you simply adapt and add conditions and or operators, you also have the case of using the parentheses as soon as possible I will create an example that supports something like (1 * 2) - (3 /4)