从类中获取use语句


Get use statement from class

不太确定最好的标题,但我会尽我所能解释我所问的。假设我有以下文件:

MyCustomClass.php

<?php    
namespace MyNamespace;
use FooNamespace'FooClass;
use BarNamespace'BarClass as Bar;
use BazNamespace'BazClass as BazSpecial;
class MyCustomClass {
    protected $someDependencies = [];
    public function __construct(FooClass $foo, Bar $bar) {
        $someDependencies[] = $foo;
        $someDependencies[] = $bar;
    }
}

现在,如果我要使用反射,我可以从构造中的类型提示中获得完全限定的类名。

但是,我会收到FooNamespace'FooClassBarNamespace'BarClass。不是,FooNamespace'FooClassBarNamespace'Bar。我也不会得到BazNamespace'BazClass的引用。

基本上,我的问题是:我如何才能从MyCustomClass.php中获得完全限定的名称,而只知道FooClass, BarBazSpecial ?

我不想使用文件解析器,因为这会降低性能。我希望能够做这样的事情:

$class = new ReflectionClass('MyCustomClass');
...
$class->getUsedClass('FooClass'); // FooNamespace'FooClass
$class->getUsedClass('Bar'); // BarNamespace'BarClass
$class->getUsedClass('BazSpecial'); // BazNamespace'BazClass

我该怎么做呢?

看到没有人回答,我认为没有一个简单的方法来实现这一点。因此,我创建了自己的类ExtendedReflectionClass,它实现了我所需要的。

我已经用类文件和自述文件创建了一个要点,它在底部,所以可以滚动!

ExtendedReflectionClass

:

require 'ExtendedReflectionClass.php';
require 'MyCustomClass.php';
$class = new ExtendedReflectionClass('MyNamespace'Test'MyCustomClass');
$class->getUseStatements();    
// [
//     [
//         'class' => 'FooNamespace'FooClass',
//         'as' => 'FooClass'
//     ],
//     [
//         'class' => 'BarNamespace'BarClass',
//         'as' => 'Bar'
//     ],
//     [
//         'class' => 'BazNamespace'BazClass',
//         'as' => 'BazSpecial'
//     ]
// ]
$class->hasUseStatement('FooClass'); // true
$class->hasUseStatement('BarNamespace'BarClass'); // true
$class->hasUseStatement('BazSpecial'); // true
$class->hasUseStatement('SomeNamespace'SomeClass'); // false

我使用TokenFinderTool。

基本上,它使用令牌来提取use语句。

据我所知,不幸的是,php中的'Reflection对象还没有这样的方法。

下面的代码使用TokenFinder工具从文件中提取use import语句。

$tokens = token_get_all(file_get_contents("/path/to/MyCompany/MyClass.php"));
a(TokenFinderTool::getUseDependencies($tokens));

将输出:

array (size=9)
  0 => string 'Bat'CaseTool' (length=12)
  1 => string 'Bat'FileSystemTool' (length=18)
  2 => string 'Bat'StringTool' (length=14)
  3 => string 'Bat'ValidationTool' (length=18)
  4 => string 'CopyDir'AuthorCopyDirUtil' (length=25)
  5 => string 'PhpBeast'AuthorTestAggregator' (length=29)
  6 => string 'PhpBeast'PrettyTestInterpreter' (length=30)
  7 => string 'PhpBeast'Tool'ComparisonErrorTableTool' (length=38)
  8 => string 'Tiphaine'TiphaineTool' (length=21)

注意:如果你只有一个类名,你可以使用下面的代码片段:

$o = new 'ReflectionClass($className);
$tokens = token_get_all(file_get_contents($$o->getFileName()));
$useStatements = TokenFinderTool::getUseDependencies($tokens);

使用BetterReflection (composer req roave/better-reflection)

            $classInfo = (new BetterReflection())
                ->reflector()
                ->reflectClass($class);
            $uses = [];
            foreach ($classInfo->getDeclaringNamespaceAst()->stmts as $stmt) {
                foreach ($stmt->uses??[] as $use) {
                    $uses[] = join('''', $use->name->parts);
                }
            }
            print_r($uses);

这是我对类似任务的解决方案。解决方案没有考虑到选项as,因为我不需要它。但是简短而且有用。

    /**
     * @param class-string $className
     * @return string[]
     * @throws InternalServerErrorException
     */
    public static function getClassUseStatements(string $className): array
    {
        try {
            $reflectionClass = new 'ReflectionClass($className);
        } catch ('ReflectionException $e) {
            throw new InternalServerErrorException($e->getMessage());
        }
        if (false === $reflectionClass->getFileName()) {
            throw new InternalServerErrorException("Can not get fileName of class '$className'.");
        }
        $fileContent = 'file_get_contents($reflectionClass->getFileName());
        if (false === $fileContent) {
            throw new InternalServerErrorException(
                "Can not get contents from file {$reflectionClass->getFileName()}."
            );
        }
        $tokens = 'token_get_all($fileContent);
        $useStatements = [];
        while (null !== key($tokens)) {
            $token = 'current($tokens);
            if ('is_array($token) && $token[0] === T_USE) {
                'next($tokens);
                'next($tokens);
                $token = 'current($tokens);
                if ('is_array($token) && $token[0] === T_NAME_QUALIFIED) {
                    $useStatements[] = $token[1];
                }
            }
            'next($tokens);
        }
        return $useStatements;
    }