PHP 验证约束列表
PHP validate a list of constraints
我有一个数组 <
和 >
对我从用户那里得到的变量名称的约束:
$constraints = array('1<x','x<5','y>4');
其中 $x
和 $y
在相关范围内定义。
我想验证 所有 约束是否成立(return true
或 false
)
如何不使用 eval
来执行此操作?
很多人确实知道 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;
}
$total_expressions
仅当所有约束都成立时才为真。
如果您只想知道所有 约束是否有效,您可以将它们传递给执行检查的函数。它可以使用 foreach
循环逐一检查每个约束。如果当前约束无效,那么它将 return false
并停止检查。否则,如果它到达循环的末尾,它将 return 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));
该函数假定数据完全按照您的描述传递给它。如果数据格式可能不同,您可能需要添加一些检查。
我在这里炮制了部分答案。它不循环,但支持五种不同的比较运算符。
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));
如果您只需要计算简单的表达式,并且您事先知道变量的数量和名称,那么您可以编写一个简单的解析器:
/**
* 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");
该代码适用于整数值。要使其与浮点值一起使用,只需在 switch
语句的 default
分支上将 (int)
替换为 (double)
。
正则表达式:
^ # 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]
查找数组中的值)。
我有一个数组 <
和 >
对我从用户那里得到的变量名称的约束:
$constraints = array('1<x','x<5','y>4');
其中 $x
和 $y
在相关范围内定义。
我想验证 所有 约束是否成立(return true
或 false
)
如何不使用 eval
来执行此操作?
很多人确实知道 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;
}
$total_expressions
仅当所有约束都成立时才为真。
如果您只想知道所有 约束是否有效,您可以将它们传递给执行检查的函数。它可以使用 foreach
循环逐一检查每个约束。如果当前约束无效,那么它将 return false
并停止检查。否则,如果它到达循环的末尾,它将 return 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));
该函数假定数据完全按照您的描述传递给它。如果数据格式可能不同,您可能需要添加一些检查。
我在这里炮制了部分答案。它不循环,但支持五种不同的比较运算符。
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));
如果您只需要计算简单的表达式,并且您事先知道变量的数量和名称,那么您可以编写一个简单的解析器:
/**
* 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");
该代码适用于整数值。要使其与浮点值一起使用,只需在 switch
语句的 default
分支上将 (int)
替换为 (double)
。
正则表达式:
^ # 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]
查找数组中的值)。