1

I work with @if|@else|@endif statement in my HTML code and I need find most nested statement/condition (a condition that don't contain another condition) via preg_replace_callback() function.

When function resolve the condition on last level, it start again with parent IF statement as long as all conditions/IF statements will be solved.

In every iteration I need find the actual most nested conditions.

This is example of my HTML code:

  @if($i%2 == 0)

     <something html> 

        @if($i==2)

           <something html>

           @if($i == 0)

              <something html>

           @endif

        @endif

     <something html>

  @else 

     <something html>

        @if($i==2)

           <something html>

        @endif

     <something html>

  @endif

I try something like:

$pattern = '/@if\s*\(\s*(.*)\s*\)\s*((?!@if|@endif).*)\s*(?:@else\s*((?!@if|@endif).*))?\s*@endif/s';

Thank you in advance for your help.

Richard
  • 325
  • 7
  • 23
  • 2
    Possible duplicate of [RegEx match open tags except XHTML self-contained tags](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags) – CAustin Mar 20 '18 at 21:04
  • How about something like [`/@if\s*\(([^)(]*)\)((?:(?!@(?:end)?if).)++)@endif/s`](https://regex101.com/r/rLOWF9/1) bear in mind, that it's slow and inaccurate. Used two capture groups, remove if unwanted. – bobble bubble Mar 20 '18 at 21:40
  • Is there any reason you can't use an existing template engine, like Twig? –  Mar 21 '18 at 02:33

1 Answers1

1

Your attempt has two problems:

  1. The first capturing group matches the rest of the input: "(.)". You need to exclude the closing parens of the if-condition: "([^)])".
  2. The negative look-ahead on @if and @endif only works for the very first character. After that, it matches the rest of the input with ".". The correct nesting here is "((?!...).)".

This gives the following regex:

@if\s*\(\s*([^)]*)\s*\)\s*(((?!@if|@endif).)*)\s*(?:@else\s*(((?!@if|@endif).)*))?\s*@endif

But please note that this only works if the if-conditions do not contain expressions with parenthesis. If they can contain arbitrarily nested parenthesis, you are screwed. (You probably know this, but regex can not count an arbitrary level of nesting, you need a stack for that).

I used this to help figure out your problem: https://regex101.com/

lambda
  • 89
  • 4
  • If I little bit modified your pattern for my match_array structure ([1] => condition, [2] => true part, [3] => false part) It works perfectly for me in all iterations. `/@if\s*\(\s*([^)]*)\s*\)\s*((?:(?!@if|@endif).)*?)\s*(?:@else\s*((?:(?!@if|@endif).)*))?\s*@endif/s` – Martin Smola Mar 20 '18 at 22:20
  • This matches `@if(@if(abc)XYZ@endif`. You have to move the `(?:(?!@if|@endif).)*` into the if body, and actually everywhere you are matching characters. –  Mar 20 '18 at 23:58