使用 preg_split 作为模板引擎
Using preg_split for templating engine
我正在构建一个模板引擎并希望允许嵌套逻辑。
我需要使用“@”作为分隔符来拆分以下字符串,但我想忽略此分隔符 - 将其视为另一个字符 - 如果它位于 [方括号] 内。
这里是输入字符串:
@if(param1>=7) [ something here @if(param1>9)[ nested statement ] ] @elseif(param2==true) [ 2st condition true ] @else [ default condition ]
结果应如下所示:
array(
" if(param1>=7) [ something here @if(param1>9)[ nested statement ] ] ",
" elseif(param2==true) [ 2st condition true ] ",
" else [ default condition ] "
)
我相信 preg_split 是我正在寻找的,但可以使用正则表达式的帮助
要匹配嵌套括号,您需要使用递归模式。
(?:(\[(?:[^[\]]|(?1))*])|[^@[\]])+
它将匹配每个段,不包括前导 @
。它还会将最后一个括号捕获到组 1 中,您可以忽略它。
使用 preg_match
或 preg_match_all
的模式。
正则表达式:
@(?> if | else (?>if)? ) \s* # Match a control structure
(?> \( [^()]*+ \) \s*)? # Match statements inside parentheses (if any)
\[ # Match start of block
(?:
[^][@]*+ # Any character except `[`, `]` and `@`
(?> \.[^][@]* )* (@+ (?! (?> if | else (?>if)? ) ) )? # Match escaped or literal `@`s
| # Or
(?R) # Recurs whole pattern
)* # As much as possible
\]\K\s* # Match end of container block
PHP:
print_r(preg_split("~@(?>if|else(?>if)?)\s*(?>\([^()]*+\)\s*)?\[(?:[^][@]*+(?>\.[^][@]*)*(@+(?!(?>if|else(?>if)?)))?|(?R))*\]\K\s*~", $str, -1, PREG_SPLIT_NO_EMPTY));
输出:
Array
(
[0] => @if(param1>=7) [ something here @if(param1>9)[ nested statement ] ]
[1] => @elseif(param2==true) [ 2st condition true ]
[2] => @else [ default condition ]
)
感谢您的回复! Revo 的回答有效!
我自己无法想出正则表达式,而是构建了一个同样有效的解析器函数。也许它对某人有用:
function parse_context($subject) { $arr = array(); // array to return
$n = 0; // number of nested sets of brackets
$j = 0; // current index in the array to return, for convenience
$n_subj = strlen($subject);
for($i=0; $i<$n_subj; $i++){
switch($subject[$i]) {
case '[':
$n++;
$arr[$j] .= '[';
break;
case ']':
$n--;
$arr[$j] .= ']';
break;
case '@':
if ($n == 0) {
$j++;
$arr[$j] = '';
break;
}
default: $arr[$j].=$subject[$i];
}
}
return $arr;
} // parse_context()
我正在构建一个模板引擎并希望允许嵌套逻辑。
我需要使用“@”作为分隔符来拆分以下字符串,但我想忽略此分隔符 - 将其视为另一个字符 - 如果它位于 [方括号] 内。
这里是输入字符串:
@if(param1>=7) [ something here @if(param1>9)[ nested statement ] ] @elseif(param2==true) [ 2st condition true ] @else [ default condition ]
结果应如下所示:
array(
" if(param1>=7) [ something here @if(param1>9)[ nested statement ] ] ",
" elseif(param2==true) [ 2st condition true ] ",
" else [ default condition ] "
)
我相信 preg_split 是我正在寻找的,但可以使用正则表达式的帮助
要匹配嵌套括号,您需要使用递归模式。
(?:(\[(?:[^[\]]|(?1))*])|[^@[\]])+
它将匹配每个段,不包括前导 @
。它还会将最后一个括号捕获到组 1 中,您可以忽略它。
使用 preg_match
或 preg_match_all
的模式。
正则表达式:
@(?> if | else (?>if)? ) \s* # Match a control structure
(?> \( [^()]*+ \) \s*)? # Match statements inside parentheses (if any)
\[ # Match start of block
(?:
[^][@]*+ # Any character except `[`, `]` and `@`
(?> \.[^][@]* )* (@+ (?! (?> if | else (?>if)? ) ) )? # Match escaped or literal `@`s
| # Or
(?R) # Recurs whole pattern
)* # As much as possible
\]\K\s* # Match end of container block
PHP:
print_r(preg_split("~@(?>if|else(?>if)?)\s*(?>\([^()]*+\)\s*)?\[(?:[^][@]*+(?>\.[^][@]*)*(@+(?!(?>if|else(?>if)?)))?|(?R))*\]\K\s*~", $str, -1, PREG_SPLIT_NO_EMPTY));
输出:
Array
(
[0] => @if(param1>=7) [ something here @if(param1>9)[ nested statement ] ]
[1] => @elseif(param2==true) [ 2st condition true ]
[2] => @else [ default condition ]
)
感谢您的回复! Revo 的回答有效!
我自己无法想出正则表达式,而是构建了一个同样有效的解析器函数。也许它对某人有用:
function parse_context($subject) { $arr = array(); // array to return
$n = 0; // number of nested sets of brackets
$j = 0; // current index in the array to return, for convenience
$n_subj = strlen($subject);
for($i=0; $i<$n_subj; $i++){
switch($subject[$i]) {
case '[':
$n++;
$arr[$j] .= '[';
break;
case ']':
$n--;
$arr[$j] .= ']';
break;
case '@':
if ($n == 0) {
$j++;
$arr[$j] = '';
break;
}
default: $arr[$j].=$subject[$i];
}
}
return $arr;
} // parse_context()