Creating template handler in PHP (negative look ahead problem)
I'm developing a little template handler in PHP like smarty just a much simpler one. I used smarty before but it was too robust for me. I thought that I'm ready with my one but a really annoying bug showed up. My conception is simple. I use preg_split to separate the template commands and the html code, process and implode. the {foreach} command works just fine so I won't mantion it at all, the bug appears in the {if} clause. The "{if} part" of the regexp is:
{if\s+[a-zA-Z0-9\{'\}\=\s\/$\!\=]{1,}\s*}
The problem is that I have to allow curly brackets because of the variable substitution and that causes the regexp not to stop at the end of the "{if**}". I tried negative look aheads and negate positive look aheads to exclude "{if" to avoid the problem but I had no luck.
An example for the template:
{if '{$a}' = 'a'}
blabla
{if 'c' = 'c'}
blabla
{/if}
{/if}
valami más
{if 'b' != 'b'}
blabla
{/if}
{if '{$c}' = '{$c}'}
blabla
{/if}
Output expected:
{if '{$a}' = 'a'}
{if 'c' = 'c'}
{if 'b' != 'b'}
{if '{$c}' = 开发者_运维百科'{$c}'}
Could somebody please help?
Based on your description above, I've created the following regex. I hope this is what you're looking for.
<?php
$string = <<<'EOD'
{if '{$a}' = 'a'}
blabla
{if 'c' = 'c'}
blabla
{/if}
{/if}
valami más
{if 'b' != 'b'}
blabla
{/if}
{if '{$c}' = '{$c}'}
blabla
{/if}
EOD;
$pattern = "~{if\s+'(?:\\{\\$(?:[A-Za-z0-9]+\\})|(?:[A-Za-z0-9]))'\s+(?:=|(?:!=))\s+'(?:(?:\\{\\$(?:[A-Za-z0-9]+\\}))|(?:[A-Za-z0-9]))'\\}~";
preg_match_all($pattern, $string, $matches);
echo '<pre>'.print_r($matches,1).'</pre>';
?>
Outputs:
Array
(
[0] => Array
(
[0] => {if '{$a}' = 'a'}
[1] => {if 'c' = 'c'}
[2] => {if 'b' != 'b'}
[3] => {if '{$c}' = '{$c}'}
)
)
Updated Answer
With the below comments in mind, I have created the following regex. I admit it is by far not the best solution, but it works to allow for arguments such as {if '{$c}' = '{$c}' && '{$b}' = '{$b}'}
. You could simply duplicate the pattern to allow for more than two expressions in a single if
statement. Obviously, it would then grow to a ridiculous length! If this is for a templating engine, however, perhaps it would not need much extending? Any other operators required can also simply be added into the regex at the appropriate place.
Once again, I am aware this isn't the best way of solving the problem; I'm sorry I can't create a recursive version myself. Perhaps someone else will be able to do so!
$pattern = "~{if\s+(?:(?:'(?:\\{\\$(?:[A-Za-z0-9]+\\})|(?:[A-Za-z0-9]))'\s+(?:=|(?:!=)|(?:==)|(?:<=)|(?:>=))\s+'(?:(?:\\{\\$(?:[A-Za-z0-9]+\\}))|(?:[A-Za-z0-9]))')(?:(?:\s+(?:&&|\\|\\|)\s+)(?:'(?:\\{\\$(?:[A-Za-z0-9]+\\})|(?:[A-Za-z0-9]))'\s+(?:=|(?:!=)|(?:==)|(?:<=)|(?:>=))\s+'(?:(?:\\{\\$(?:[A-Za-z0-9]+\\}))|(?:[A-Za-z0-9]))'))?)\\}~";
Tested on:
$string = <<<'EOD'
{if '{$a}' = 'a'}
blabla
{if 'c' = 'c'}
blabla
{/if}
{/if}
valami más
{if 'b' != 'b'}
blabla
{/if}
{if '{$c}' = '{$c}'}
blabla
{/if}
{if '{$c}' = '{$c}' && '{$b}' = '{$b}'}
EOD;
Result:
Array
(
[0] => Array
(
[0] => {if '{$a}' = 'a'}
[1] => {if 'c' = 'c'}
[2] => {if 'b' != 'b'}
[3] => {if '{$c}' = '{$c}'}
[4] => {if '{$c}' = '{$c}' && '{$b}' = '{$b}'}
)
)
精彩评论