PHP: How to get all classes when using autoloader

2019-05-07 14:14发布

I'm using composer to generate autoloader:

"autoload": {
  "psr-4": {
    "SomeNamespace\\": "src/SomeDir"
  }
}

I need to create instances of all classes that implement specific interface. It's fairly easy when not using autoloader, however get_declared_classes() is not playing well with autoloaders. It will list a class only after it was instantiated with autoloader.

3条回答
虎瘦雄心在
2楼-- · 2019-05-07 14:30

I had similar use case for all *Sniff.php classes found in some directory.

You can use Nette\RobotLoader.

Snippets from my app for inspiration: RobotLoaderFactory + Getting all the classes

And for your use case:

$src = __DIR__ . '/src';

$robotLoader = new Nette\Loaders\RobotLoader\RobotLoader;
$robotLoader->setTempDirectory(__DIR__ . '/temp');
$robotLoader->addDirectory($src);
$robotLoader->acceptFiles = ['*Suffix.php']; // optional to reduce file count
$robotLoader->rebuild();

$foundClasses = array_keys($robotLoader->getIndexedClasses());

// filter in any way
查看更多
看我几分像从前
3楼-- · 2019-05-07 14:31

You can use dirty hack and require_once all classes before get_declared_classes() function call.

  1. At first do dump all classes to class map via composer dumpautoload --optimize
  2. Now you can fetch composer loader and require all files
    $res = get_declared_classes();
    $autoloaderClassName = '';
    foreach ( $res as $className) {
        if (strpos($className, 'ComposerAutoloaderInit') === 0) {
            $autoloaderClassName = $className; // ComposerAutoloaderInit323a579f2019d15e328dd7fec58d8284 for me
            break;
        }
    }
    $classLoader = $autoloaderClassName::getLoader();
    foreach ($classLoader->getClassMap() as $path) {
        require_once $path;
    }
  1. Now you can easy use get_declared_classes() again, which return all classes the composer knowns

P.S. Don't use it in production.

查看更多
Fickle 薄情
4楼-- · 2019-05-07 14:36

If you don't keep track of which classes exist, autoloading won't help you.

But the situation would be pretty much the same if you tried to include all files and see which classes would come out of it, when there would be a chance that you missed a file.

Whatever it is what you try to do: Try it differently. I highly doubt you have to instantiate ALL these classes when serving one single request. It would not be very beneficial to the performance if you had to scan ALL declared classes for an interface, and then create an instance for all of them.

Provide some kind of registering method to let the application know about your classes. If you really have to use them all, so be it. But scanning whatever code has been run previously does not sound right (and you didn't explain what you do at all).

查看更多
登录 后发表回答