php 重构/获取php函数的代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7026690/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 01:50:19  来源:igfitidea点击:

reconstruct/get code of php function

phpreflection

提问by Marek Sebera

Can I programmatically get a code of function by it's name?

我可以通过它的名称以编程方式获取函数代码吗?

Like:

喜欢:

function blah($a, $b) { return $a*$b; }
echo getFunctionCode("blah");

is it possible?

是否可以?

Are there any php self-descriptive functions to reconstruct function/class code?
(mean instead of getting source code right from sources file)

有没有php自描述函数来重构函数/类代码?
(意味着而不是从源文件中获取源代码)

In Java there exists: http://java.sun.com/developer/technicalArticles/ALT/Reflection/

在 Java 中存在:http: //java.sun.com/developer/technicalArticles/ALT/Reflection/

In PHP:
get_class_methods()
typeof
ReflectionFunction (thanks Alin Purcaru)

在 PHP 中:
get_class_methods()
typeof
ReflectionFunction(感谢 Alin Purcaru)

回答by Brandon Horsley

Expanding on the suggestion to use the ReflectionFunction, you could use something like this:

扩展使用 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);

回答by Alin Purcaru

There isn't anything that will give you the actual code of the function. The only thing close to that available is the ReflectionFunctionclass. For classes you have ReflectionClassthat gives you the class members (constants, variables and methods) and their visibility, but still no actual code.

没有任何东西可以为您提供函数的实际代码。唯一接近可用的是ReflectionFunction类。对于类,您有ReflectionClass,它为您提供类成员(常量、变量和方法)及其可见性,但仍然没有实际代码。



Workaround(it does involve reading the source file):
Use ReflectionFunction::exportto find out the file name and line interval where the function is declared, then read the content from that file on those lines. Use string processing to get what's between the first {and the last }.

解决方法(它确实涉及读取源文件):
使用ReflectionFunction::export找出声明函数的文件名和行间隔,然后从这些行读取该文件的内容。使用字符串处理来获取第一个{和最后一个之间的内容}

Note: The Reflection API is poorly documented.

注意:反射 API 的文档很差。

回答by ZiTAL

We program through different operating systems, gnu/linux, windows, mac... Due to this, we have different carriage returns in the code, to solve this, I forked the Brandon Horsley's answer and prepare to check different CR and get code from an class's method instead of a function:

我们通过不同的操作系统进行编程,gnu/linux、windows、mac...因此,我们在代码中有不同的回车,为了解决这个问题,我分叉了 Brandon Horsley 的答案并准备检查不同的 CR 并从中获取代码一个类的方法而不是一个函数:

$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;

回答by emmanuel

thank you, final function

谢谢,最后的功能

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

回答by Deji

I have a similar need and after learning that \ReflectionFunctiononly has information on the start and end lines, felt it necessary to write some code to extract the code of a closure or more likely a short closure when multiple may exist on the same line and even be nested (better safe than sorry). The one caveat is you have to know whether it's the 1st, 2nd, etc. closure, which you probably do somewhat if they have been passed as an argument list or array.

我有类似的需求,在了解到\ReflectionFunction只有开始和结束行的信息后,我觉得有必要编写一些代码来提取闭包的代码,或者更可能是在同一行上可能存在多个甚至是短闭包的情况下嵌套(比抱歉更安全)。一个警告是你必须知道它是第一个、第二个等闭包,如果它们作为参数列表或数组传递,你可能会做一些事情。

I have very specific desires in my case, but maybe the general solution of getting the code of a closure will be useful to others, so I'll drop it here...

在我的情况下,我有非常具体的愿望,但也许获取闭包代码的通用解决方案对其他人有用,所以我将其放在这里......

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

The Source::readFunctionTokens()method will return similar output to PHP's own \token_get_all()function, just filtered down to only the code from the start of the closure to the end. As such, it's a mix of strings and arrays depending on PHP's syntactical needs, see here.

Source::readFunctionTokens()方法将返回与 PHP 自己的\token_get_all()函数类似的输出,只是过滤到仅从闭包开始到结束的代码。因此,它是字符串和数组的混合,具体取决于 PHP 的语法需求,请参见此处

Usage:

用法:

$fn = [fn() => fn() => $i = 0, function () { return 1; }];
$tokens = Source::readFunctionTokens(new \ReflectionFunction($fn[1]), 1);

0 as the second arg will return the code for the first closure in the outermost scope, and 1 will return the second closure in the outermost scope. The code is very rough and raw so feel obliged to neaten it up if you wish to use it. It shouldbe pretty stable and capable though, since we already know all syntax is valid and can go off basic syntax rules.

0 作为第二个 arg 将返回最外层作用域中第一个闭包的代码,而 1 将返回最外层作用域中的第二个闭包。代码非常粗糙和原始,所以如果你想使用它,觉得有必要把它整理一下。不过它应该非常稳定和强大,因为我们已经知道所有语法都是有效的并且可以脱离基本的语法规则。