0
<?php

function compute($input){

        $compute = create_function('', 'return '.$input.';');
        return 0 + $compute();
    
}

$test = array(
    '5 + 5',
    '3 +( 2 * 3)',
    '(3 + 2) * 3',
    '3 + 2 * 3',
    '(3-2-1)*3'
    
);

foreach( $test AS $string){
    echo compute( $string);
    echo "<br />";
}

http://sandbox.onlinephpfunctions.com/code/61ee53b5477d7450ff89f4f012915a44e609000b

The code above gets deprecated in PHP 7.2. Most of the other people who had this problem were told to use an anonymous function. But in this case I have no idea how to use it.

  • 2
    I never knew about this function (scary thing it is indeed), but the doc page warns you it uses eval. Coincidentally (or not) this is the function you might use to parse your array into runnable code (which is what you do: you have a string that you want to "run"). Warning: `eval` is not something you wish to do lightly, but as you are allready doing that basically, you could keep on using it I guess :) – Nanne Jul 18 '18 at 10:27
  • 1
    There's no shame in utilizing features that set dynamic languages apart from compiled languages. `eval()` is what you should be using here, given that it doesn't contain user input. Otherwise, a slightly complex regex assertion could be used prior. – mario Jul 18 '18 at 10:42
  • Mario makes a good point that eval without user input is generally safer. You can go one stretch further and say that carefully vetted code from user input is also fine, e.g. formulas that have undergone careful review by an administrator before being allowed into the system. – braindigitalis Jul 18 '18 at 10:50
  • https://stackoverflow.com/q/18880772/2943403 and https://stackoverflow.com/q/5803679/2943403 and https://stackoverflow.com/q/12692727/2943403 – mickmackusa Jul 18 '18 at 11:19
  • https://stackoverflow.com/q/5057320/2943403 – mickmackusa Jul 18 '18 at 11:25
  • https://stackoverflow.com/q/1804249/2943403 – mickmackusa Jul 18 '18 at 11:36
  • 18880772 uses exactly my function - because that's where it was coming from.. – Alexander Weixelbaumer Jul 18 '18 at 14:54

2 Answers2

0

A simple parser, perhaps recursive descent, or like this one would do the job nicely, if you REALLY wanted to remain using PHP for the entire solution. You can quite easily use such a system to evaluate custom formulas without resorting to eval.

Personally, i wanted something that could evaluate an entire function in php safely (with loops, conditionals etc), so i used a very lightweight javascript interpreter called duktape, which can be compiled into php 7 as a module. You can then call it to evaluate javascript functions, which are simplistic in nature and have no input/output capabilities except for being able to return a value, and take input from the original script.

Here's how i did this:

    /* Execute a javascript function. Requires the duktape extension to be included in PHP.
     * Accepts an array of variables by name to include as parameters to the called code.
     * The code is expected to return a value, which should be scalar (numeric, string or null).
     */
    function jsfunction($inputs, $code)
    {
            $duktape = new Duktape();

            $javascript = "function e() {";
            foreach ($inputs as $name=>$value) {
                    $javascript .= "var " . $name . ' = ' . json_encode((object)$value) . ";\n";
            }
            $javascript .= $code . "}\ne();";

            try {
                    $result = $duktape->eval($javascript);
            }
            catch (Exception $e) {
                    return null;
            }
            return $result;
    }

The parameters must be passed as an array of name/value pairs, e.g. passing ['a'=>42] makes a variable with the name 'a' visible to the javascript, with the value 42. $code contains a javascript function body, without braces around it, which must end with a return statement.

Hope this helps!

Kapsonfire
  • 1,013
  • 5
  • 17
braindigitalis
  • 524
  • 7
  • 19
  • Also its important to note duktape is a really nice library however it is quite simplistic and primitive by design. This makes it ideal for this solution as you know there isn't any simple way someone might for example make use of a filesystem call or fetch a webpage. When it errors however it only ever throws a generic Exception object, there really isn't any detail, so you should probably carefully check your formulas before using them just to prevent needless confusion. – braindigitalis Jul 18 '18 at 10:53
0

Wow, what a really inefficient way to write your own 'eval()' function. But useful for getting persistence on compromised systems. Perhaps this is the reason for the function being deprecated?

If the problem you are trying to solve is your test case, then either use eval (with validated inputs - /^[^0-9 +-*\/\(\)]$/ ) or use a lib specifically designed for resolving such expressions.

If you are not trying to implement polymorphic code, then anonymous functions are straight forward (which coincidentally implements polymorphic code!):

$compute = function () use ($arg) {
      if (preg_match('/^[^0-9 +-*\/\(\)]$/', $arg)) {
           return eval('return '.$arg . ';');
      }
      return false;
  };
symcbean
  • 47,736
  • 6
  • 59
  • 94
  • The code you have written above **IS EVAL** and you've not understood why you were told not to use eval() – symcbean Jul 18 '18 at 11:25
  • ...and you also never mentioned the constraint in your question - although your code effectively demonstrates why its a stupid one. – symcbean Jul 18 '18 at 11:28
  • Please explain, character-by-character, what you think your regex pattern is doing and why you chose to write it how you have. – mickmackusa Jul 18 '18 at 11:40
  • @mickmackuse: No. If you think my code is exploitable please provide your evidence. – symcbean Jul 19 '18 at 11:13