不太确定最好的标题,但我会尽我所能解释我所问的。假设我有以下文件:
MyCustomClass.php
<?php
namespace MyNamespace;
use FooNamespaceFooClass;
use BarNamespaceBarClass as Bar;
use BazNamespaceBazClass as BazSpecial;
class MyCustomClass {
protected $someDependencies = [];
public function __construct(FooClass $foo, Bar $bar) {
$someDependencies[] = $foo;
$someDependencies[] = $bar;
}
}
现在,如果我要使用反射,我可以从构造中的类型提示中获得完全限定的类名。
但是,我会收到FooNamespaceFooClass
和BarNamespaceBarClass
。不是,FooNamespaceFooClass
和BarNamespaceBar
。我也不会得到BazNamespaceBazClass
的引用。
基本上,我的问题是:我如何才能从MyCustomClass.php
中获得完全限定的名称,而只知道FooClass
, Bar
和BazSpecial
?
我不想使用文件解析器,因为这会降低性能。我希望能够做这样的事情:
$class = new ReflectionClass('MyCustomClass');
...
$class->getUsedClass('FooClass'); // FooNamespaceFooClass
$class->getUsedClass('Bar'); // BarNamespaceBarClass
$class->getUsedClass('BazSpecial'); // BazNamespaceBazClass
我该怎么做呢?
看到没有人回答,我认为没有一个简单的方法来实现这一点。因此,我创建了自己的类ExtendedReflectionClass
,它实现了我所需要的。
我已经用类文件和自述文件创建了一个要点,它在底部,所以可以滚动!
ExtendedReflectionClass
:
require 'ExtendedReflectionClass.php';
require 'MyCustomClass.php';
$class = new ExtendedReflectionClass('MyNamespaceTestMyCustomClass');
$class->getUseStatements();
// [
// [
// 'class' => 'FooNamespaceFooClass',
// 'as' => 'FooClass'
// ],
// [
// 'class' => 'BarNamespaceBarClass',
// 'as' => 'Bar'
// ],
// [
// 'class' => 'BazNamespaceBazClass',
// 'as' => 'BazSpecial'
// ]
// ]
$class->hasUseStatement('FooClass'); // true
$class->hasUseStatement('BarNamespaceBarClass'); // true
$class->hasUseStatement('BazSpecial'); // true
$class->hasUseStatement('SomeNamespaceSomeClass'); // 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 'BatCaseTool' (length=12)
1 => string 'BatFileSystemTool' (length=18)
2 => string 'BatStringTool' (length=14)
3 => string 'BatValidationTool' (length=18)
4 => string 'CopyDirAuthorCopyDirUtil' (length=25)
5 => string 'PhpBeastAuthorTestAggregator' (length=29)
6 => string 'PhpBeastPrettyTestInterpreter' (length=30)
7 => string 'PhpBeastToolComparisonErrorTableTool' (length=38)
8 => string 'TiphaineTiphaineTool' (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;
}