评估存储在数组中的PHP逻辑

6

目标

我想将复杂的比较操作存储为PHP数组。然后,我想读取该数组来评估一组变量,并返回语句是真还是假。

问题

如何存储复杂的比较操作,以便稍后可以读取并评估此时存在的变量?这是实现这个功能的最佳方式吗?非常感谢您的帮助。

英文示例

如果($source = 200 OR $source = 300) AND ($age < 10 OR $age > 60),则返回true,否则返回false。

注:变量可以是代码中的任何变量,我在这里使用source和age作为示例。嵌套数组可以比示例更深或更浅。

代码示例

$conditions = Array
(
[relation] => AND
[0] => Array
    (
        [relation] => OR
        [0] => Array
            (
                [var] => source
                [compare] => =
                [value] => 300
            )

        [1] => Array
            (
                [var] => source
                [compare] => =
                [value] => 300
            )

    )

[1] => Array
    (
        [relation] => OR
        [0] => Array
            (
                [var] => age
                [compare] => <
                [value] => 10
            )

        [1] => Array
            (
                [var] => age
                [compare] => >
                [value] => 60
            )

    )

解决方案

根据@sam-grondahl提供的答案,该函数能够处理您可以想到的任何嵌套逻辑。 接受正则表达式和数字比较。

$source = '300';
$age = '60';

$input_variables = array('source' => $source, 'age' => $age);

$conditions = array(
                'relation' => 'and',
                array(
                        'relation' => 'and',
                        array(
                              'var' => 'source',
                              'compare' => 'regex',
                              'value' => '(200|300)+'
                              ),
                        array(
                              'relation' => 'OR',
                                array(
                                      'var' => 'age',
                                      'compare' => '=',
                                      'value' => '59'
                                      ),
                                array(
                                      'var' => 'age',
                                      'compare' => '=',
                                      'value' => '60'
                                      )
                                      )
                      ),
                array(
                        'relation' => 'or',
                        array(
                              'var' => 'age',
                              'compare' => '<',
                              'value' => '10'                                 
                              ),
                        array(
                              'var' => 'age',
                              'compare' => '>=',
                              'value' => '60'
                              )
                      ),
                     array(
                              'var' => 'source',
                              'compare' => '=',
                              'value' => '300'
                              ),

                );


function evaluateConditions($condArray, $varablesArray){

if(!$condArray || !is_array($condArray)){throw new Exception('Missing Conditions Input Array');}
if(!$varablesArray || !is_array($varablesArray)){throw new Exception('Missing Variables Input Array');}

//PULL VARIABLES FROM ARRAY
foreach($varablesArray as $key_var => $value_var){
    ${$key_var} = $value_var;
}


///PROCESS THE ARRAY THAT CONTAINS THE COMPARISON ARRAY
if (!$condArray['relation']){

 //FORMAT COMPARE INPUT
 $condArray['compare'] = strtoupper(trim($condArray['compare']));
 $condArray['compare'] = preg_replace('/^=$/','==',$condArray['compare']);

 ///ACCEPTED COMPARE VALUES
 $accepted_compare_array = array('=','==','===','<>','!=','!==','>','>=','<','<=','REGEX');

 //VERIFY REQUIRED FIELDS ARE PRESENT
 if(!$condArray['var']){throw new Exception('Missing var');}
 if(!$condArray['compare']){throw new Exception('Missing compare');}
 if(!$condArray['value']){throw new Exception('Missing value');}

 //VERIFY ALL VALUES
 if($condArray['compare'] != 'REGEX' && !is_numeric($condArray['value'])){throw new Exception('Value is Not Numeric');}
 if(!in_array($condArray['compare'], $accepted_compare_array)){throw new Exception('Invalid Compare Value');}
 if(!preg_match("/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/",$condArray['var'])){throw new Exception('Invalid Var');}

 if($condArray['compare'] == 'REGEX'){
    $condArray['compare'] = preg_replace('/\//','\/',$condArray['compare']);
    $eval_str = 'if(preg_match("/'.$condArray['value'].'/i", $'.$condArray['var'].')){return 1;} else{return 0;}'; 
 }
 else{   
    $eval_str = 'if($'.$condArray['var'].' '.$condArray['compare'].' '.$condArray['value'].'){return 1;} else{return 0;}';
 }

 return eval($eval_str);

 }
else{

 $condArray['relation'] = strtoupper(trim($condArray['relation']));

 //VERIFY RELATION VALUE
 $accepted_relation_array = array('AND','OR');
 if(!in_array($condArray['relation'], $accepted_relation_array)){throw new Exception('Relation Invalid');}

 //VERIFY THAT AT LEAST TWO ARRAYS
 $array_count = 0;
 foreach($condArray as $condArray_var){
    if(is_array($condArray_var)){$array_count++;} 
 }

 if($array_count < 2){throw new Exception('Missing Comparison Array');}

 }



if (strtoupper($condArray['relation']) == 'OR') {
 unset($condArray['relation']);

 $eval_str = 'if(';
 for($i=0; $i < count($condArray); $i++){
    $eval_str .= evaluateConditions($condArray[$i], $varablesArray);
    if($i != count($condArray)-1){$eval_str .= ' || ';}  
 }   
 $eval_str .= '){return 1;}else{return 0;}';

 return eval($eval_str);

 }
 if (strtoupper($condArray['relation']) == 'AND'){
 unset($condArray['relation']);
 $eval_str = 'if(';
 for($i=0; $i < count($condArray); $i++){
    $eval_str .= evaluateConditions($condArray[$i], $varablesArray);
    if($i != count($condArray)-1){$eval_str .= ' && ';}  
 }   
 $eval_str .= '){return 1;}else{return 0;}';

 return eval($eval_str);

 }
throw new Exception('General Error Occurred');
} //end of function

/////

try {
echo evaluateConditions($conditions, $input_variables);
} 
catch (Exception $e) {
echo 'Caught exception: ',  $e->getMessage(), "\n";
}   

1
全局变量是有害的。不要使用它们! - Vlad Balmos
也许你可以重新考虑你的逻辑,因为处理问题可能还有其他方法。 - Nin
解决方案不再使用全局变量。感谢您的建议。 - BDS1400
1个回答

5

处理这个问题的最简单方法是使用PHP的eval()方法进行递归处理(http://php.net/manual/en/function.eval.php):

static function evaluateArray($inArray)
{
  if (!$inArray['relation']) return eval($inArray['var'] + $inArray['compare'] + $inArray['value']);
  if ($inArray['relation'] == 'OR') return eval(evaluateArray($inArray[0])+'||'+evaluateArray($inArray[1]));
  if ($inArray['relation'] == 'AND') return eval(evaluateArray($inArray[0])+'&&'+evaluateArray($inArray[1]));
  return false; //could throw exception here if you wanted
}

这个代码是为字符串设计的,但可以很容易地进行修改。此外,您应该检查每个子数组是否正确结构化,但我已经省略了此代码。


谢谢回复。我不确定它是否有效,因为eval()中的$inArray[0]可能是另一组嵌套数组。也许我只需要将结构限制在一级。 - BDS1400
抱歉,忘记包含递归 :0。但现在已经有了,所以这应该适用于任何嵌套程度(只要函数堆栈不溢出)。 - Sam Grondahl
1
看起来你在 $inArray 变量中打错了一个字母(多了一个'r')。有几个语法问题阻止了 PHP 解决方案的完美运行。但是你的逻辑是正确的!我已经在上面发布了我的完整解决方案。 - BDS1400

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接