2

Hey. I need a little help enhancing my IF/ELSE parser (for a simple template engine). Right now I can use:

{if:something=value}Some info{/if}

or

{if:something=value}Some info{if:somethingelse=value}even more info{/if}{/if}

or

{if:something=value}Some info{%else%}Some other info{/if}

The problems start when I have to write something a bit more complicated, for example:

{if:something=value}
 {if:somethingelse=value}Some info{%else%}Some other info{/if}
{%else%}
 Totally different info
{/if}

The IF-s get parsed recursively as they should, but since I'm using a simple "explode" to find the "else" value, it just returns the first %else% (DoItDifferently) if something=false.

My code atm:

function parseIfs($input) {
    $regex = '#\{if\:?"?(.*?)"?\}((?:[^{]|\{(?!/?if\:?"?(.*?)"?\})|(?R))+)\{/if\}#i'; 
    if(is_array($input)) {
        // IF - value (defaults to "true" if not defined)
        $block = explode('=',$input[1]); if(empty($block[1])) $block[1] = true;
        // Explode if:value 
        $condition = explode(':',str_replace('!','',$block[0])); 
        // Get the problematic "else" value
        $outcome = explode('{%else%}',$input[2]);
        global ${$condition[0]};
        // Value to check against (can handle arrays - something:something1:something2 => $something[something1][something2])
        $replacement = ${$condition[0]}; for($i = 1; $i < count($condition); $i++) $replacement = $replacement[$condition[$i]];
        if(!strpos($block[0],'!') ? $replacement == $block[1] : $replacement != $block[1])
            $input = $outcome[0];
        else 
            $input = !empty($outcome[1]) ? $outcome[1] : '';
    }
    return trim(preg_replace_callback($regex, 'parseIfs', $input));
}
Allan
  • 201
  • 4
  • 12
  • 7
    "The problems start then I have to write something a bit more complicated" --- to be clear problems started when you decided to write your own template engine. There are a lot of good ones (smarty, twig, mustache) and needless to say that php itself *is* a template engine. – zerkms May 14 '11 at 10:36
  • You could use a sax parser like statemachine to parse the epressions. – Roki May 14 '11 at 10:43
  • @zerkms - Not really. I do not need an overly complicated system .. just the very basics. The whole thing is around 100 lines of code and can handle most occasions pretty well (and fast). – Allan May 14 '11 at 10:46
  • 1
    PHP itself is a [templating engine](http://codeangel.org/articles/simple-php-template-engine.html). – tereško May 14 '11 at 10:56
  • You should make your regex more readable using the `/x` flag. The if-else construct you've chosen is ambigious unless the else is always required. If so, it should be noted in the regex, not in a separate explode. The combination of the assertion `(?!/?if` and the `(?R)` seems problematic. At the very least you should give a `print_r($input)` example to make your question easier to understand and answer. (Main issue seems to me however the condition evaluation with such approaches.) – mario May 14 '11 at 10:59
  • 3
    Don't forget [PHP Templating Language](http://phptemplatinglanguage.com/) :) – alex May 14 '11 at 11:09
  • Unfortunately I can't use eval() (as suggested by dmitry7) or PHP directly .. this is a part of some companies intranet thing and they have very a specific set of rules that they want their UI guys to follow (they don't allow any PHP in the .tpl files). – Allan May 14 '11 at 11:23
  • 4
    @Allan This will sound harsh, but I'm not sure who's more to blame: The company for crippling production with arbitrary rules, or you for wasting their time implementing them. Use an off-the-shelf solution. It will be faster, more robust and more bug-free. Trying to write your own to make it "faster" indicates a deep misunderstanding of web-oriented development; speed should be your absolutely last concern after security, stability and ease of use. Plus you're [doomed to failure anyways](http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags) – user229044 May 14 '11 at 11:39
  • You can't handle such constructs with regex because they are not context-free. You need a parser. – Danubian Sailor May 19 '13 at 19:01

2 Answers2

2

This is not exact answer to your question (i mean the problem with the function) but here is the other solution you may consider to use (in this way you should use PHP's conditional operators like ==, ===, !=, etc but you may add additional code to override that if you want).

function parse($code){
    $code = str_replace('{%else%}', '<? } else { ?>', $code);
    $code = str_replace('{/if}', '<? } ?>', $code);
    $code = preg_replace('/\{if:(.+?)\}/i', '<? if($1){ ?>', $code);

    eval('?>'.$code.'<?');
}
dmitry7
  • 441
  • 3
  • 6
-1

i think you will get better help from here

https://ellislab.com/expressionengine/user-guide/templates/conditionals.html
Ijaz Ahmed Bhatti
  • 736
  • 1
  • 7
  • 26