treyBake
treyBake

Reputation: 6560

Evaluate string as condition PHP

I have a custom validation rule module that essentially allows users to set up CSV validation. My problem is I get it to this array:

Array(
    [field_name] => 'is_int(324230435)',
    [some_other_field] => 'strlen("some str") > 25'
)

I did some research and came across the eval() function.

refs: How to use string in IF condition in PHP

However, I really don't want to use eval() due to the security issues (ref: When is eval evil in php?)

Although it doesn't strictly say eval is evil, I still would prefer if there was an alternative method.

Am I being over-cautious about the usage of eval() - perhaps I should escape and use eval() or is there a better way?

Upvotes: 2

Views: 2079

Answers (2)

treyBake
treyBake

Reputation: 6560

Just going off of @deceze answer and suggestion to use Symfony's ExpressionLanguage Component.

I installed it to my project via Composer and thought for anyone stumbling across the post it might be helpful to see it working (and in relation to my question):

# build array for testing rows against rules
$test = [];

# foreach csv row
foreach ($csv as $keey => $row)
{
    # 10000s of rows, just for simplicity - break after 3
    if ($keey == 0) {continue;}
    if ($keey >= 3) {continue;}

    # get array keys for 
    $keys = array_keys($row);

    foreach ($keys as $key)
    {
        # if row key is in the $conditions array, add to $test array for testing
        if (in_array($key, array_map('strtolower', array_keys($conditions)))) {
            $conditionType = array_keys($conditions[$key]);
            $conditionType = $conditionType[0];

            if ($conditionType === 'condition_suffix') {
                $brokenCondition = explode(' ', $conditions[$key][$conditionType]);

                # build array to pass into ->evaluate()
                $test[$key]['evaluate'] = 'field '. $brokenCondition[0] .' required'; # expression to actually test
                $test[$key]['pass'] = [ # works like pdo, pass in the names and give them a value
                    'field' => strlen($row[$key]),
                    'required' => $brokenCondition[1]
                ];
            } else {
                $test[$key]['evaluate'] = 'field == required';
                $test[$key]['pass'] = [
                    'field' => is_numeric($row[$key]),
                    'required' => true
                ];
            }
        }
    }
}

echo '#----------------------------------------------------------------------------#';

# show test arr for reference
echo '<pre>';
print_r($test);
echo '</pre>';

# foreach test row, check against the condition
foreach ($test as $key => $item)
{
    echo '<pre>';
    var_dump($key. ': ' .$expressionLanguage->evaluate(
        $item['evaluate'],
        $item['pass']
    ));
    echo '</pre>';

    echo '+----------------------------------------------------------------------------+';
}

This now evaluates my custom created php query strings via the ExpressionLanguage Symfony component. Thanks @deceze

refs:

https://symfony.com/doc/current/components/expression_language/syntax.html

https://symfony.com/doc/current/components/expression_language.html

Upvotes: 0

deceze
deceze

Reputation: 522005

Well, executing arbitrary strings as code has the caveat that you're executing arbitrary code whichever way you do it. There's no better alternative to eval that would let you execute PHP code without… executing PHP code.

The sane way to go here is to define a DSL which gives your users a way to write certain limited expressions which are not PHP code, which you will parse and evaluate with specific limited capabilities.

A good library which does that is Symfony's ExpressionLanguage component. Beyond that you'd go into the domain of language parsers.

Upvotes: 3

Related Questions