Мне нужно регулярное выражение, соответствующее определенной группе захвата, которая попадает в многострочный комментарий / * … * /.
В частности, мне нужно найти определения переменных PHP внутри многострочных комментариев
например:
/* other code $var = value1 */
$var = value2 ;
/*
other code
$var = value3 ;
other code
*/
должен соответствовать только двум вхождениям $ var = внутри комментариев, но не одному за пределами комментария.
для приведенного выше примера я написал регулярное выражение, которое использует неограниченный вид сзади, как это
(?<=[/][\*][^/]+)(\$var) | (?<=[/][\*][^\*]+)(\$var)
но это регулярное выражение не выполняется в случае, если он находит оба символа * и /, даже если они APART друг от друга, между открывающим тег комментарием ‘/ *’ и $ var, что не является желаемым поведением:
например, это терпит неудачу в случае:
$var = .... ;
/*
other * code /
$var = .... ;
other code
*/
потому что он находит ‘*’ и ‘/’, даже если это не закрывающий тег комментарий.
Ключевым моментом является то, что я не могу отменить токен, который является комбинацией двух символов, но могу отменить их только один за другим: [^ *] или [^ /].
…Более того, я не могу использовать токен [\ s \ S] вместо [^ /] и [^ *], потому что он выберет $ var из комментариев, которым предшествует предыдущий блок комментариев.
Есть идеи? Можно ли даже с помощью регулярных выражений достичь этого? Или мне нужно что-то другое?
Идея использования \ G клеить соответствует /*
(?:/\*|\G(?!^))(?:(?!\*/)[^$])*\K\$var\s*=\s*(?:(?!\*/)[^$;])*
Может быть трудно понять, если вы не много делаете с регулярными выражениями. Смотрите regex101 для демонстрации.
\G
может рассматриваться как «клей», это продолжается в конце предыдущего матча. Но \G
также соответствует началу строки. Вот почему используется негативный взгляд \G(?!^)
нужно только продолжить.
/\*|\G(?!^)
Эта часть, чтобы найти начало матча в /*
или продолжить сопоставление.
(?:(?!\*/)[^$])*
Подберите любое количество символов, которые не являются $
(отрицается класс), не заканчивая комментарий (?!\*/)
для вещей до / между $var
\K\$var
\K
перезагружается начало указанного матча до $var
происходит. \K
может быть полезен в качестве альтернативы lookebhind переменной ширины, который не доступен в pcre.
\s*=\s*(?:(?!\*/)[^$;])*
чтобы соответствовать значению переменной. Это далеко не идеально. Потребуется модификация, если приведенные значения или не удобно для вашего ввода. После =
это соответствует [^$;]
символы, которые не являются долларом или точкой с запятой (?!\*/)
пока нет */
вперед.
Это регулярное выражение не проверяет, есть ли на самом деле конец комментария */
это просто связывает спички /*
Другой идеей было бы использовать вид этот трюк с глаголами (*SKIP)(*FAIL)
лайк в этой демонстрации.
Это соответствует просто $var
и только внутри многострочного комментария:
(?s)\$var(?=(?:(?!/\*|\*/).)*\*/)
(?:(?!/\*|\*/).)*
это плененный взгляд (также известный как Закаленный жадный жетон—хорошее имя, но слишком много слогов), и именно так вы исключаете последовательность, в отличие от одного символа. Этот соответствует нулю или более любого символа (включая символ новой строки, потому что (?s)
), пока это не первый персонаж /*
или же */
,
Включающий lookahead преуспевает, если находит */
без первого знакомства /*
, Это означает, что текущая позиция должна быть внутри комментария (нет необходимости совпадать с открытием /*
). А так как предвидение не потребляет никаких символов, вы можете сопоставить более одного элемента на комментарий, если вам нужно.
Одна вещь, которая может обмануть это регулярное выражение */
это не совсем комментарий ближе. Итак, эти:
$var = "*/";
$var = ...;
// */
… будет соответствовать, даже если они не в комментарии.
Как насчет:
$str = '
/* other code */
$var = "var1";
/*
other code
$var = "var2";
other code
*/
/* other code */
$var = "var3";
/*
other code / <-- a slash here
$var = "var4";
other code
*/';
preg_match_all('~/\*(?:(?!\*/).)+?(\$var = .+?;).*?\*/~s', $str, $m);
print_r($m[1]);
Выход:
Array
(
[0] => $var = "var2";
[1] => $var = "var4";
)
Нечто подобное может работать:
/\/\*.*?\$var\s*\=\s(.*?)(?=\s*;)/s
Использование:
$str = '$var = .... ;
/*
other code
$var = ..... ;
other code
*/';
preg_match('/\/\*.*?\$var\s*\=\s(.*?)(?=\s*;)/s', $str, $matches);
var_dump($matches);
Будет выводить:
array(2) {
[0]=>
string(26) "/*
other code
$var = ....."[1]=>
string(5) "....."}
И ваша строка хранится в $matches[1]