PHP регулярное выражение для поиска "функций" в строке

Есть строка типа строка с {@rand(1||{@rand(2|3|{@rand(4|5)})})} и {@rand(1|{@rand(2|3|{@rand(4|5)})})}. Здесь {@rand(param1|param2|)} - вызов функции rand с параметрами(param1, param2 и ПУСТАЯ СТРОКА). В качестве параметров может быть строка, пустая строка или вызов другой подобной функции.

И в этой строке мне нужно найти все вызовы функций ПЕРВОГО УРОВНЯ. Т.е. здесь результат такой

<?php

array(
  '{@rand(1||{@rand(2|3|{@rand(4|5)})})}',
  '{@rand(1|{@rand(2|3|{@rand(4|5)})})}'
);

Я составил такую регулярку - /{@(?<func>\w+?)\((?<params>.*)\)}/ui (Она работает неправильно)

Но здесь проблема в том, регулярка не видит конец первой функции и захватывает всё от начала первой и конца второй функций

Помогите составить регулярное выражение)


Ответы (1 шт):

Автор решения: Wiktor Stribiżew

Вы можете использовать

{@(?<func>\w+)(\((?<params>(?:[^()]++|(?2))*)\))}

См. пример работы выражения. Подробности:

  • {@ - текст {@
  • (?<func>\w+) - Подмаска "func" (кроме того, это подмаска №1): одна и более букв, цифр или знаков подчёркивания
  • (\((?<params>(?:[^()]++|(?2))*)\)) - Подмаска №2:
    • \( - символ (
    • (?<params>(?:[^()]++|(?2))*) - Подмаска "params": один и более символов, отличных от ( и ), или рекурсивный повтор второй подмаски
    • \) - символ )
  • } - символ }

См. пример кода на PHP:

$re = '/{@(?<func>\w+)(\((?<params>(?:[^()]++|(?2))*)\))}/u';
$str = 'строка с {@rand(1||{@rand(2|3|{@rand(4|5)})})} и {@rand(1|{@rand(2|3|{@rand(4|5)})})}.';
if (preg_match_all($re, $str, $matches, 0, PREG_SET_ORDER)) {
    print_r($matches["func"]);
    print_r($matches["params"]);
}

Результат:

Array
(
    [0] => rand
    [1] => rand
)
Array
(
    [0] => 1||{@rand(2|3|{@rand(4|5)})}
    [1] => 1|{@rand(2|3|{@rand(4|5)})}
)
→ Ссылка