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