-2

I am trying to perform calculations in a string and am wondering how to parse through a string expression with a combination of numbers and operations (+ addition, . concatenation, etc).

Ex: [11.0.0] would equal 1100 or [3+5] would equal 8.

AyeYo
  • 27
  • 2
  • 7
  • Never heard about using concatenation in a calculation, but for other operators (+,-,*,/,%) you can use `eval()` i.e: `$string = "3+5"; echo eval("return $string;")` – catcon Oct 14 '19 at 05:26
  • 1
    Possible duplicate of [calculate math expression from a string using eval](https://stackoverflow.com/questions/18880772/calculate-math-expression-from-a-string-using-eval) – Top-Master Oct 14 '19 at 05:29

2 Answers2

0

You'd usually tokenize the string and then parse the string, the way you want, into executable calculation logic to then execute.

While there are some PHP lexer library for tokenization, you'd probably need to write your own parser (cause only you would know the rules to your calculation syntax).

If you don't have any needs to formulate your own syntax, you're free to use existing library that does the whole thing for you.

Koala Yeung
  • 7,475
  • 3
  • 30
  • 50
0

You can use this to write math expressions in input fields (which is based on another answer).

Usage Examples

$Cal = new Field_calculate();

$result = $Cal->calculate('5+7'); // 12
$result = $Cal->calculate('(5+9)*5'); // 70
$result = $Cal->calculate('(10.2+0.5*(2-0.4))*2+(2.1*4)'); // 30.4

Code

This is the class that is used in above examples:

class Field_calculate {
    const PATTERN = '/(?:\-?\d+(?:\.?\d+)?[\+\-\*\/])+\-?\d+(?:\.?\d+)?/';

    const PARENTHESIS_DEPTH = 10;

    public function calculate($input){
        if(strpos($input, '+') != null || strpos($input, '-') != null || strpos($input, '/') != null || strpos($input, '*') != null){
            //  Remove white spaces and invalid math chars
            $input = str_replace(',', '.', $input);
            $input = preg_replace('[^0-9\.\+\-\*\/\(\)]', '', $input);

            //  Calculate each of the parenthesis from the top
            $i = 0;
            while(strpos($input, '(') || strpos($input, ')')){
                $input = preg_replace_callback('/\(([^\(\)]+)\)/', 'self::callback', $input);

                $i++;
                if($i > self::PARENTHESIS_DEPTH){
                    break;
                }
            }

            //  Calculate the result
            if(preg_match(self::PATTERN, $input, $match)){
                return $this->compute($match[0]);
            }
            // To handle the special case of expressions surrounded by global parenthesis like "(1+1)"
            if(is_numeric($input)){
                return $input;
            }

            return 0;
        }

        return $input;
    }

    private function compute($input){
        $compute = create_function('', 'return '.$input.';');

        return 0 + $compute();
    }

    private function callback($input){
        if(is_numeric($input[1])){
            return $input[1];
        }
        elseif(preg_match(self::PATTERN, $input[1], $match)){
            return $this->compute($match[0]);
        }

        return 0;
    }
}
Top-Master
  • 7,611
  • 5
  • 39
  • 71