我对用户提供的变量名称有<
和>
个数组:
$constraints = array('1<x','x<5','y>4');
在相关范围内定义了$x
和$y
。
我想验证所有约束是否成立(返回true
或false
)
如何在不使用eval
的情况下执行此操作?
答案 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]
查找数组中的值。