0

I'm looking to improve the below regex as I now would like to match nested tags as well:

'%{if:\s*"\'([^\']*)\' == \'([^\']*)\'"}((?:(?!{else}|{/endif}).)*){else}((?:(?!{/endif}).)*){/endif}%sei'

Which basically matches:

{if: "'x' == 'y'"}
    a
{else}
    b
{/endif}

or

{if: "'x' == 'y'"}
    c
{/endif}

However, I would like this to be recursive in some sort of way, so nested statements could also be matched without breaking anything (at the moment it breaks if a nested statement is added).

There would also be a similar expression with !=.

I've found this http://www.devnetwork.net/viewtopic.php?f=38&t=102670&sid=02b7c691a2be894336c694700f8f911a#p551340 which matches <div> tags, though a bit unsure how to adapt it to suit my regex...

MrJ
  • 1,910
  • 1
  • 16
  • 29
  • more context please. what is the purpose of this? – Gordon May 22 '12 at 10:25
  • Sorry, its for some logic that I'm allowing for some end users to create a template to appear with an area that they can customise. I don't want to allow PHP, so that all gets stripped out for security as are – MrJ May 22 '12 at 10:27
  • why dont you use a readily available template engine then? Twig if you want PHP. Else XSLT. – Gordon May 22 '12 at 10:29
  • A bit too big for my needs and do not require all that functionality - `if` and `else` is all that is needed – MrJ May 22 '12 at 10:31

2 Answers2

0

If you limit the nesting to some predetermined depth (which might or might not be a bad idea), you could match it with a regex. Otherwise, you cannot. The link you provided matches HTML with regex, which is often used, but is known to be a bad idea in general. If you do not want to use other forms of parsing, consider matching innermost ifs, replacing with something and matching again.

Community
  • 1
  • 1
Eugene Ryabtsev
  • 2,232
  • 1
  • 23
  • 37
0

Using regex (PCRE) is not optimal in such cases as you would need to re-parse the inner content for each nested level (one of the reasons using a proper parser would be better).

That said, it can be done with a pattern like:

~
{if:\s*+
    (?<condition>
        [^{}]++
    )
}

(?<then>
    (?:
        (?:(?!{if:[^{}]++}|{else}|{/endif}).)*+
        (?R)*+
    )*+
)

(?:
    {else}
    (?<else>
        (?:
            (?:(?!{if:[^{}]++}|{else}|{/endif}).)*+
            (?R)*+
        )*+
    )
)?+

{/endif}
~six

Perl example @ ideone.

On this text

if: "'x' == 'y'"}
    a
{else}
    b
{/endif}

{if: "'x' == 'y'"}
    c
{/endif}

{if:minimal}{else}{/endif}

{if: "'nested' == 'things'"}
    {if: "'x' == 'y'"}x{if:minimal}{else}{/endif}x{/endif}
{else}
    b{if: "'x' == 'y'"}c{/endif}{if: "'x' == 'y'"}c{/endif}
{/endif}

{if:foo} unbalanced {if:bar}ignores first if{/endif}

it matches

*** matched if:
  * cond: "'x' == 'y'"
  * then:
    a

  * else:
    b

*** matched if:
  * cond: "'x' == 'y'"
  * then:
    c

*** matched if:
  * cond: minimal
  * then:
  * else:
*** matched if:
  * cond: "'nested' == 'things'"
  * then:
    {if: "'x' == 'y'"}x{if:minimal}{else}{/endif}x{/endif}

  * else:
    b{if: "'x' == 'y'"}c{/endif}{if: "'x' == 'y'"}c{/endif}

*** matched if:
  * cond: bar
  * then: ignores first if
Qtax
  • 33,241
  • 9
  • 83
  • 121
  • Thanks - would this still function if I were to put {$tag_name} within the single quotation marks? – MrJ May 22 '12 at 13:04
  • Sorry, for example `{if: "'{$tag_name}' == '123'"}true{else}false{/endif}` – MrJ May 22 '12 at 15:36
  • @MrJ, not as it is right now. Change the condition expression (`[^{}]++`) to whatever you want it to accept. – Qtax May 22 '12 at 16:43
  • @MrJ, haven't I answered the question? Or is something wrong with the answer? – Qtax Apr 01 '13 at 22:41