Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying not to use eval() in php to evaluate a string with a condition statement

I'm creating a validator that will be able to handle complex conditions by allowing the dev to use a conditional statement in the conditions rules.

Take this example with a set of rules:

...
"element_name":{
     "required": "conditional",
     "conditions" : {
         "requirements" : "(4 < 5)",
         "requirements" : "('something' == 'something_else')" 
     }
}
...

what the PHP will then do is loop through those requirements and evaluate them as code to return a boolean which will determine if the element is required or not.

The problem with using the eval() function is quite obvious. So I'm asking, given that condition statements are what will only be allowed, is there a safer way of doing this than:

$result = eval(element_name->requirements[0]);

Thank you guys.

---- UPDATE -----

Thank you Mike and everyone for the ideas, I wish I could mark you all as the answer because to be quite honest, I ended up using a little bit of everyone's idea. More of Mike's so he gets it.

So, this is probably something that is going to be looked at in the future because it's quite an attractive method of conditionally validating a field. My goal is to create an intuitive way of going about this. I liked the idea of simply slapping in a conditional statement right in the json config file. Of course, this would involve some serious security risks or a super complex parsing engine, so I ended requiring the dev to learn our method of conditional language, but as you will see, I kept it quite similar to the original one. I think it's important to have simple APIs otherwise you'll deter development on your platform. Check it out:

"element_name":{
     "required": "conditional",
     "conditions" : {
         "<" : ['4', '5'],
         "==" : [':element_id', ':other_element'], // will grab the values of those elements and compare
         "exp" : [['something', '==', 'something_else'], 'OR', [':other_element', '!=', '0']] 
     }
}
like image 560
Philll_t Avatar asked Jul 26 '13 00:07

Philll_t


People also ask

When not to use eval function in PHP?

Never ever use eval function in PHP ! In some applications, we need to let user defines the expression (user lambda function) to evaluate a value or a condition. So we store user-defined expression in database then using eval function and pass variables to that function. It works, but not securely ! Remember this important thing :

How do I evaluate a string in PHP?

The eval () function evaluates a string as PHP code. The string must be valid PHP code and must end with semicolon. Note: A return statement will terminate the evaluation of the string immediately. Tip: This function can be useful for storing PHP code in a database. Required.

How to catch parse error in Eval() in PHP 7?

As of PHP 7, if there is a parse error in the evaluated code, eval()throws a ParseError exception. Before PHP 7, in this case eval()returned falseand execution of the following code continued normally. It is not possible to catch a parse error in eval()using set_error_handler().

Why is the eval () language construct dangerous in PHP?

The eval () language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged. If you have carefully verified that there is no other option than to use this construct, pay special attention not to pass any user provided data into it without properly validating it beforehand.


1 Answers

To expand on dethtron5000's answer, one way I thought of doing this to prevent a ridiculously complex regex is to have the dev break up his conditionals into more of a multi-dimensional conditional thing and loop over it using a recursive function. At each level, you would have an "operator", which would be either "AND" or "OR" (at least I hope this is called an "operator", if not feel free to change it).

In your example you have: (32 < 40 AND 10 > 5 OR 20 == 10)

(It looks like you're json_encoding the conditionals, so I started with the following PHP array and worked backwards from there. I'm assuming you can just json_decode what your dev provides you to get a valid PHP array). The above example would be represented as the following PHP array:

$arr = array(
    'required' => 'conditional',
    'conditions' => array(
        'requirements' => array(
            'operator' => 'OR', // this makes it so that if any conditions at this level are true, it returns true
            0 => array(
                'operator' => 'AND', // this makes it so that all the conditions at this sub-level need to be satisfied to return true
                array(
                    'conditional1' => 32,
                    'conditional2' => 40,
                    'operation' => 'lessthan',
                ),
                array(
                    'conditional1' => 10,
                    'conditional2' => 5,
                    'operation' => 'greaterthan',
                ),
            ),
            1 => array(
                // Since there is only one condition here, it is not necessary to specify "AND" or "OR"
                array(
                    'conditional1' => 20,
                    'conditional2' => 10,
                    'operation' => 'equals',
                ),
            ),
        ),
    ),
);

You can then loop through the conditionals with a recursive function like this:

function check_req(Array $reqs) {
    $operator = (isset($reqs['operator'])) ? $reqs['operator'] : 'AND';
    unset($reqs['operator']);
    foreach ($reqs as $req) {
        if (isset($req['operation'])) {
            switch ($req['operation']) {
                case 'lessthan':
                    $valids[] = $req['conditional1'] < $req['conditional2'];
                    break;
                case 'greaterthan':
                    $valids[] = $req['conditional1'] > $req['conditional2'];
                    break;
                case 'equals':
                    $valids[] = $req['conditional1'] == $req['conditional2'];
                    break;
            }
        }
        else {
            $valids[] = check_req($req);
        }
    }
    if ($operator == 'OR') {
        foreach ($valids as $valid) {
            if ($valid == true) {
                return true;
            }
        }
        return false;
    }
    else {
        foreach ($valids as $valid) {
            if ($valid == false) {
                return false;
            }
        }
        return true;
    }
}

var_dump(check_req($arr['conditions']['requirements'])); // true in this case

When I json_encode it, I get:

{
    "required":"conditional",
    "conditions":{
        "requirements":{
            "operator":"OR",
            "0":{
                "operator":"AND",
                "0":{
                    "conditional1":32,
                    "conditional2":40,
                    "operation":"lessthan"
                },
                "1":{
                    "conditional1":10,
                    "conditional2":5,
                    "operation":"greaterthan"
                }
            },
            "1":[{
                "conditional1":20,
                "conditional2":10,
                "operation":"equals"
            }]
        }
    }
}

I'm assuming this is what the dev would have to provide you.

like image 160
Mike Avatar answered Oct 20 '22 00:10

Mike