2

I have the string bellow:

$string = '

@if(topLevel)
{{
  <form method="post">
    @if(bottomLevel)
    {{
      <input>
    }}
  </form>
}}';

What i'm trying is to find the value inside the @if(.*?) and the content between the brackets of the top level "@if".

This is the pattern i've tried:

@if\((.*?)\)(\s|\n|\t)+{{(\s|\n|\t)(.*?)(\s|\n|\t)}}

This is matching:

@if(bottomLevel)
{{
  <input>
}}

How can i go from the outside into the string and match the top level "@if"?

Mistalis
  • 17,793
  • 13
  • 73
  • 97
Eleandro Duzentos
  • 1,370
  • 18
  • 36

6 Answers6

2

Use singleline flag, and remove lazy from .*?:

@if\((.*?)\)\s+{{\s*(.*)\s*}}

And since TAB is included in \s, and with the singleline flag \n as well, you only need \s to match the white space.

See it here at regex101.

Edit

Here's an alternative using recursion, to manage multiple groups of braces.

@if\((.*?)\)\s+{{((?:[^{}]|(?R))*)}}

Regex101 example.

SamWhan
  • 8,296
  • 1
  • 18
  • 45
1

(.*?) with the ? is non-greedy matching, which means it will find the shortest possible match. Try without the ?, and it won't stop at the first }}. You should also turn on the single-line flag /s and replace your whitespace pattern with simple \s*.

Result: /@if\((.*?)\)\s*\{\{\s*(.*)\s*\}\}/s

PS.: I escaped {} characters, which is not necessary for your case, but I like to escape characters that have a meaning in regex.

aorcsik
  • 15,271
  • 5
  • 39
  • 49
1

Sorry, I hate preg_matches, so I did with substr and substr-count, I love the readability of that:

    $string = '

@if(topLevel)
{{
  <form method="post">
    @if(mediumLevel)
    {{
      <input>
    }}
  </form>
  <form method="post">
    @if(bottomLevel)
    {{
      <input>
    }}
  </form>
}}';
function string_between($string, $start, $end){
    $total = substr_count($string, $start);
    for ($i = 0; $i < $total; $i++){
        $string = ' ' . $string;
        $ini = strpos($string, $start);
        $ini += strlen($start);
        $len = strpos($string, $end, $ini) - $ini;
        $result[$i] = substr($string, $ini, $len);
        $string = substr($string, $len);
        $ini = strpos($string, $start);
        $string = substr($string, $ini);
    }
    return $result;
}

function string_between_brackets($string, $start, $end){
    $total = substr_count($string, $start);
    for ($i = 0; $i < $total; $i++){
        $string = ' ' . $string;
        $ini = strpos($string, $start);
        $ini += strlen($start);
        if ($i == 0){
            $len = strrpos($string, $end, $ini) - $ini;
        } else {
            $len = strpos($string, $end, $ini) - $ini;
        }
        $result[$i] = substr($string, $ini, $len);
        $string = substr($string, $ini);
    }
    return $result;
}

$parsed = string_between($string, '@if(', ')');
$parsedContent = string_between_brackets($string, '{{', '}}');
$i = 0;
$parsedString = array_combine($parsed, $parsedContent);
var_dump($parsedString);

/*array (size=3)
  'topLevel' => string '
  <form method="post">
    @if(mediumLevel)
    {{
      <input>
    }}
  </form>
  <form method="post">
    @if(bottomLevel)
    {{
      <input>
    }}
  </form>
' (length=165)
  'mediumLevel' => string '
      <input>
    ' (length=19)
  'bottomLevel' => string '
      <input>
    ' (length=19)*/

Inspired by Get substring between two strings PHP

capcj
  • 1,535
  • 1
  • 16
  • 23
0

I don't think regex is the best way to parse your code, anyway here is a suggestion getting the part of the code you want in two times:

Regex 1: extracts the parameter in @if(...)

@if\((\w+)\)

It extracts from your string:

topLevel
bottomLevel

Test it on regex101


Regex 2: get the value between brackets

{{\s+([<\w\s"@(){}\/=>]+)}}

It extracts from your string:

<form method="post">
  @if(bottomLevel)
  {{
    <input>
  }}
</form>

Test it on regex101

Community
  • 1
  • 1
Mistalis
  • 17,793
  • 13
  • 73
  • 97
  • That's not what i want, this only give me the content inside the parentheses of @if, i want a regex that match the top level and give me the content between it's brackets. – Eleandro Duzentos May 23 '17 at 13:30
0

Give this a try:

{{\n\s+(.+)\n.+}}

It looks for opening double curly braces, then a new line, then any number of characters, followed by a new line, some spaces, any characters followed by a new line, any chars, and then the closing }}. The brackets capture the specific text.

You can see an example of it here: https://regex101.com/r/pwFNA1/1

delboy1978uk
  • 12,118
  • 2
  • 21
  • 39
  • Thanks for the help, but that's not what i want. What i'm trying is to find the value inside the @if(.*?) !!!!!and!!!!! the content between the brackets of the top level "@if". – Eleandro Duzentos May 23 '17 at 13:35
0

This is a generic regex that supports bracket multiple-nesting:

@if\((.*?)\)\s+({{\s+((?:[^{}]|(?2))*?)\s+}})

Capturing groups are $1 and $3. See demo.

Anyway, I've got some generic hints:

  • \t and \n are redundant with respect to \s. Don't use (\t|\n|\s)+, use just \s+.
  • You cannot match multiple lines using .*, you need to use the singleline modifier.
logi-kal
  • 7,107
  • 6
  • 31
  • 43