按正则表达式拆分自定义条件


Split a custom condition by regular expression

我正忙于编写一个后端模块,用户可以在其中创建自己的条件/规则。现在我想把一个条件分成几个部分。

例如:

条件: {variable}> 100

// desired output
Array
(
    [0] => {variable} > 100
)

:{变量}= = {anotherVariable}或{var} & lt; = 10

// desired output
Array
(
    [0] => {variable} == {anotherVariable}
    [1] => OR
    [2] => {var} <= 10
)

条件: {variable} == {anotherVariable} AND {var} <= 10 AND {variable} == some string

// desired output
Array
(
    [0] => {variable} == {anotherVariable}
    [1] => AND
    [2] => {var} <= 10
    [3] => AND
    [4] => {variable} == some string
)

之后,我必须将比较分成3部分:

比较:

{变量}= = {anotherVariable}

// desired output
// allowed comparison operators are: ==, !=, >, >=, <, <=, <>
Array
(
    [0] => {variable}               // comparison
    [1] => ==                       // comparision operator
    [2] => {anotherVariable}        // comparison
)

我现在挣扎了几个小时正则表达式,但没有导致所需的输出。

为了更简单,我假设字符串在双引号之间。

模式的第一部分用命名的子模式定义((?(DEFINE)...))所需的元素。首先是简单元素(比较运算符、布尔运算符、操作数),然后是更复杂的元素(原子条件、条件、检查)。每个子模式都可以与其他子模式及其本身一起定义。

一旦定义部分关闭,主模式开始并在每次迭代中捕获名为capture的"令牌"中的三个可能元素:一个布尔运算符、一个原子条件(即最可能的基本条件)或括号 中的整个条件。

要成功,主模式必须通过两个入口点之一,第一个(只使用一次)是子模式'g<check>。此子模式确保整个字符串从头到尾都是良好的,并且由于它是以'A开头的零宽度断言,因此强制令牌搜索从字符串的开头开始。第二个入口点是(?!'A)'G,表示"不在字符串的开头,与先前匹配相邻"。此条目用于所有其他匹配。

命名为'g<par>的capture仅供递归函数使用。如果捕获存在,则令牌是括号内的一个完整条件,需要解析以生成一个数组。

define('PATTERN', <<<'EOD'
~
(?(DEFINE)
  (?<comp_op> [=!]= | >=? | <[=>]? )
  (?<bool_op> OR | AND )
  (?<operand> { 'w+ } | -?'d+ | "[^"]+" ) # you can improve the " pattern
  (?<at_cond> 'g<operand> 's+ 'g<comp_op> 's+ 'g<operand> ) # atomic cond
  (?<cond> 'g<at_cond> (?> 's+ 'g<bool_op> 's+ 'g<cond> )* | '( 'g<cond> ') )
  (?<check> (?= 'A 's* 'g<cond> 's* 'z ) ) # checks the whole string format
)
(?: 'g<check> 's* | (?!'A)'G 's+)
(?| (?<token>'g<bool_op>) | ('g<at_cond>) | '( ('g<cond>) )
(?<par> ') )? # only to test if a recursion is needed
~x
EOD
);
function getTokens ($str) {
    if (preg_match_all(PATTERN, $str, $matches)) {
        $tokens = array(); 
        foreach ($matches['token'] as $k=>$token) {
            $tokens[] = ($matches['par'][$k]) ? getTokens($token) : $token;
        }
        return $tokens;
    }
    return false;
}
$testset = array(
    '{variable} > 100',
    '{variable} == {zogabu} OR {buga} <= 10',
    '{meuh} == {zo} AND ({meuh} <= 10 OR {zomeuh} == "GABUZO")',
    '{BU} > 3 AND ({ga} == "ZO" OR ({bu} == "GA" AND {meuh} != "GA"))' );
foreach($testset as $test_string) {
    $result = getTokens($test_string);    
    echo "'ntest_string: $test_string'n" . print_r($result, true);
}