Сопоставьте тело функции с помощью Regex

Учитывая фиктивную функцию как таковую:

public function handle()
{
  if (isset($input['data']) {
    switch($data) {
      ...
    }
  } else {
    switch($data) {
      ...
    }
  }
}

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

Я сталкивался с рекурсивными шаблонами, но не мог понять регулярное выражение, которое будет соответствовать телу функции.

Я пробовал следующее (без рекурсии):

$pattern = "/function\shandle\([a-zA-Z0-9_\$\s,]+\)?". // match "function handle(...)"
            '[\n\s]?[\t\s]*'. // regardless of the indentation preceding the {
            '{([^{}]*)}/'; // find everything within braces.

preg_match($pattern, $contents, $match);

Эта схема вообще не подходит. Я уверен, что это последний бит, который неверен '{([^{}]*)}/', поскольку этот шаблон работает, когда в теле нет других фигурных скобок.

Заменив его на:

'{([^}]*)}/';

Он соответствовал до закрытия } переключателя внутри оператора if и останавливался на этом (включая } переключателя, но исключая if).

Как и этот шаблон, тот же результат:

'{(\K[^}]*(?=)})/m';

person mulkave    schedule 29.06.2016    source источник
comment
в какой вселенной вам нужно извлечь содержимое функции с помощью регулярного выражения (или любым другим способом)   -  person    schedule 30.06.2016
comment
Я бы переосмыслил то, что я делаю, если бы я был тобой. Проблема здесь в том, что если вы понятия не имеете, что может содержать тело функции, регулярное выражение должно быть чудовищным, неподдерживаемым и подверженным 100000 ошибкам. Представьте, что у вас есть строка, содержащая { или } (без соответствующей начальной или конечной скобки), тогда ваш рекурсивный шаблон не будет работать. И это только первая ситуация, о которой я подумал.   -  person Magnus Eriksson    schedule 30.06.2016
comment
Действительно, попробуйте ответить на вопрос @Dagon здесь - какова ваша цель?   -  person Jan    schedule 30.06.2016
comment
Это не то, для чего регулярное выражение хорошо подходит: я могу сделать одно, но в конечном итоге чем глубже вы хотите, чтобы оно могло соответствовать, тем длиннее регулярное выражение. Для каждого уровня вам нужна отдельная группа, если вы хотите иметь возможность сопоставить их: регулярное выражение, которое поддерживает до 4 вложенных {{{{}}}}, сломается при {{{{{}}}}} вложенности 5.   -  person TemporalWolf    schedule 30.06.2016
comment
@TemporalWolf - не забывайте, что вам также необходимо учитывать и игнорировать любые фигурные скобки, которые находятся внутри одинарных / двойных кавычек, heredoc и т. Д. ...   -  person Magnus Eriksson    schedule 30.06.2016
comment
@MagnusEriksson Выполнимо, просто требуется еще больше регулярных выражений. Как я уже сказал, это, вероятно, не правильный инструмент для работы. То, что он ищет, - это pushdown automaton, которое регулярное выражение не может сделать. Он может подделать его для достаточно простых задач, но в конечном итоге регулярное выражение будет бесконечно длинным, чтобы охватить все случаи.   -  person TemporalWolf    schedule 30.06.2016
comment
@TemporalWolf - я согласен, что регулярное выражение не подходит для этого. На самом деле я бы сказал, что вы не можете сделать его на 100% безопасным (от ошибок) с помощью регулярных выражений. Учтите, что вам также нужно игнорировать одинарные кавычки внутри двойных кавычек (чтобы он не думал, что вы все еще в кавычках, когда нет и т. д.), экранированные кавычки внутри кавычек и так далее. Вы наверняка всегда пропустите несколько различных комбинаций.   -  person Magnus Eriksson    schedule 30.06.2016
comment
@MagnusEriksson Однако в основном случае вы можете, если знаете, каким будет ваш вклад. В любом случае, я оказался неправ -> php поддерживает рекурсивные регулярные выражения ... так что это не так уж ужасно, как доказывает ответ revo.   -  person TemporalWolf    schedule 30.06.2016
comment
@TemporalWolf - его ответ легко проваливается (как я прокомментировал). Если вы знаете, что будет на входе, зачем вообще заниматься его разбором? Тогда просто жестко закодировать это?   -  person Magnus Eriksson    schedule 30.06.2016
comment
@TemporalWolf - В любом случае. Это обсуждение довольно спорное, поскольку ОП на самом деле не предоставил нам отзыв на наши первоначальные вопросы. На данный момент мы все просто предполагаем.   -  person Magnus Eriksson    schedule 30.06.2016
comment
@MagnusEriksson Спасибо, что указали на то, что это неправильный подход к решению, что является одной из основных причин, по которой я разместил этот вопрос. Мое намерение состоит в том, чтобы прочитать содержимое тела функции и отобразить его как есть (строка), ничего кроме этого. Дагон, это ответ на твой вопрос?   -  person mulkave    schedule 30.06.2016
comment
@TemporalWolf нет ограничений на количество вложенных уровней, которые могут быть в теле, в какие секунды вы говорите. Я хотел бы узнать больше о pushdown automaton и о том, как он может решить такую ​​​​проблему, никогда раньше не слышал об этом термине (обязательно проведу некоторое исследование, но также буду признателен за ваши биты по этому поводу).   -  person mulkave    schedule 30.06.2016
comment
Возможный дубликат Как сопоставить блок метода с помощью регулярного выражения?   -  person SamWhan    schedule 30.06.2016


