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

标签 php function file-io

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

function function_name

其中 function_name 是函数的名称。

我从 XML 文档中获取了所有函数定义,这些定义已放入函数名称数组中,并且我需要从 php 文件中获取这些函数。并重建该 php 文件,使其仅包含这些函数。也就是说,如果 php 文件的功能多于 XML 标记中定义的功能,那么我需要删除这些功能,并仅使用用户在 XML 文件中指定的功能重写 .php 文件。

所以,我面临的困境是如何确定逐行读取的函数的结尾,并且我知道函数内部可以有函数。所以我不想删除其中的功能。只是独立的函数,并且未在随附的 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 中,并获取整个函数并将其写入同一个文件中。看起来很简单,但是哇,在我看来,这是一些主要的编码,只是为了这个小事情......

Or I could also check if a function is defined in here that isn't within the $functions array, if so, than just remove that function. Perhaps it's easier with this approach instead??

最佳答案

Sarfraz 提到的 PHP 分词器是一个好主意,特别是如果您要进行超出此处提到的大量代码重写时。

但是,这个案例可能很简单,您不需要它。

一个 PHP 函数,如果结构良好的话,应该具有:

1) 一个“头”,看起来像 function funcname($arg1,...,$argn) 。您也许可以找到它并使用正则表达式将其取出。

2) 在头部之后是“主体”,它将由头部之后包含在一对匹配的大括号内的所有内容组成。所以,你必须弄清楚如何匹配它们。一种方法是指定 $curlyBraceDepth多变的。从 0 开始,然后从打开函数主体的大括号开始,一次一个字符地浏览代码。每次遇到左大括号时,递增 $curlyBraceDepth 。每次遇到右大括号时,将其递减。当$curlyBraceDepth < 1 (例如,当您返回深度 0 时),您将完成函数体的遍历。当您检查每个字符时,您要么想要在数组中累积正在读取的每个字符,或者如果您已经将所有这些字符存储在内存中的字符串中,则标记开始和结束位置,以便你可以稍后把它拉出来。

现在,这里有一个很大的警告:如果您的任何函数将不匹配的大括号作为字符串内的字符处理 - 不是特别常见,但绝对合法且可能的 php - 那么您还必须添加将字符串解析为单独标记的条件代码。虽然您也可以编写自己的代码来处理这个问题,但如果您担心它是一个极端情况,那么 Tokenizer 可能是最可靠的方法。

但是,无论如何,当您扫描 token 时,您将使用类似于我上面给出的算法 - 找到表示头部的 token ,对构成主体的 token 进行排序,计算 T_CURLY_OPEN 和 T_CURLY_CLOSE 来跟踪您的大括号深度,在您移动时累积标记,并在达到零大括号深度时将它们连接起来。

更新(使用分词器)

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
        )

)

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

关于php - 如何从文件中获取整个函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2751032/

相关文章:

Javascript 'this' 作为参数

python - 有没有办法获得装饰器包装的功能?

c++ - 解析文件时输出错误?

php - 如何获得最大数量的不同值

php - 即使设置了 UTF-8 编码,在浏览器中显示俄文字母的问题

php - 如何在同一查询中同时使用 ON DUPLICATE KEY 和 REPLACE INTO ?

linux - Strace——打印内容而不是地址

php - 两种方式mysql数据库同步

r - Julia 相当于 R 的 `require()` 函数

java - 获取java war文件中的资源文件夹