Упомянутый Сарфразом токенайзер PHP является хорошей идеей, особенно если вы собираетесь делать много переписывания кода помимо того, что вы упомянули здесь.
Однако этот случай может быть достаточно простым, он вам не понадобится.
Функция php, если она правильно сформирована, должна иметь:
1) «Голова», которая выглядит как function funcname($arg1,...,$argn)
. Вы, вероятно, можете найти это и извлечь это с помощью регулярного выражения.
2) После головы - «тело», которое будет состоять из всего, что находится после головы, в пару соответствующих фигурных скобок. Итак, вы должны выяснить, как соответствовать им. Один из способов сделать это - указать переменную $curlyBraceDepth
. Начните с 0, а затем, начиная с фигурной скобки, открывающей тело функции, проходите по коду по одному символу за раз. Каждый раз, когда вы сталкиваетесь с открывающей скобкой, увеличивайте $curlyBraceDepth
. Каждый раз, когда вы сталкиваетесь с закрывающей скобкой, уменьшайте ее. Когда $curlyBraceDepth < 1
(например, когда вы вернетесь на глубину 0), вы закончите проходить тело функции. Пока вы проходите проверку каждого символа, вам нужно либо накапливать каждый символ, который вы читаете, в массиве, либо, если у вас уже есть все это, в строке в памяти, отмечая начальную и конечную позиции так, Вы можете вытащить это позже.
Теперь, здесь есть большая оговорка: если какая-то из ваших функций обрабатывает непревзойденные фигурные скобки как символы внутри строк - не особенно часто, но абсолютно допустимый и возможный php - тогда вам также придется добавить условный код для разбора строк как отдельных токенов. В то время как вы могли бы написать свой собственный код, чтобы справиться и с этим, если вы беспокоитесь об этом как о «крайнем случае», вероятно, Tokenizer - это надежный путь.
Но вы все равно будете использовать что-то похожее на алгоритм, который я дал выше, когда будете сканировать токены, во всяком случае - найти токены, обозначающие голову, перебирать токены, составляющие тело, считая T_CURLY_OPEN и T_CURLY_CLOSE, чтобы отслеживать глубину вашей фигурной скобки, накапливая токены по мере продвижения и объединяя их, когда вы достигаете нулевой глубины фигурной скобки.
ОБНОВЛЕНИЕ (с использованием Tokenizer)
token_get_all
обеспечивает объединение отдельных символов источника в синтаксически значимые токены PHP. Вот быстрый пример. Допустим, у нас есть следующая строка исходного кода PHP:
$s = '<?php function one() { return 1; }';
И мы проходим через token_get_all
:
$tokens = token_get_all($s);
Если вы сделаете print_r
для этого, вот что вы увидите (с некоторыми встроенными комментариями):
Array
(
[0] => Array
(
[0] => 367 // token number (also known by constant T_OPEN_TAG)
[1] => <?php // token literal as found in source
[2] => 1
)
[1] => Array
(
[0] => 333 // token number (also known by constant T_FUNCTION)
[1] => function // token literal as found in source
[2] => 1
)
[2] => Array
(
[0] => 370 // token number (aka T_WHITESPACE)
[1] => // you can't see it, but it's there. :)
[2] => 1
)
[3] => Array
(
[0] => 307 // token number (aka T_STRING)
[1] => one // hey, it's the name of our function
[2] => 1
)
[4] => ( // literal token - open paren
[5] => ) // literal token - close paren
[6] => Array
(
[0] => 370
[1] =>
[2] => 1
)
[7] => {
[8] => Array
(
[0] => 370
[1] =>
[2] => 1
)
[9] => Array
(
[0] => 335
[1] => return
[2] => 1
)
[10] => Array
(
[0] => 370
[1] =>
[2] => 1
)
[11] => Array
(
[0] => 305
[1] => 1
[2] => 1
)
[12] => ;
[13] => Array
(
[0] => 370
[1] =>
[2] => 1
)
[14] => }
[15] => Array
(
[0] => 370
[1] =>
[2] => 1
)
[16] => Array
(
[0] => 369
[1] => ?>
[2] => 1
)
)
Обратите внимание, что некоторые записи в массиве являются символьными литералами (фактически, скобки и скобки, что делает это проще, чем я думал). Другие являются массивами, содержащими «номер токена» в индексе 0 и литерал токена в индексе 1 (понятия не имею, что это значение «1» в индексе 2). Если вам нужно «имя токена» - действительно, PHP-константа, которая вычисляется по номеру токена - вы можете использовать функцию token_name
. Например, этот знакомый первый токен с номером 367 называется именем и константой PHP T_OPEN_TAG.
Если вы хотите использовать это для копирования источника функции 'one' из файла A в файл B, вы можете выполнить $tokens = token_get_all(file_get_contents('file_A'))
, а затем найти последовательность буквенных токенов, которая обозначает начало этой функции - в нашем случае это T_FUNCTION, T_WHITESPACE и T_STRING, равное единице. Итак:
for($i=0,$z=count($tokens); $i<$z; $i++)
if( is_array($tokens[$i])
&& $tokens[$i][0] == T_FUNCTION
&& is_array($tokens[$i+1])
&& $tokens[$i+1][0] == T_WHITESPACE
&& is_array($tokens[$i+2])
&& $tokens[$i+2][1] == 'one')
break;
В этот момент вы должны сделать то, что я описал ранее: начать с открывающей фигурной скобки для тела функции с уровнем отступа 1, следить за токенами фигурных скобок, отслеживать глубину и накапливать токены:
$accumulator = array();
// collect tokens from function head through opening brace
while($tokens[$i] != '{' && ($i < $z)) {
$accumulator[] = is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i];
$i++;
}
if($i == $z) {
// handle error
} else {
// note, accumulate, and position index past brace
$braceDepth = 1;
$accumulator[] = '{';
$i++;
}
while($braceDepth > 0 && ($i < $z)) {
if(is_array($tokens[$i]))
$accumulator[] = $tokens[$i][1];
else {
$accumulator[] = $tokens[i];
if($tokens[$i] == '{') $braceDepth++;
else if($tokens[i] == '}') $braceDepth--;
}
}
$functionSrc = implode(null,$accumulator);