eval() 替代方法来获取字符串的输出,例如“(0 AND 1 OR 1)”

eval() alternative to get output of the string like "(0 AND 1 OR 1)"

我有来自数据库的字符串格式的逻辑表达式数据 -

"(id1 OR id2) AND (id3 OR id3)"

这可以是任何类型的组合,因为它是基于用户输入的动态字符串。 从数据库中获取此信息后,我根据某种逻辑将所有 ID 值替换为 0/1,此字符串变为 -

"(1 OR 1) AND (0 OR 1)"

现在我想 运行 这个字符串作为 PHP 代码并期望它的输出为布尔值。但问题是我使用了 eval() PHP 功能,出于安全原因,该功能并不好。

$op = "(1 OR 1) AND (0 OR 1)";
$op = eval('return '.$op.';');

我不得不使用 eval 的唯一原因是字符串类型。如果我 运行 这个 echo (1 OR 1) AND (0 OR 1) 那么我得到预期的输出。

有人可以帮我解决这个问题吗?对此有什么更好的方法?

单数格式

如果它始终采用相同的格式,您可以使用正则表达式并格式化您自己的表达式...

if(preg_match("/\((\d) OR (\d)\) AND \((\d) OR (\d)\)/", $string, $matches))
    var_dump( ($matches[1] || $matches[2]) && ($matches[3] || $matches[4]));

多种格式

警告 如果格式不正确,下面的表达式不会捕捉到每个表达式。例如表达式:

AND 1

会导致两种方法都失败...您可以通过定义额外的匹配规则在正则表达式中解决这个问题,例如:

Pattern         Replacement

/^\s*AND\s*1/   0
/^\s*AND\s*0/   0
/^\s*OR\s*0/    0

/^\s*OR\s*1/    1

但要涵盖所有内容将意味着一长串规则。

示例表达式

$expressions  = [
    "1 AND 0",
    "1 AND 1",
    "0 OR 0",
    "0 OR 1",
    "1 OR 1",
    "0 AND (1 OR 1) AND (0 OR 0) OR 0",
    "(1 OR 1) AND (0 OR 1)",
    "(1 AND 0 OR 1 ) OR (0 OR 1 OR 1) AND 1",
    "(1 OR 0) DELETE EVERYTHING!"
];

方法一:eval

虽然人们倾向于讨厌使用 eval(因为它 可能 允许执行流氓代码)但没有理由不使用它,只要你清理你的输入。

所以让我们做一个简单的函数:

function cleanBooleanExpression($string){                
    $string = preg_replace("/OR/i", "O", $string);       // Replace instances of OR with the letter O; i flag makes it case insensitive
    $string = preg_replace("/AND/i", "A", $string);      // Replace instances of AND with the letter A; i flag makes it case insensitive
    $string = preg_replace("/[^OA01() ]/", "", $string);  // Strip all non-legal characters
    $string = preg_replace("/O/", "OR", $string);         // Re-instate OR
    $string = preg_replace("/A/", "AND", $string);        // Re-instate AND
    return $string;                                       // Return the updated string
}

然后您可以在 eval:

中使用经过清理的输出
foreach($expressions as $input){
    $input = cleanBooleanExpression($input);
    eval("var_dump({$input});");
}

/* Output:

bool(false)
bool(true)
bool(false)
bool(true)
bool(true)
bool(false)
bool(true)
bool(true)
bool(true)

*/

方法二:自定义函数

或者,如果您真的不想使用 eval,您可以使用正则表达式来评估输入:

function evalBooleanString($string){
    $string = preg_replace('/
        (
        \(1\)|
        [01]*1[01]+|
        [01]+1[01]*|
        \(*\s*1\s*OR\s*[01]\s*\)*|
        \(*\s*[01]\s*OR\s*1\s*\)*|
        \(*\s*1\s*AND\s*1\s*\)*
        )
        /x', 1, $string);
    $string = preg_replace('/
        (
        \(\)|
        \(0\)|
        00+|
        \(*\s*0\s*OR\s*0\s*\)*|
        \(*\s*[01]\s*AND\s*0\s*\)*|
        \(*\s*0\s*AND\s*[01]\s*\)*
        )
        /x', 0, $string);

    $string = trim($string);

    if($string === "1" || $string === "0"){
        return (bool)$string;
    }
    return evalBooleanString($string);
}

并像这样使用它:

foreach($expressions as $input){
    $input = cleanBooleanExpression($input);
    var_dump(evalBooleanString($input));
}

/* Output:

bool(false)
bool(true)
bool(false)
bool(true)
bool(true)
bool(false)
bool(true)
bool(true)
bool(true)

*/

也许有帮助

function tmp_function($body)
{
    $tmp_file = tempnam(sys_get_temp_dir(), "tmp_func");
    file_put_contents($tmp_file, "<?php return function() { return $body };");
    $function = include($tmp_file);
    unlink($tmp_file);

    return $function;
}

$op = "(1 OR 1) AND (0 OR 1);";
$call_me = tmp_function($op);
var_dump($call_me());