好的,我现在正在逐行阅读一个文件。我知道文件中的每个函数名称,因为它是在 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/