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 数组中的函数,如果是,则不只是删除该函数。也许用这种方法更容易?
提到的 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)