我能否通过函数名以编程方式获取函数的源代码?
例如:
function blah($a, $b) { return $a*$b; }
echo getFunctionCode("blah");
有没有 PHP 自描述函数来重构函数/类的代码?(我是指不是从源文件中直接获取源代码。)
在 Java 中存在:http://java.sun.com/developer/technicalArticles/ALT/Reflection/
我能否通过函数名以编程方式获取函数的源代码?
例如:
function blah($a, $b) { return $a*$b; }
echo getFunctionCode("blah");
有没有 PHP 自描述函数来重构函数/类的代码?(我是指不是从源文件中直接获取源代码。)
在 Java 中存在:http://java.sun.com/developer/technicalArticles/ALT/Reflection/
在使用 ReflectionFunction 的建议上进行扩展,您可以使用类似于以下代码:
$func = new ReflectionFunction('myfunction');
$filename = $func->getFileName();
$start_line = $func->getStartLine() - 1; // it's actually - 1, otherwise you wont get the function() block
$end_line = $func->getEndLine();
$length = $end_line - $start_line;
$source = file($filename);
$body = implode("", array_slice($source, $start_line, $length));
print_r($body);
$func = new ReflectionFunction('myfunction');
而是使用了 $reflectionClass->getMethod("myfunction")
- 不确定。 - jakabadambalazs没有什么东西可以给你实际的函数代码。唯一接近的是ReflectionFunction类。对于类,您有ReflectionClass,它提供了类成员(常量、变量和方法)及其可见性,但仍然没有实际的代码。
解决方法(它需要读取源文件):
使用ReflectionFunction::export查找函数声明所在的文件名和行间隔,然后从那个文件中读取这些行的内容。使用字符串处理获取第一个 {
和最后一个 }
之间的内容。
注意:Reflection API文档不完善。自PHP 7.4以来,ReflectionFunction::export已弃用。
我们使用不同的操作系统进行编程,如gnu/linux、Windows和Mac...由于这个原因,在代码中会有不同的回车符。为了解决这个问题,我分叉了Brandon Horsley的答案,并准备检查不同的回车符,并从一个类的方法中获取代码,而不是从一个函数中获取:
$cn = 'class_example';
$method = 'method_example';
$func = new ReflectionMethod($cn, $method);
$f = $func->getFileName();
$start_line = $func->getStartLine() - 1;
$end_line = $func->getEndLine();
$length = $end_line - $start_line;
$source = file($f);
$source = implode('', array_slice($source, 0, count($source)));
// $source = preg_split("/(\n|\r\n|\r)/", $source);
$source = preg_split("/".PHP_EOL."/", $source);
$body = '';
for($i=$start_line; $i<$end_line; $i++)
$body.="{$source[$i]}\n";
echo $body;
$lines = preg_split("/".PHP_EOL."/", $input);
- ZiTAL$source = implode('', array_slice($source, 0, count($source)));
这行代码。 - Richard Tyler Milesfile()
返回一系列行,将它们组合成一个字符串,然后将它们拆分回行似乎非常低效。 - IMSoP谢谢,最终函数。
function get_function($method,$class=null){
if (!empty($class)) $func = new ReflectionMethod($class, $method);
else $func = new ReflectionFunction($method);
$f = $func->getFileName();
$start_line = $func->getStartLine() - 1;
$end_line = $func->getEndLine();
$length = $end_line - $start_line;
$source = file($f);
$source = implode('', array_slice($source, 0, count($source)));
$source = preg_split("/".PHP_EOL."/", $source);
$body = '';
for($i=$start_line; $i<$end_line; $i++)
$body.="{$source[$i]}\n";
return $body;
}
$source = implode('', array_slice($source, 0, count($source)));
这行代码。 - Richard Tyler Miles<?php
namespace Phluid\Transpiler;
use ReflectionFunction;
final class Source
{
private const OPEN_NEST_CHARS = ['(', '[', '{'];
private const CLOSE_NEST_CHARS = [')', ']', '}'];
private const END_EXPRESSION_CHARS = [';', ','];
public static function doesCharBeginNest($char)
{
return \in_array($char, self::OPEN_NEST_CHARS);
}
public static function doesCharEndExpression($char)
{
return \in_array($char, self::END_EXPRESSION_CHARS);
}
public static function doesCharEndNest($char)
{
return \in_array($char, self::CLOSE_NEST_CHARS);
}
public static function readFunctionTokens(ReflectionFunction $fn, int $index = 0): array
{
$file = \file($fn->getFileName());
$tokens = \token_get_all(\implode('', $file));
$functionTokens = [];
$line = 0;
$readFunctionExpression = function ($i, &$functionTokens) use ($tokens, &$readFunctionExpression) {
$start = $i;
$nest = 0;
for (; $i < \count($tokens); ++$i) {
$token = $tokens[$i];
if (\is_string($token)) {
if (self::doesCharBeginNest($token)) {
++$nest;
} elseif (self::doesCharEndNest($token)) {
if ($nest === 0) {
return $i + 1;
}
--$nest;
} elseif (self::doesCharEndExpression($token)) {
if ($nest === 0) {
return $i + 1;
}
}
} elseif ($i !== $start && ($token[0] === \T_FN || $token[0] === \T_FUNCTION)) {
return $readFunctionExpression($i, $functionTokens);
}
$functionTokens[] = $token;
}
return $i;
};
for ($i = 0; $i < \count($tokens); ++$i) {
$token = $tokens[$i];
$line = $token[2] ?? $line;
if ($line < $fn->getStartLine()) {
continue;
} elseif ($line > $fn->getEndLine()) {
break;
}
if (\is_array($token)) {
if ($token[0] === \T_FN || $token[0] === \T_FUNCTION) {
$functionTokens = [];
$i = $readFunctionExpression($i, $functionTokens);
if ($index === 0) {
break;
}
--$index;
}
}
}
return $functionTokens;
}
}
Source::readFunctionTokens()
方法返回类似于PHP自带的\token_get_all()
函数的输出,只是过滤了从闭包开头到结尾的代码。因此,它是字符串和数组的混合,具体取决于PHP语法的需要,详见这里。
用法:
$fn = [fn() => fn() => $i = 0, function () { return 1; }];
$tokens = Source::readFunctionTokens(new \ReflectionFunction($fn[1]), 1);
第二个参数为0将返回最外层作用域中第一个闭包的代码,而1将返回最外层作用域中第二个闭包的代码。如果您想使用它,请随意整理它,因为代码非常粗糙和原始。但是,它应该相当稳定和有能力,因为我们已经知道所有语法都是有效的,并且可以遵循基本语法规则。
我会在这个帖子中添加另一种优秀代码的风味。这对我很有效。
请注意,您可以使用yourClassName::class替换self::class以帮助您的编辑器解析文件。
$methods = get_class_methods(self::class);
foreach ($methods as $method) {
$func = new ReflectionMethod(self::class, $method);
$f = $func->getFileName();
$start_line = $func->getStartLine() - 1;
$end_line = $func->getEndLine();
$length = $end_line - $start_line;
$source = file_get_contents($f);
$source = preg_split('/' . PHP_EOL . '/', $source);
$body = implode(PHP_EOL, array_slice($source, $start_line, $length));
echo $body . PHP_EOL . PHP_EOL;
}