2010-05-01 20 views
2

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

function function_name 

其中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; 
} 

OK,所以它需要这个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中,并获取整个函数并将其写入同一个文件。看起来很简单,但天啊,这只是IMO这个小东西的一些主要编码...

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

+0

伙计们,我已经解决了这个问题。请参阅下面的答案。再次感谢:) – SoLoGHoST 2010-05-02 06:40:27

回答

-1

好吧,伙计们,我设法解决这个问题完全正常,和我自己的,这里是完美解决方案我想感谢大家对此的帮助。谢谢,你们已经远远超出了帮助我。但我知道这将是一个简单的解决方案,而不使用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); 
} 

,这将使它所以,如果有一个函数内部的功能,比用户必须定义​​它,否则,该功能将无法正常工作。所以这对我来说并不是什么大问题,因为它们可以具有无限的功能,并且更适合每个功能都是它自己的功能。

1

你可能想尝试的PHP标记生成器。

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

从外部脚本:

<?php 

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

?> 
+0

OMG,这是如何工作的?我看到它的输出,但是如果它对我来说是有意义的... – SoLoGHoST 2010-05-01 18:46:32

+0

如何使用该方法来确定函数的开始和结束?我的意思是,看看上面的var_dump ...?函数名称是“module_testing”,是的,我看到它,但我怎么用这个,也许一个例子会很好。 – SoLoGHoST 2010-05-01 19:08:00

+0

@SoLoGHoST:我刚刚给出了一个使用'var_dump'的示例,请参阅'token_get_all'的文档以了解更多信息:http://php.net/manual/en/function.token-get-all.php – Sarfraz 2010-05-01 19:12:48

4

标记者Sarfraz的PHP提到的是一个好主意,特别是如果你打算做大量的代码重写高于您什么在这里提到。

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

PHP函数,如果它是良好的,应该有:

1) “头”,它看起来像function funcname($arg1,...,$argn)。你或许可以找到这个,并用正则表达式来解决这个问题。

2)头部后面是一个“身体”,它将包含在一对相配的大括号内的头部之后的所有东西。所以,你必须弄清楚如何匹配它们。一种方法是指定一个$curlyBraceDepth变量。从0开始,然后从打开函数主体的大括号开始,每次遍历代码一个字符。每次遇到大括号时,都会增加$curlyBraceDepth。每当你遇到一个大括号时,就减少它。当$curlyBraceDepth < 1(例如,当您返回到深度0时),您将完成该函数的主体。当你检查每个字符时,你要么想要将你正在读的每个字符累加到一个数组中,要么你已经将所有这些全部放入了内存中的一个字符串中,这样就标记了开始和结束位置你可以稍后再拉出来。

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

但是,当您扫描标记时,您会使用类似于上述算法的东西 - 找到表示标头的标记,对构成标记的标记进行排序,计算T_CURLY_OPEN和T_CURLY_CLOSE以跟踪您的支架深度,当您到达时积累令牌并在您达到零支架深度时连接它们。

UPDATE(使用标记生成器)

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索引处的标记字面值(不知道2索引处的'1'值是什么)。如果你想要“令牌名称” - 实际上是一个PHP常量,它的值是令牌数 - 你可以使用token_name函数。例如,那个熟悉的第一个令牌,编号为367,由名称和PHP常量T_OPEN_TAG引用。

如果您想使用此功能将函数'one'的来源从文件A复制到文件B,则可以执行$tokens = token_get_all(file_get_contents('file_A')),然后搜索表示该函数开始的文字标记序列 - in我们的例子,T_FUNCTION,T_WHITESPACE和一个等于'one'的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); 
+0

谢谢,但我不明白这个标记器的东西,它似乎很复杂,我想要做的。即使在通过Sarfraz链接的链接阅读完之后。 – SoLoGHoST 2010-05-01 19:23:50

+0

这有点复杂,但真的没有那么糟糕。我添加了一个试图解释它如何与更多示例代码更紧密协作的部分。 – 2010-05-01 22:02:01

+0

哇,谢谢很多兄弟:) – SoLoGHoST 2010-05-01 22:22:57

0

一个功能 - 据我所知 - 总是包含在这些括号:{}。 所以你的工作是扫描函数的开始的phpfile - 你说这不是问题 - 然后你必须扫描到目前为止所有打开{关闭。

但是,如果函数中有函数或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 

计数器告诉你你的代码的深度。如果depth = 0函数已经结束。

分析:你有一个$数组的字符,你的phpfile存储在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) {藏汉(它没有计入$长度!)

+0

现在这是一个非常有趣的概念:) – SoLoGHoST 2010-05-01 19:15:35

+0

我希望它能工作:-)我没有尝试过自己。最重要的是,使用{或}的函数内部没有任何字符串,比如“abc foo {bar”,因为这样它就会变得复杂...... – user329974 2010-05-01 19:36:18

+0

是的,我明白你的意思了。嗯,也许令牌的东西是唯一保证的方式...非常感谢:) – SoLoGHoST 2010-05-01 20:11:07

相关问题