Ответы (2)


Обновление №2

Согласно комментариям других

^\s*[\w\s]+\(.*\)\s*\K({((?>"(?:[^"\\]*+|\\.)*"|'(?:[^'\\]*+|\\.)*'|//.*$|/\*[\s\S]*?\*/|#.*$|<<<\s*["']?(\w+)["']?[^;]+\3;$|[^{}<'"/#]++|[^{}]++|(?1))*)})

Примечание: короткого регулярного выражения, например {((?>[^{}]++|(?R))*)}, достаточно, если вы знаете, что вводимые данные не содержат { или } вне синтаксиса PHP.

Итак, длинное регулярное выражение, в каких плохих случаях оно работает?

  1. У вас есть [{}] в строке между кавычками ["']
  2. У вас эти кавычки экранированы друг в друге
  3. У вас есть [{}] в блоке комментариев. //... или /*...*/ или #...
  4. У вас есть [{}] в heredoc или nowdoc <<<STR или <<<['"]STR['"]

В противном случае предполагается наличие пары открывающих/закрывающих фигурных скобок, а глубина вложенных фигурных скобок не важна.

У нас есть случай, когда это не удается?

Нет, если у вас нет марсианина, который живет внутри ваших кодов.

 ^ \s* [\w\s]+ \( .* \) \s* \K               # how it matches a function definition
 (                             # (1 start)
      {                                      # opening brace
      (                             # (2 start)
           (?>                               # atomic grouping (for its non-capturing purpose only)
                "(?: [^"\\]*+ | \\ . )*"     # double quoted strings
             |  '(?: [^'\\]*+ | \\ . )*'     # single quoted strings
             |  // .* $                      # a comment block starting with //
             |  /\* [\s\S]*? \*/             # a multi line comment block /*...*/
             |  \# .* $                      # a single line comment block starting with #...
             |  <<< \s* ["']?                # heredocs and nowdocs
                ( \w+ )                      # (3) ^
                ["']? [^;]+ \3 ; $           # ^
             |  [^{}<'"/#]++                 # force engine to backtack if it encounters special characters [<'"/#] (possessive)
             |  [^{}]++                      # default matching bahaviour (possessive)
             |  (?1)                         # recurse 1st capturing group
           )*                                # zero to many times of atomic group
      )                             # (2 end)
      }                                      # closing brace
 )                             # (1 end)

Форматирование выполняется с помощью RegexFormatter программного обеспечения @sln.

Что я предоставил в живой демонстрации?

Файл Laravel Eloquent Model.php (~3500 строк) случайным образом дается как ввод. Посмотрите: демонстрация

person revo    schedule 29.06.2016
comment
Сбой, если это класс или если у нас есть строка с фигурной скобкой - person Magnus Eriksson; 30.06.2016
comment
Опять же, регулярное выражение - неправильный инструмент, и, как мы с @Dagon уже сказали, это невозможно сделать с помощью регулярного выражения, поскольку вы не сможете учесть все ситуации и варианты. В основном вы потратите остаток своей жизни на исправление регулярного выражения. Строки - это всего лишь одна ситуация... затем есть комментарии, которые вам нужно обрабатывать, и так далее... - person Magnus Eriksson; 30.06.2016
comment
Вы не должны говорить это парню с RegEx, так как он хорошо это знает. Регулярные выражения как мощный инструмент обработки текста — это не простая утилита для выполнения только дешевых работ, ее цель — идеально выполнять тяжелые текстовые задачи. Если бы в этом не было необходимости, мы бы придерживались подстановочных знаков UNIX и простых шаблонов файлов. Кстати, я ничего не исправляю, этого короткого RegEx поначалу было достаточно для того, чего хочет OP. Но пока кто-то вроде вас пытается показать мне, что с этим может быть не так, я буду исправлять. @МагнусЭрикссон - person revo; 30.06.2016
comment
Также подумайте об этом, я никогда не говорил Только регулярное выражение и никогда не скажу этого. Если у вас есть инструмент, который выполнял большую часть этой тяжелой и неприятной работы в фоновом режиме, не стесняйтесь показать его OP. И... не голосуйте за комментарии друг друга. Подтверждение друг друга не помогает. @МагнусЭрикссон - person revo; 30.06.2016
comment
@revo впечатляет! Спасибо за это решение, оно сработало. - person mulkave; 30.06.2016
comment
Проголосовали за - В нашем проекте, который использует SquishAPI (и, следовательно, в значительной степени полагается на строки "{...}" для идентификации объектов), у нас есть только 63 вхождения в 80kLOC {/} внутри строк... Я думаю, что это будет работать денди в большинстве случаев. - person TemporalWolf; 30.06.2016

Это работает для вывода файла заголовка (.h) из встроенных функциональных блоков (.c).

Найти регулярное выражение:

(void\s[^{};]*)\n^\{($[^}$]*)\}$

Заменить:

$1;

Для ввода:

void bar(int var)
{ 
    foo(var);
    foo2();
}

выведет:

void bar(int var);

Получите тело функционального блока со вторым совпавшим шаблоном:

$2

выведет:

    foo(var);
    foo2();
person TianaR    schedule 02.09.2018