如何从文件中获取整个函数

SoL*_*oST 2 php file-io function

好的,我现在正在逐行阅读文件。我知道文件中的每个函数名称,因为它是在 XML 文档中的其他地方定义的。那应该是这样的:

function function_name
Run Code Online (Sandbox Code Playgroud)

其中 function_name 是函数的名称。

我从一个 XML 文档中获取了所有函数定义,我已经将它放入了一个函数名数组中,我只需要从 php 文件中获取这些函数。并重建该 php 文件,使其中仅包含这些功能。也就是说,如果一个 php 文件的函数比 XML 标签中定义的函数多,那么我需要去掉这些函数,只用用户在 XML 文件中指定的函数重写 .php 文件。

因此,我面临的困境是如何确定逐行读取函数的 END,并且我知道函数中可以包含函数。所以我不想删除其中的功能。只是独立的且未在随附的 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;
}
Run Code Online (Sandbox Code Playgroud)

好的,所以它需要这个 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();
}

?>
Run Code Online (Sandbox Code Playgroud)

并像这样改变它:

<?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





?>
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,这里带走了一大堆东西。缺少最后一个右括号......我需要做的就是检查该函数是否存在于此处function module_testing,然后获取整个函数并将其写入同一个文件。看起来很简单,但是哇,这是IMO这个小事情的一些主要编码......

或者,我也可以检查此处是否定义了不在 $functions 数组中的函数,如果是,则不只是删除该函数。也许用这种方法更容易?

Wes*_*n C 5

提到的 PHP 标记器 Sarfraz 是一个好主意,特别是如果您要进行大量代码重写而不是您在此处提到的内容。

但是,这种情况可能很简单,您不需要它。

一个 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; }';
Run Code Online (Sandbox Code Playgroud)

我们运行它token_get_all

$tokens = token_get_all($s);
Run Code Online (Sandbox Code Playgroud)

如果您对此进行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
        )

)
Run Code Online (Sandbox Code Playgroud)

请注意,数组中的某些条目是字符文字(实际上,括号和大括号使这比我想象的要容易)。其他是数组,包含在 0 索引处的“令牌编号”和 1 索引处的令牌文字(不知道 2 索引处的“1”值是什么)。如果您想要“令牌名称”——实际上,一个计算令牌编号的 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;
Run Code Online (Sandbox Code Playgroud)

此时,您将执行我之前描述的操作:从函数主体的左花括号开始,缩进级别为 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);
Run Code Online (Sandbox Code Playgroud)