I want to match the if else statements from a smarty template. And insert there my own tags. In my example <OPEN>
and </CLOSE>
.
Here is my code:
$value =
<<<SMARTY
{if}
text1
{if}
text2
{/if}
text3
{/if}
{if}
text1
{else}
text2
{/if}
{if}
text1
{if}
text2
{else}
text3
{/if}
text
{/if}
SMARTY;
echo filter($value);
function filter($value)
{
$pattern =
'(\{(?:if|else).*?\})'. // open
'((?:'.
'[^{]'.
'|\{(?!/?if|else)'.
'|(?R)'.
')+)'.
'(\{(?:\/if|else)\})'; // close
return preg_replace_callback('#'.$pattern.'#', 'filter_callback', $value);
}
function filter_callback($matches)
{
$m2 = $matches;
$m2[0] = preg_replace(array('/[
]/','/ +/','/^ /'),array('',' ',''), $m2[0]);
$m2[2] = preg_replace(array('/[
]/','/ +/','/^ /'),array('',' ',''), $m2[2]);
print_r($m2);
return $matches[1].'<OPEN>'.filter($matches[2]).'</CLOSE>'.$matches[3];
}
But it dosent work correctly.
For example I want to have the follow output:
{if}<OPEN>
text1
</CLOSE>{else}<OPEN>
text2
</CLOSE>{/if}
and
{if}<OPEN>
text1
{if}<OPEN>
text2
</CLOSE>{else}<OPEN>
text3
</CLOSE>{/if}
text
</CLOSE>{/if}
If anybody have a idea?
You don't need a recursive regex for this task. The problem with recursive regexes in PHP is that you can't easily have an access to the captures of the different recursion levels. And for a replacement task, you can forget it.
An alternative solution:
$searches = array('{if}', '{else}', '{/if}');
$reps = array('{if}<OPEN>', '</CLOSE>{else}<OPEN>', '</CLOSE>{/if}');
$result = str_replace($searches, $reps, $value); // probably the faster way
or
$patterns = array('~{if}~', '~{else}~', '~{/if}~');
$reps = array('$0<OPEN>', '</CLOSE>$0<OPEN>', '</CLOSE>$0');
$result = preg_replace($patterns, $reps, $value);
print_r($result);
Note: You can avoid one pass using:
$patterns = array('~(?<={if}|{else})~', '~(?={(?:/if|else)})~');
$reps = array('<OPEN>', '<CLOSE>');
EDIT: if you want to extract params and reuse these as a kind of tag attributes, you can do it using the preg_replace way (no need to use preg_replace_callback):
$patterns = array('~{if(?:( )[^(}]+\(([^)]+)\))?}~', '~{else}~', '~{/if}~');
$reps = array('$0<OPEN$1$2>', '<CLOSE>$0<OPEN>', '<CLOSE>$0');
$result = preg_replace($patterns, $reps, $value);
Note: The subpattern [^)]+
that describes the params can be replaced by(?s)(?>[^)\']++|\'(?>[^\'\\]++|\\{2}|\\.)*\')+
to allow closing parenthesis inside parameters with single quotes.
Here is the concrete example to match special if-statements:
$value =
<<<SMARTY
{if}
text1
{if \$user->isAllowed(null,'favourites','read')}
text2
{/if}
text3
{/if}
{if \$user->isAllowed(null,'favourites','read')}
text1
{else}
text2
{/if}
{if \$user->isAllowed(null,'favourites','read')}
text1
{if}
text2
{else}
text3
{/if}
text
{/if}
SMARTY;
echo filter($value);
function filter($value)
{
$pattern =
'(\{(?:if|else).*?\})'. // open
'((?:'.
'[^{]'.
'|\{(?!/?if|else)'.
'|(?R)'.
')+)'.
'(\{(?:\/if|else)\})'; // close
return preg_replace_callback('#'.$pattern.'#', 'filter_callback', $value);
}
function filter_callback($matches)
{
if(preg_match('#\{if.*?\$user\-\>isAllowed\((.*?)\)[^\}]*?\}#', $matches[1], $params)) {
return $matches[1].'<OPEN '.$params[1].'>'.filter($matches[2]).'</CLOSE>'.$matches[3];
}
return $matches[1].filter($matches[2]).$matches[3];
}