How to find a string based on a group of regular expressions

3

I'm trying to create a Template Engine in PHP for study reasons.

Suppose I have the following array :

$regexList = [
  'varPattern' => '/{{\s*\$(.*?)\s*}}/',
  'loopPattern' => '/@for\((.*?)\)\s*{{((?:[^{}]|(?R))*)}}/',
  'statementPattern' => '/@if\((.*?)\)\s*{{((?:[^{}]|(?R))*)}}/'
]

and the following functions :

getVar($nomeDaVariavel);
loop($nomeDoArray);
getStatementResult($expressãoBooleana);

and the following string :

$string = '

<span>{{ $nomeCompleto }}</span>

@for($nomes as $nome)
{{
  @if($nome == 'Eleandro)
  {{
    <p>{{ $nome }}</p>
  }}
}} ';

The idea is to read the string from top to bottom and based on the list of regular expressions find the result and give it the correct function.

For example: The first thing to find must be {{ $string }} , so we pass the variable name to getVar($nomeDaVariávelEncontrada) function.

Next is loop , so we call loop($comoNomeDoArrayEncontrado) ; and within the loop will be found if , then we get the contents of if and we give it the getStatementResult($expressãoBooleanaEncontrada) function with the found value.

How can I do this in the right order (top to bottom)?

    
asked by anonymous 24.05.2017 / 14:09

1 answer

1
  

I am using preg_replace_callback to replace the results of patterns that are in $regexList .   The problem is that this function takes the occurrence of the first pattern in $regexList , instead of taking the first occurrence in $string that has a pattern in $regexList . / p>


Use only a regex.

In this case, do not use an array. Instead:

$regexList = [
                 '/regex1/',
                 '/regex2/',
                 '/regex3/',
             ];


Use this:

$regex = '/regex1|regex2|regex3/';

In function:

$resultado = preg_replace_callback(
                 $regex,
                 function ($matches) {
                     // ...
                     return 'substituído';
                 },
                 $texto
             );


But how do you know which regex was found?

You can use groups to identify them.

$regex = '/(regex1)|(regex2)|(regex3)/';

Then:

$resultado = preg_replace_callback(
                 $regex,
                 function ($matches) {
                     if ($matches[1]) {
                         return 'regex1 substituído';
                     } else if ($matches[2]) {
                         return 'regex2 substituído';
                     } else if ($matches[3]) {
                         return 'regex3 substituído';
                     }
                 },
                 $texto
             );



But you have other groups in the patterns, and counting the right group number can be tricky. We can use named groups to make things easier.

$regex = '/(?P<padrao1>regex1)|(?P<padrao2>regex2)|(?P<padrao3>regex3)/';


$resultado = preg_replace_callback(
                 $regex,
                 function ($matches) {
                     if ($matches['padrao1']) {
                         return 'regex1 substituído';

                     } else if ($matches['padrao2']) {
                         return 'regex2 substituído';

                     } else if ($matches['padrao3']) {
                         return 'regex3 substituído';
                     }
                 },
                 $texto
             );


Code

To answer your question, with a little recursion:

function analisar( $string ) {
    $regex = '/
                    (?P<var>       {{\s*\$(.*?)\s*}}                      )
                |
                    (?P<loop>      @for\((.*?)\)\s*{{((?:[^{}]|(?R))*)}}  )
                |
                    (?P<statement> @if\((.*?)\)\s*{{((?:[^{}]|(?R))*)}}   )
              /x';

    $resultado = preg_replace_callback(
                     $regex,
                     function ($matches) {
                         /*
                             //debug
                             echo "\n\nSubst: $matches[0]\n\$matches = ";
                             var_export($matches);
                         */
                         if ($matches['var']) {
                             return getVar($matches[2]);

                         } else if ($matches['loop']) {
                             return loop($matches[4], $matches[5]);

                         } else if ($matches['statement']) {
                             return getStatementResult($matches[7], $matches[8]);
                         }
                     },
                     $string
                 );

    return $resultado;
}

function getVar($nomeDaVariavel){
    return 'VAR('
        . analisar($nomeDaVariavel)
        . ')';
}
function loop($nomeDoArray, $codigo){
    return "LOOP\nLOOP-COND("
        . analisar($nomeDoArray)
        . ")\nLOOP-CODIGO("
        . analisar($codigo)
        . ')';
}
function getStatementResult($expressãoBooleana, $codigo){
    return "IF\nIF-COND("
        . analisar($expressãoBooleana)
        . ")\nIF-CODIGO("
        . analisar($codigo)
        . ')';
}

Test:

$string = '
<span>{{ $nomeCompleto }}</span>

@for($nomes as $nome)
{{
  @if($nome == \'Eleandro\')
  {{
    <p>{{ $nome }}</p>
  }}
}} 
';

echo analisar($string);

Result:

<span>VAR(nomeCompleto)</span>

LOOP
LOOP-COND($nomes as $nome)
LOOP-CODIGO(
  IF
IF-COND($nome == 'Eleandro')
IF-CODIGO(
    <p>VAR(nome)</p>
  )
) 

Demo: link

    
24.11.2017 / 12:47