Pramodh Kampalli
Pramodh Kampalli

Reputation: 21

How to evaluate a string in IF?

I've a string that contains a condition like "$a==1||$b==2||$c==3".

What should I do to evaluate this string (as a condition) using IF statement?

Upvotes: 1

Views: 123

Answers (4)

VolkerK
VolkerK

Reputation: 96159

Instead of using eval() - which in your context will probably be a nightmare in regard of limiting the expression to only what you want to/should allow - you can create a parser/lexer that can only handle those operations you want.
You can e.g. take a look at the pear packages PHP_LexerGenerator and PHP_ParserGenerator. Or search stackoverflow for answers regarding [php] parser.


And just for fun:
PHP 7 introduced an Abstract Syntax Tree layer to its compilation process.
The extension at https://github.com/nikic/php-ast exposes this AST layer to the userland script. Using this [a) limited to php7+, b) unclear whether this extension will be maintained in the future, I just found it via google ] you can set up an interpreter like e.g.

<?php
$data = [
    ['a'=>5, 'b'=>6, 'c'=>7],
    ['a'=>5, 'b'=>6, 'c'=>3],
    ['a'=>5, 'b'=>2, 'c'=>7],
    ['a'=>1, 'b'=>6, 'c'=>7],
    ['a'=>1, 'b'=>2, 'c'=>7],
    ['a'=>7, 'b'=>2, 'c'=>3],
];
$expressions = [
    '$a==1||$b==2||$c==3',
    '($a==1&&$b==2)||$c==3',
    '$a==1&&($b==2||$c==3)'
];

foreach( $expressions as $x ) {
    $ep = new ExpressionParser($x);
    echo $x, "\r\n";
    foreach( $data as $scope ) {
        $result = $ep->eval( $scope );
        printf(
            "   %s => %s\r\n", 
            compact_var_export($scope),
            compact_var_export($result)
        );
    }
}

class ExpressionParser {
    public function __construct($expr) {
        $ast = ast\parse_code('<?php '.$expr.';', $version=20);
        if (!$ast || ast\AST_STMT_LIST!=$ast->kind ) {
            throw new InvalidArgumentException('unable to parse expression');
        }
        else if ( 1!==count($ast->children) || ast\AST_BINARY_OP!==$ast->children[0]->kind ) {
            throw new InvalidArgumentException('only one binary expression allowed');
        }
        else {
            $this->ast = $ast->children[0];
        }
    }

    public function eval($scope) {
        return evalOp($scope, $this->ast);
    }
}

function evalOp($scope, $ast) {
    static $handler = null;

    if ( is_null($handler) ) {
        $handler = [
            ast\AST_UNARY_OP => [
                ast\flags\UNARY_BOOL_NOT    => function($scope, $c) { return !evalOp($scope, $c[0]); },
                ast\flags\UNARY_BITWISE_NOT => function($scope, $c) { return ~evalOp($scope, $c[0]); },
                ast\flags\UNARY_MINUS       => function($scope, $c) { return -evalOp($scope, $c[0]); },
            ],

            ast\AST_BINARY_OP => [
                ast\flags\BINARY_BOOL_AND            => function($scope, $c) { return evalOp($scope, $c[0]) && evalOp($scope, $c[1]); },
                ast\flags\BINARY_BOOL_OR             => function($scope, $c) { return evalOp($scope, $c[0]) || evalOp($scope, $c[1]); },
                ast\flags\BINARY_BOOL_XOR            => function($scope, $c) { return evalOp($scope, $c[0]) ^ evalOp($scope, $c[1]); },
                ast\flags\BINARY_IS_IDENTICAL        => function($scope, $c) { return evalOp($scope, $c[0]) === evalOp($scope, $c[1]); },
                ast\flags\BINARY_IS_NOT_IDENTICAL    => function($scope, $c) { return evalOp($scope, $c[0]) !== evalOp($scope, $c[1]); },
                ast\flags\BINARY_IS_EQUAL            => function($scope, $c) { return evalOp($scope, $c[0]) == evalOp($scope, $c[1]); },
                ast\flags\BINARY_IS_NOT_EQUAL        => function($scope, $c) { return evalOp($scope, $c[0]) != evalOp($scope, $c[1]); },
                ast\flags\BINARY_IS_SMALLER          => function($scope, $c) { return evalOp($scope, $c[0]) < evalOp($scope, $c[1]); },
                ast\flags\BINARY_IS_SMALLER_OR_EQUAL => function($scope, $c) { return evalOp($scope, $c[0]) <= evalOp($scope, $c[1]); },
                ast\flags\BINARY_IS_GREATER          => function($scope, $c) { return evalOp($scope, $c[0]) > evalOp($scope, $c[1]); },
                ast\flags\BINARY_IS_GREATER_OR_EQUAL => function($scope, $c) { return evalOp($scope, $c[0]) >= evalOp($scope, $c[1]); },
            ],

            ast\AST_VAR => function($scope, $a) {
                if ( !isset($scope[$a->children[0]]) ) {
                    throw new InvalidArgumentException('undefined variable: '.$a->children[0]);
                }
                else {
                    return $scope[$a->children[0]];
                }
            }
        ];
    }

    if ( !is_object($ast) ) {
        return $ast;
    }
    else if ( !isset($handler[$ast->kind]) ) {
        throw new InvalidArgumentException('unsupported operation type');
    }
    else if ( is_array($handler[$ast->kind]) ) {
        if ( !isset($handler[$ast->kind][$ast->flags]) ) {
            throw new InvalidArgumentException('unsupported operation');
        }
        else {
            return $handler[$ast->kind][$ast->flags]($scope, $ast->children);
        }
    }
    else {
        return $handler[$ast->kind]($scope, $ast);
    }
}

