Как использовать preg_match для извлечения пользовательских тегов

#php #regex #preg-match

#php #регулярное выражение #preg-match

Вопрос:

я использую этот шаблон '/({(w )}(.*))?{%(w )%}((.*){/(w )})?/i' для извлечения тегов из шаблона с помощью preg_match функции.

пример шаблона:

 <table id="middle" cellspacing="0px" cellpadding="0px">
    {middle}
    <tr>
        {left}<td>{%left%}</td>{/left}
        <td>{%middle%}{%content%}</td>
        {right}<td>{%right%}</td>{/right}
    </tr>
    {/middle}
</table>
  

как убедиться, что start и end каждого тега действительно соответствуют его имени

в этом примере middle тег совпадает для обоих middle и content , хотя он должен просто соответствовать middle тегу

Ответ №1:

Я думаю, что лучшим способом решить эту проблему было бы сделать это в несколько разных шагов.

Во-первых, вы должны использовать preg_replace_callback с /(?>{([^}] )})(.*)?{/1}/sim в качестве регулярного выражения. Это позволит найти {тег} верхнего уровня{/tag}. $matches[2] будет содержать содержимое (без тегов), в то время как $matches1 будет содержать сам тег.

Вы должны создать функцию, которая вызывается рекурсивно, чтобы при обратном вызове она вызывалась снова для $ matches [2], поэтому найдите дочерние элементы {теги} на всякий случай, если их больше. Вот как вы пройдете по дереву.

Наконец, вы должны создать третью функцию, которая обрабатывает {%tag%}. Я бы снова использовал preg_replace_callback и использовал инструкцию switch для обработки имени тега.

Это должно указать вам правильное направление.

РЕДАКТИРОВАТЬ: Вот полностью функциональная демонстрация того, что я описал выше:

 <?php

$content = <<<END
{a}
  {b}I like {%first%} {%last%} a {c}lot{/c}.{/b}
{/a}
END;

echo find_tags($content);

function find_tags($content)
{
  return preg_replace_callback('/(?>{([^}] )})(.*)?{/1}/sim', 'find_tags_callback', $content);
}

function find_tags_callback($matches)
{
  // Find and process any children tag pairs.
  $matches[2] = find_tags($matches[2]);

  // Process the tags {%tag%}.
  $matches[2] = preg_replace_callback('/{%([^%] )%}/sim', 'process_tags', $matches[2]);

  switch ( $matches[1] )
  {
    case 'a':
      $tag = 'div';

      break;
    case 'b':
      $tag = 'p';

      break;
    case 'c':
      $tag = 'b';

      break;
  }

  return '<'.$tag.'>'.$matches[2].'</'.$tag.'>';
}

function process_tags($matches)
{
  switch ( $matches[1] )
  {
    case 'first':
      return 'Francois';

      break;
    case 'last':
      return 'Deschenes';

      break;
  }
}

//
  

Результирующей строкой будет: <div><p>I like Francois Deschenes a <b>lot</b>.</p></div> .

Ответ №2:

1 Я уверен, но не уверен, что для того, чтобы убедиться, что входящие теги ({this}{/this}) соответствуют тегам данных ({%this%}), вам понадобится сопровождающий оператор if для проверки возвращаемых строк.

Я бы использовал функцию preg_replace_callback, например:

 <?php
$str = '<template contents>';
$newstr = preg_replace_callback(
'/({(w )}(.*))?{%(w )%}((.*){/(w )})?/i', 
'check', //<-- the function to send matches to
$str);
function check($matches){
    if($matches[1] == $matches[2] amp;amp; $matches[1] == $matches[3]){
        /*Do Work*/
        return ''; //new formatted string to send back to the $newstr var
    }
}
?>
  

функция preg_replace_callback отправляет все найденные соответствия в виде массива указанной функции для обработки, затем вы возвращаете новую форматированную строку из этой функции.

Комментарии:

1. вам не нужен оператор if. Выполнение этого таким образом на самом деле приведет ко многим недопустимым совпадениям, особенно: {a}{b}{/b}{/a} приведет к {a}{/b}. Вам нужно посмотреть на обратные ссылки регулярного выражения . Смотрите мой пример. Это решение также не будет работать, если теги находятся в нескольких строках.