PHP-获取特定命名空间中的所有类名
我想获取命名空间中的所有类。我有这样的想法:PHP-获取特定命名空间中的所有类名,php,namespaces,Php,Namespaces,我想获取命名空间中的所有类。我有这样的想法: #File: MyClass1.php namespace MyNamespace; class MyClass1() { ... } #File: MyClass2.php namespace MyNamespace; class MyClass2() { ... } #Any number of files and classes with MyNamespace may be specified. #File: ClassHandle
#File: MyClass1.php
namespace MyNamespace;
class MyClass1() { ... }
#File: MyClass2.php
namespace MyNamespace;
class MyClass2() { ... }
#Any number of files and classes with MyNamespace may be specified.
#File: ClassHandler.php
namespace SomethingElse;
use MyNamespace as Classes;
class ClassHandler {
public function getAllClasses() {
// Here I want every classes declared inside MyNamespace.
}
}
我尝试在getAllClasses()
内部getAllClasses()
获取声明的类(),但是MyClass1
和MyClass2
不在列表中
我该怎么做呢?最简单的方法应该是使用您自己的autoloader
\uu autoload
函数,并在其中保存加载的类名。那适合你吗
否则,我认为您将不得不处理一些反射方法。
class\u parents
,spl\u classes()
和class\u uses
可用于检索所有类名非常有趣的是,似乎没有任何反射方法可以为您这样做。然而,我提出了一个能够读取名称空间信息的小类
为此,您必须遍历所有定义的类。然后我们得到该类的名称空间,并将其与类名本身一起存储到一个数组中
<?php
// ClassOne namespaces -> ClassOne
include 'ClassOne/ClassOne.php';
// ClassOne namespaces -> ClassTwo
include 'ClassTwo/ClassTwo.php';
include 'ClassTwo/ClassTwoNew.php';
// So now we have two namespaces defined
// by ourselves (ClassOne -> contains 1 class, ClassTwo -> contains 2 classes)
class NameSpaceFinder {
private $namespaceMap = [];
private $defaultNamespace = 'global';
public function __construct()
{
$this->traverseClasses();
}
private function getNameSpaceFromClass($class)
{
// Get the namespace of the given class via reflection.
// The global namespace (for example PHP's predefined ones)
// will be returned as a string defined as a property ($defaultNamespace)
// own namespaces will be returned as the namespace itself
$reflection = new \ReflectionClass($class);
return $reflection->getNameSpaceName() === ''
? $this->defaultNamespace
: $reflection->getNameSpaceName();
}
public function traverseClasses()
{
// Get all declared classes
$classes = get_declared_classes();
foreach($classes AS $class)
{
// Store the namespace of each class in the namespace map
$namespace = $this->getNameSpaceFromClass($class);
$this->namespaceMap[$namespace][] = $class;
}
}
public function getNameSpaces()
{
return array_keys($this->namespaceMap);
}
public function getClassesOfNameSpace($namespace)
{
if(!isset($this->namespaceMap[$namespace]))
throw new \InvalidArgumentException('The Namespace '. $namespace . ' does not exist');
return $this->namespaceMap[$namespace];
}
}
$finder = new NameSpaceFinder();
var_dump($finder->getClassesOfNameSpace('ClassTwo'));
定位类
类可以通过其名称和名称空间在文件系统中进行本地化,就像自动加载程序一样。在正常情况下,名称空间应该告诉类文件的相对路径。包含路径是相对路径的起点。函数返回一个字符串中包含路径的列表。可以测试每个include路径是否存在与命名空间匹配的相对路径。如果找到匹配的路径,您将知道类文件的位置
获取类名
一旦知道类文件的位置,就可以从文件名中提取类,因为类文件的名称应该由类名后跟.php
组成
示例代码
下面是一个示例代码,用于将命名空间foo\bar
的所有类名作为字符串数组获取:
$namespace='foo\bar';
//相对命名空间路径
$namespaceRelativePath=str\u replace('\\',目录分隔符,$namespace);
//包括路径
$includePathStr=get_include_path();
$includePathArr=分解(路径分隔符,$includePathStr);
//迭代包含路径
$classArr=array();
foreach($includePath作为$includePath){
$path=$includePath.DIRECTORY\u SEPARATOR.$namespaceRelativePath;
如果(is_dir($path)){//路径是否存在?
$dir=dir($path);//dir句柄
而(false!==($item=$dir->read()){//read dir中的下一项
$matches=array();
if(preg_match('/^(?[^.].+)\.php$/',$item,$matches)){
$classArr[]=$matches['class'];
}
}
$dir->close();
}
}
//调试输出
var_dump(包括Atharr);
var_dump($classArr);
通用方法是在项目中获取所有完全限定的类名(具有完整命名空间的类),然后按所需命名空间进行筛选
PHP提供了一些本机函数来获取这些类(get_声明的_类等),但它们无法找到尚未加载的类(include/require),因此自动加载程序(例如Composer)无法正常工作。
这是一个主要问题,因为自动加载器的使用非常普遍
因此,您最后的办法是自己查找所有PHP文件并对其进行解析,以提取其名称空间和类:
$path = __DIR__;
$fqcns = array();
$allFiles = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
$phpFiles = new RegexIterator($allFiles, '/\.php$/');
foreach ($phpFiles as $phpFile) {
$content = file_get_contents($phpFile->getRealPath());
$tokens = token_get_all($content);
$namespace = '';
for ($index = 0; isset($tokens[$index]); $index++) {
if (!isset($tokens[$index][0])) {
continue;
}
if (T_NAMESPACE === $tokens[$index][0]) {
$index += 2; // Skip namespace keyword and whitespace
while (isset($tokens[$index]) && is_array($tokens[$index])) {
$namespace .= $tokens[$index++][1];
}
}
if (T_CLASS === $tokens[$index][0] && T_WHITESPACE === $tokens[$index + 1][0] && T_STRING === $tokens[$index + 2][0]) {
$index += 2; // Skip class keyword and whitespace
$fqcns[] = $namespace.'\\'.$tokens[$index][1];
# break if you have one class per file (psr-4 compliant)
# otherwise you'll need to handle class constants (Foo::class)
break;
}
}
}
如果您遵循PSR0或PSR4标准(您的目录树反映了您的名称空间),则不必过滤任何内容:只需给出与所需名称空间对应的路径即可
如果您不喜欢复制/粘贴上述代码段,只需安装此库:。
如果您使用PHP>=5.5,还可以使用以下库:。我将给出一个示例,该示例实际上在我们的Laravel 5应用程序中使用,但几乎可以在任何地方使用。该示例返回带有名称空间的类名,如果不需要,可以很容易地将其取出
传奇
- {{1}}-要从当前文件的路径中删除以访问应用程序文件夹的路径
- {{2}}-目标类所在的应用程序文件夹的文件夹路径
- {{3}}-命名空间路径
代码
Laravel用户可以使用app_path()/{{2}/*.php'
在glob()中。更新:由于这个答案变得有些流行,我创建了一个packagist包来简化事情。它基本上包含了我在这里描述的内容,无需自己添加类或手动配置$approt
。它最终可能支持的不仅仅是PSR-4
可以在此处找到该包:
请参阅自述文件中的更多信息
我对这里的任何解决方案都不满意,所以我最终构建了自己的类来处理这个问题此解决方案要求您:
- 使用作曲家
- 使用PSR-4
简而言之,这个类试图根据您在composer.json
中定义的名称空间,找出类在文件系统中的实际位置。例如,命名空间Backup\Test
中定义的类可以在/home/hpierce/BackupApplicationRoot/src/Test
中找到。这是可以信任的,因为将目录结构映射到命名空间是:
“名称空间前缀”后的连续子名称空间名称
对应于“基本目录”中的子目录,其中
命名空间分隔符表示目录分隔符。子目录
名称必须与子命名空间名称的大小写匹配
您可能需要调整approt
以指向包含composer.json
的目录
您可以使用获取声明的类
,但需要做一些额外的工作
$needleNamespace = 'MyNamespace';
$classes = get_declared_classes();
$neededClasses = array_filter($classes, function($i) use ($needleNamespace) {
return strpos($i, $needleNamespace) === 0;
});
因此,首先获取所有已声明的类,然后检查其中哪些类以名称空间开头
注意:您将得到一个数组,其中键不以0开头。要实现这一点,您可以尝试:array\u values($neededClasses)
$ composer require haydenpierce/class-finder
$needleNamespace = 'MyNamespace';
$classes = get_declared_classes();
$neededClasses = array_filter($classes, function($i) use ($needleNamespace) {
return strpos($i, $needleNamespace) === 0;
});
<?php
namespace Backup\Util;
final class ClassFinder
{
private static $composer = null;
private static $classes = [];
public function __construct()
{
self::$composer = null;
self::$classes = [];
self::$composer = require APP_PATH . '/vendor/autoload.php';
if (false === empty(self::$composer)) {
self::$classes = array_keys(self::$composer->getClassMap());
}
}
public function getClasses()
{
$allClasses = [];
if (false === empty(self::$classes)) {
foreach (self::$classes as $class) {
$allClasses[] = '\\' . $class;
}
}
return $allClasses;
}
public function getClassesByNamespace($namespace)
{
if (0 !== strpos($namespace, '\\')) {
$namespace = '\\' . $namespace;
}
$termUpper = strtoupper($namespace);
return array_filter($this->getClasses(), function($class) use ($termUpper) {
$className = strtoupper($class);
if (
0 === strpos($className, $termUpper) and
false === strpos($className, strtoupper('Abstract')) and
false === strpos($className, strtoupper('Interface'))
){
return $class;
}
return false;
});
}
public function getClassesWithTerm($term)
{
$termUpper = strtoupper($term);
return array_filter($this->getClasses(), function($class) use ($termUpper) {
$className = strtoupper($class);
if (
false !== strpos($className, $termUpper) and
false === strpos($className, strtoupper('Abstract')) and
false === strpos($className, strtoupper('Interface'))
){
return $class;
}
return false;
});
}
}
$result1 = $finder->in(__DIR__)->files()->contains('namespace foo;');
$result2 = $finder->in(__DIR__)->files()->contains('namespace bar;');
public function find(array $excludes, ?string $needle = null)
{
$path = "../".__DIR__;
$files = scandir($path);
$c = count($files);
$models = [];
for($i=0; $i<$c; $i++) {
if ($files[$i] == "." || $files[$i] == ".." || in_array($dir[$i], $excludes)) {
continue;
}
$model = str_replace(".php","",$dir[$i]);
if (ucfirst($string) == $model) {
return $model;
}
$models[] = $model;
}
return $models;
}
function classes_in_namespace($namespace) {
$namespace .= '\\';
$myClasses = array_filter(get_declared_classes(), function($item) use ($namespace) { return substr($item, 0, strlen($namespace)) === $namespace; });
$theClasses = [];
foreach ($myClasses AS $class):
$theParts = explode('\\', $class);
$theClasses[] = end($theParts);
endforeach;
return $theClasses;
}
$MyClasses = classes_in_namespace('namespace\sub\deep');
var_dump($MyClasses);
$classes = ClassHelper::findRecursive(__NAMESPACE__);
print_r($classes);
Array
(
[0] => Helpers\Dir\Getters\Bar
[1] => Helpers\Dir\Getters\Foo\Bar
[2] => Helpers\DirSame\Getters\Foo\Cru
[3] => Helpers\DirSame\Modifiers\Foo\Biz
[4] => Helpers\DirSame\Modifiers\Too\Taz
[5] => Helpers\DirOther\Modifiers\Boo
)
function get_available_widgets()
{
$namespaces = array_keys((new ComposerClassMap)->listClasses());
return array_filter($namespaces, function($item){
return Str::startsWith($item, "App\\Modules\\Widgets\\") && Str::endsWith($item, "Controller");
});
}
public function getAllNameSpaces($path)
{
$filenames = $this->getFilenames($path);
$namespaces = [];
foreach ($filenames as $filename) {
$namespaces[] = $this->getFullNamespace($filename) . '\\' . $this->getClassName($filename);
}
return $namespaces;
}
private function getClassName($filename)
{
$directoriesAndFilename = explode('/', $filename);
$filename = array_pop($directoriesAndFilename);
$nameAndExtension = explode('.', $filename);
$className = array_shift($nameAndExtension);
return $className;
}
private function getFullNamespace($filename)
{
$lines = file($filename);
$array = preg_grep('/^namespace /', $lines);
$namespaceLine = array_shift($array);
$match = [];
preg_match('/^namespace (.*);$/', $namespaceLine, $match);
$fullNamespace = array_pop($match);
return $fullNamespace;
}
private function getFilenames($path)
{
$finderFiles = Finder::create()->files()->in($path)->name('*.php');
$filenames = [];
foreach ($finderFiles as $finderFile) {
$filenames[] = $finderFile->getRealpath();
}
return $filenames;
}