function compact_var_export($x) {
    return preg_replace('!\s+!m', ' ', var_export($x, true));
}

The output is

$a==1||$b==2||$c==3
    array ( 'a' => 5, 'b' => 6, 'c' => 7, ) => false
    array ( 'a' => 5, 'b' => 6, 'c' => 3, ) => true
    array ( 'a' => 5, 'b' => 2, 'c' => 7, ) => true
    array ( 'a' => 1, 'b' => 6, 'c' => 7, ) => true
    array ( 'a' => 1, 'b' => 2, 'c' => 7, ) => true
    array ( 'a' => 7, 'b' => 2, 'c' => 3, ) => true
($a==1&&$b==2)||$c==3
    array ( 'a' => 5, 'b' => 6, 'c' => 7, ) => false
    array ( 'a' => 5, 'b' => 6, 'c' => 3, ) => true
    array ( 'a' => 5, 'b' => 2, 'c' => 7, ) => false
    array ( 'a' => 1, 'b' => 6, 'c' => 7, ) => false
    array ( 'a' => 1, 'b' => 2, 'c' => 7, ) => true
    array ( 'a' => 7, 'b' => 2, 'c' => 3, ) => true
$a==1&&($b==2||$c==3)
    array ( 'a' => 5, 'b' => 6, 'c' => 7, ) => false
    array ( 'a' => 5, 'b' => 6, 'c' => 3, ) => false
    array ( 'a' => 5, 'b' => 2, 'c' => 7, ) => false
    array ( 'a' => 1, 'b' => 6, 'c' => 7, ) => false
    array ( 'a' => 1, 'b' => 2, 'c' => 7, ) => true
    array ( 'a' => 7, 'b' => 2, 'c' => 3, ) => false

( I'm not really good at this. Therefore experts might find this clumsy, faulty or even plain wrong; feel free to comment ;-) )

Upvotes: 0

dagamo
dagamo

Reputation: 25

Simple and easy. try this

<?php

   $a="yes";
   $b="ok";
   $c="yes";

   if(($a=='yes') || ($b=='ok') || ($c=='yes')){
   echo "If a=yes or b=ok or c=yes you got this";
   } else {
   echo "You got this, because a!=yes or b!=ok or c!=yes";
   }
?>

Upvotes: 0

Tim Hysniu
Tim Hysniu

Reputation: 1540

To evaluate this condition you can use eval(). For example:

$a=1; $b=33; $c=1;

$condition = '$a==1||$b==2||$c==3';
eval('$isTrue = ' . $condition . ';');
echo intval($isTrue); // should output 1

You want to be careful with eval(). I wouldn't recommend using it unless you really have to AND you're confident that $condition is not something anyone can tamper with.

Upvotes: 0

Angelo Ber&#231;acola
Angelo Ber&#231;acola

Reputation: 173

You can use Eval function to evaluate it, but first, you need to concatenate some code:

$yourString = "$a==1||$b==2||$c==3";
$evalString = "if(".$yourString .")return true;";
$evaluation = eval($evalString);

in this case above, if any of terms are true, will return true.

OBS: the variables $a,$b,$c should be declared before the execution.

Upvotes: 1

Related Questions