Как получить всю функцию из файла - PullRequest
2 голосов
/ 01 мая 2010

Хорошо, я сейчас читаю файл, строка за строкой. Я знаю имя каждой функции в файле, так как оно определено в другом месте документа XML. Вот что должно быть так:

function function_name

Где имя_функции - это имя функции.

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

Итак, передо мной стоит дилемма, как построчно определять КОНЕЦ чтения функции, и я знаю, что функции могут иметь функции внутри них. Поэтому я не хочу удалять функции внутри них. Только функции, которые являются автономными и не определены в прилагаемом XML-файле. Любые идеи о том, как это сделать ??

Хорошо, сейчас я использую следующую функцию:

//!!! - Used to grab the contents of all functions within a file with the functions array.
function get_functions($source, $functions = array()) 
{
    global $txt;

    if (!file_exists($source) || !is_readable($source))
        return '';

    $tokens = token_get_all(file_get_contents($source));

    foreach($functions as $funcName)
    {
        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] == $funcName)
                break;

            $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
                fatal_error($txt['error_occurred'], false);
            } 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--;
               }
               $i++;
            }
            $functionSrc = implode(null,$accumulator);
        }
    }

    return $functionSrc;
}

ОК, поэтому он принимает содержимое php-файлов:

<?php
function module_testing($params)
{
    // Is it installed?
    $test_param = !isset($params['test_param']) ? 'Testing Testing 1 2 3!' : $params['test_param'];

    // Grab the params, if they exist.
    if (is_array($params))
    {           
        echo $test_param;
    }
    // Throw an error.
    else
        module_error();
}

?>

и меняет его так:

<?php

function module_testing($params)

{

    // Is it installed?

    $test_param  isset$params'test_param'  'Testing Testing 1 2 3!'  $params'test_param'



    // Grab the params, if they exist.

    if is_array$params



        echo $test_param



    // Throw an error.

    else

        module_error





?>

Как вы можете видеть, это заняло целую кучу вещей. И последняя закрывающая скобка отсутствует ... Все, что мне нужно сделать, это проверить, существует ли здесь функция function module_testing, взять всю функцию и записать ее в тот же файл. Кажется достаточно простым, но WoW, это какая-то основная кодировка только для этой незначительной вещи IMO ...

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

Ответы [ 4 ]

4 голосов
/ 01 мая 2010

Упомянутый Сарфразом токенайзер 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);
1 голос
/ 01 мая 2010

Возможно, вы захотите попробовать PHP Tokenizer.

http://www.php.net/manual/en/ref.tokenizer.php

Из внешнего скрипта:

<?php

var_dump(token_get_all(file_get_contents('myscript.php')));

?>
0 голосов
/ 02 мая 2010

Хорошо, ребята, мне удалось исправить это совершенно нормально, и самостоятельно, и вот идеальное решение. Я хочу поблагодарить вас всех за вашу помощь в этом. Спасибо, вы, ребята, пошли намного дальше, чем помогаете мне здесь. Но я знал, что это будет простое решение без использования функций tokenizer. Возможно, вы, ребята, забыли, что у меня есть название каждой функции? В любом случае, еще раз спасибо, но функции токена для этого не понадобятся.

Приветствие.

function remove_undefined_functions($source, $functions = array())
{
    if (!file_exists($source) || !is_readable($source))
        return '';

    $code = '';
    $removeStart = false;

    $fp = fopen($source, 'rb');
    while (!feof($fp))
    {
        $output = fgets($fp);
        $funcStart = strpos(strtolower($output), 'function');

        if ($funcStart !== false)
        {
            foreach($functions as $funcName)
            {
                if (strpos($output, $funcName) !== false)
                {
                    $code .= $output;
                    $removeStart = false;
                    break;
                }
                else
                    $removeStart = true;
            }
            continue;
        }
        else
        {
            if (substr($output, 0, 2) == '?>' || !$removeStart)
                $code .= $output;
        }
    }
    fclose($fp);

    // Rewrite the file with the functions that are defined.
    $fo = @fopen($source, 'wb');

    // Get rid of the extra lines...
    @fwrite($fo, str_replace("\r\n", "\n", $code));

    fclose($fo);
}

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

0 голосов
/ 01 мая 2010

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

Но что, если в вашей функции есть функция, предложение if или что-то еще, которая также использует эти скобки? Чтобы справиться с этим, вы должны ввести $counter, который рассчитывает для каждого { и вниз для каждого }. Если counter = zero достигнут конец функции.

Пример: Ваша функция:

//lots of functions
function f_unimportant($args) { //Scan the first "{" after your f_unimportant
                                //and set $counter=1;
if($args > '') {                //increase $counter by 1
   //Do stuff
}                               //decrease $counter by 1

echo $result;

}                               //decrease $counter by 1
                                //now $counter is zero and end of function is reached

Счетчик сообщает вам глубину вашего кода. Если глубина = 0, функция завершена.

Анализ: у вас есть массив символов $, в котором хранится ваш php-файл, начиная с function f_unimportant($args) {.

$counter = 1;
$length = 0; //length of your function (to be able to delete it)
foreach($array as $char) {
   $length ++;
   if($char == '{') {
      $counter ++;
   }
   else if($char == '}') {
      $counter --;
   }

   if($counter == 0) {break;} //leave foreach because end of function is reached
}
//now you just delete $length chars from your phpfile starting at the position
//you already found out, where your function starts.

и не забудьте удалить function f_unimportant($args) { также (это не считается в $ length!)

...