Dynamic calculations php

5

Is there a possibility of doing a calculation in php, and my mathematical operations are in a variable? Ex:

$operacao = '+';
$valor1 = 10;
$valor2 = 10;
$calculo = $valor1 .$operacao. $valor2;

I would have to give Result 20, however, by default PHP concatenates it by bringing me a string:

"10 + 10";
    
asked by anonymous 08.06.2016 / 20:44

2 answers

4

Yes, you have the function eval () ( eval - Executes a string as PHP code ).

$operacao = '+';
$valor1 = 10;
$valor2 = 10;
$calculo = "$valor1 $operacao $valor2";
eval('echo '.$calculo.';');

DEMO

With function:

function soma($n1,$n2)
{
    return $n1+$n2;
}

eval('echo soma(10,20);');

DEMO

Note: Particularly speaking, I would not do the sum like this, but once I had to use eval () to solve a particular problem since the return came from a function in a string and had to execute , I used eval () and solved that particular problem.

Reference:

eval

PHP eval () function - Utilities

    
08.06.2016 / 21:04
2

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)

    
05.12.2016 / 19:27