PHP验证约束列表

时间:2016-12-16 21:03:40

标签: php expression eval

我对用户提供的变量名称有<>个数组:

$constraints = array('1<x','x<5','y>4');

在相关范围内定义了$x$y

我想验证所有约束是否成立(返回truefalse

如何在不使用eval的情况下执行此操作?

4 个答案:

答案 0 :(得分:4)

我在这里编造了一个部分答案。它不会循环,但它确实支持五个不同的比较运算符。

function lt($p1, $p2) {
    return ($p1 < $p2);
}
function le($p1, $p2) {
    return ($p1 <= $p2);
}

function gt($p1, $p2) {
    return ($p1 > $p2);
}
function ge($p1, $p2) {
    return ($p1 >= $p2);
}
function eq($p1, $pw) {
    return ($p1 == $p2);
}

function apply_rule($rule, $x, $y) {
    $matches = NULL;
    if (!preg_match('/^([a-zA-Z0-9]+)(<|>|=|<=|>=)([a-zA-Z0-9]+)$/', $rule, $matches)) {
        throw new Exception("Invalid rule: " . $rule);
    }
    //var_dump($matches);
    $p1 = $matches[1];
    $operator = $matches[2];
    $p2 = $matches[3];

    // check if first param is a variable
    if (preg_match('/([a-zA-Z]+)/', $p1)) {
        $p1 = $$p1;
    }
    // check if second param is a variable
    if (preg_match('/([a-zA-Z]+)/', $p2)) {
        $p2 = $$p2;
    }

    switch($operator) {
        case "<":
            return lt($p1, $p2);
        case "<=":
            return le($p1, $p2);
        case ">":
            return gt($p1, $p2);
        case ">=":
            return ge($p1, $p2);
        case "=":
            return eq($p1, $p2);
    }
}

var_dump(apply_rule("x>=10", 10, 20));

答案 1 :(得分:1)

很多人都知道NoSuchElementException中的$符号实际上是一个评估变量的运算符。

php
只有当所有约束都成立时,

$total_expressions = true; foreach($constraints as $c) { #parse the expression in to the number, and the variable $parts = explode(">",str_replace("<",">",$c)); $i = is_numeric($parts[0]) ? 0 : 1 ; $n = $parts[$i]; $v = $parts[1-$i]; # At this stage, $v is the variable name, and $n is the number # This line is kinda hard coded to only ">" or "<", but you get the idea $expression = strpos(">",$c) && $i ? $$v > $n : $$v < $n; $total_expressions = $total_expressions && $expression; if (!$total_expressions) break; } 才会成立。

答案 2 :(得分:1)

如果您只是想知道所有约束都有效,您可以将它们传递给执行检查的函数。它可以使用foreach循环逐个检查每个约束。如果当前约束无效,则它将返回false并停止检查。否则,如果它到达循环的末尾,它将返回true。变量的值作为str_replace()中使用的两个数组传递给函数。

function validate($constraints, $search, $replace) {

    foreach ($constraints as $constraint) {

        // replace variables in string with submitted values
        $constraint = str_replace($search, $replace, $constraint);

        if (strpos($constraint, '<') !== false) {
            // extract parts from less than constraint
            list($a, $b) = explode('<', $constraint, 2);
            if ($a >= $b) {
                // $a is greater than or equal to $b i.e. not less than
                return false;
            }
        } else if (strpos($constraint, '>') !== false) {
            // extract parts from greater than constraint
            list($a, $b) = explode('>', $constraint, 2);
            if ($a <= $b) {
                // $a is less than or equal to $b i.e. not greater than
                return false;
            }
        }
    }
    // no invalid constraints were found...
    return true;
}

然后,您可以使用它来检查$constraints数组,

// variables to search for
$search = ['x', 'y'];

// variable replacements
$replace = [5, 2];

// constraints array
$constraints = array('4<x','x<6','y>1');

// run the function
var_dump(validate($constraints, $search, $replace));

该函数确实假设数据完全按照您的描述传递给它。如果数据格式可能不同,您可能需要添加一些检查。

答案 3 :(得分:0)

如果你只需要评估简单的表达式,并且事先知道变量的数量和名称,那么你可以编写一个简单的解析器:

/**
 * Parse and evaluate a simple comparison.
 *
 * @param string  $condition e.g. 'x<4'
 * @param integer $x         the value of 'x'
 * @param integer $y         the value of 'y'
 */
function compare($condition, $x, $y)
{
    // Verify that the condition uses the format accepted by this function
    // Also extract the pieces in $m
    $m = array();
    if (! preg_match('/^(x|y|\d+)([<>])(x|y|\d+)$/', $condition, $m)) {
        throw new RuntimeException("Cannot parse the condition");
    }

    // $m[0] is the entire string that matched the expression
    // $m[1] and $m[3] are the operands (the first and the third groups)
    // $m[2] is the operator (the second group in the regex)

    // Replace the variables with their values in $m[1] and $m[3]
    foreach (array(1, 3) as $i) {
        switch ($m[$i]) {
        case 'x':
            $m[$i] = $x;
            break;
        case 'y':
            $m[$i] = $y;
            break;
        default:
            $m[$i] = (int)$m[$i];
            break;
        }
    }

    // Compare the values, return a boolean
    return ($m[2] == '<') ? ($m[1] < $m[3]) : ($m[1] > $m[3]);
}

// A simple test
$x = 7;
$y = 3;
echo('$x='.$x."\n");
echo('$y='.$y."\n");

echo('1<x: '.(compare('1<x', $x, $y) ? 'TRUE' : 'FALSE')."\n");
echo('x<5: '.(compare('x<5', $x, $y) ? 'TRUE' : 'FALSE')."\n");
echo('y>4: '.(compare('y>4', $x, $y) ? 'TRUE' : 'FALSE')."\n");

代码使用整数值。要使其与浮点值一起使用,只需将(int)替换为(double)语句的default分支上的switch

正则表达式:

^               # match the beginning of the string
(               # start capturing group #1
  x               # match the 'x' character 
  |y              # ... OR (|) the 'y' character
  |\d+            # ... OR (|) a sequence of 1 or more (+) digits (\d)
)               # end capturing group #1     <-- find the captured value in $m[1]
(               # start capturing group #2
  [               # match any character from the range
    <>              # match '<' or '>'
  ]               # end range
)               # end capturing group #2     <-- find the captured value in $m[2]
(x|y|\d+)       # the capturing group #3, identical to group #1
$               # match the end of the string

通过简单的更改,上面的代码可以调整为允许<=>==(更改正则表达式)或事先未知的变量列表(通过数组中的变量由其名称索引,使用$m[$i]查找数组中的值。