0

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('/[\n\r]/','/ +/','/^ /'),array('',' ',''), $m2[0]);
    $m2[2] = preg_replace(array('/[\n\r]/','/ +/','/^ /'),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?

  • Regular expressions are, by nature, **not** recursive and cannot handle recursive matching very well. An explanation of why can be found in this answer (HTML is a recursive language, too, so the same principles apply): http://stackoverflow.com/a/1758162/334053 You need to just write (or find) a parser for Smarty and do what you're trying to do with PHP. – qJake Nov 25 '13 at 15:29
  • @SpikeX for the recursion I use (?R) (http://us2.php.net/manual/de/regexp.reference.recursive.php) in my expression – laemmi_punk Nov 27 '13 at 09:54

2 Answers2

1

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.

Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125
  • in the next step I want to find special "if else statments" like {if mystatement param1 param2}{if}{else}{/if}{/if} and fetch the params. Your solution cant´t find special statements. – laemmi_punk Nov 27 '13 at 09:43
  • @laemmi_punk: You can adapt the second solution with a little modification of the pattern and use `preg_replace_callback` to fetch what you want. If you want more help to do that, please provide a concrete example in your question. – Casimir et Hippolyte Nov 27 '13 at 10:46
0

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];
}