首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何获取特定命名空间中的所有类名?

如何获取特定命名空间中的所有类名?
EN

Stack Overflow用户
提问于 2014-03-31 20:28:54
回答 15查看 64.1K关注 0票数 71

我想要获取一个名称空间中的所有类。我有这样的东西:

代码语言:javascript
运行
复制
#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()中尝试了get_declared_classes(),但是MyClass1MyClass2不在列表中。

我怎么能这么做呢?

EN

回答 15

Stack Overflow用户

发布于 2014-12-12 17:30:34

一般的方法是获取项目中的所有完全限定的类名(具有完整命名空间的类),然后根据所需的命名空间进行过滤。

PHP提供了一些本机函数来获取这些类(get_declared_classes等),但它们无法找到尚未加载的类(include / require),因此它不能像预期的那样使用自动加载器(例如Composer )。这是一个主要问题,因为自动加载器的使用非常普遍。

因此,您最后的办法是自己找到所有PHP文件,并解析它们以提取它们的名称空间和类:

代码语言:javascript
运行
复制
$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标准(目录树反映了您的名称空间),则不必过滤任何内容:只需提供与所需名称空间对应的路径即可。

如果你不喜欢复制/粘贴上面的代码片段,你可以简单地安装这个库:https://github.com/gnugat/nomo-spaco。如果您使用PHPPHP5.5,您还可以使用下列库:https://github.com/hanneskod/classtools

票数 43
EN

Stack Overflow用户

发布于 2016-10-25 08:41:16

更新:自从这个答案变得有些流行以来,我已经创建了一个打包程序包来简化事情。它基本上包含了我在这里描述的内容,不需要自己添加类或手动配置$appRoot。它可能最终支持的不仅仅是PSR-4。

这个包可以在这里找到:haydenpierce/class-finder

代码语言:javascript
运行
复制
$ composer require haydenpierce/class-finder

在自述文件中查看更多信息。

我对这里的任何解决方案都不满意,所以我最终构建了我的类来处理这个问题。此解决方案要求您是用户

使用Composer

  • Using PSR-4

简而言之,这个类试图根据您在composer.json中定义的名称空间找出类在您的文件系统中的实际位置。例如,在/home/hpierce/BackupApplicationRoot/src/Test中可以找到在名称空间Backup\Test中定义的类。这是可信的,因为将目录结构映射到命名空间是required by PSR-4

“名字空间前缀”之后的邻接子名字空间名称对应于“基本目录”内的一个子目录,其中名字空间分隔符表示目录分隔符。子目录名称必须与子命名空间名称的大小写匹配。

您可能需要调整appRoot以指向包含composer.json的目录。

代码语言:javascript
运行
复制
<?php    
namespace Backup\Util;

class ClassFinder
{
    //This value should be the directory that contains composer.json
    const appRoot = __DIR__ . "/../../";

    public static function getClassesInNamespace($namespace)
    {
        $files = scandir(self::getNamespaceDirectory($namespace));

        $classes = array_map(function($file) use ($namespace){
            return $namespace . '\\' . str_replace('.php', '', $file);
        }, $files);

        return array_filter($classes, function($possibleClass){
            return class_exists($possibleClass);
        });
    }

    private static function getDefinedNamespaces()
    {
        $composerJsonPath = self::appRoot . 'composer.json';
        $composerConfig = json_decode(file_get_contents($composerJsonPath));

        return (array) $composerConfig->autoload->{'psr-4'};
    }

    private static function getNamespaceDirectory($namespace)
    {
        $composerNamespaces = self::getDefinedNamespaces();

        $namespaceFragments = explode('\\', $namespace);
        $undefinedNamespaceFragments = [];

        while($namespaceFragments) {
            $possibleNamespace = implode('\\', $namespaceFragments) . '\\';

            if(array_key_exists($possibleNamespace, $composerNamespaces)){
                return realpath(self::appRoot . $composerNamespaces[$possibleNamespace] . implode('/', $undefinedNamespaceFragments));
            }

            array_unshift($undefinedNamespaceFragments, array_pop($namespaceFragments));            
        }

        return false;
    }
}
票数 40
EN

Stack Overflow用户

发布于 2019-04-26 13:15:31

上面有几个有趣的答案,其中一些对于所提出的任务来说实际上是特别复杂的。

为了给这些可能性增加一种不同的风格,这里有一个快速而简单的非优化函数,它可以使用我能想到的最基本的技术和常见语句来完成您所要求的操作:

代码语言:javascript
运行
复制
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;
}

简单地使用:

代码语言:javascript
运行
复制
$MyClasses = classes_in_namespace('namespace\sub\deep');

var_dump($MyClasses);

我编写此函数的目的是假设您在名称空间中添加尾随的最后一个斜杠“”(\),因此您不需要使用两倍的斜杠来转义它。;)

请注意,此函数只是一个示例,有许多缺陷。基于上面的例子,如果你使用'namespace\sub‘和'namespace\sub\deep’exists,这个函数将返回在这两个命名空间中找到的所有类(就像它是递归的一样)。但是,调整和扩展此函数将非常简单,主要需要在foreach块中进行一些调整。

它可能不是code-art-nouveau的顶峰,但至少它做了所提出的事情,并且应该足够简单,以便不言自明。

我希望它能为你实现你想要的东西铺平道路。

注意: PHP 5、7和8是友好的。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22761554

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档