Update: Since this answer became somewhat popular, I’ve created a packagist package to simplify things. It contains basically what I’ve described here, without the need to add the class yourself or configure the $appRoot
manually. It may eventually support more than just PSR-4.
That package can be found here: haydenpierce/class-finder.
$ composer require haydenpierce/class-finder
See more info in the README file.
I wasn’t happy with any of the solutions here so I ended up building my class to handle this. This solution requires that you are:
- Using Composer
- Using PSR-4
In a nutshell, this class attempts to figure out where the classes actually live on your filesystem based on the namespaces you’ve defined in composer.json
. For instance, classes defined in the namespace Backup\Test
are found in /home/hpierce/BackupApplicationRoot/src/Test
. This can be trusted because mapping a directory structure to namespace is required by PSR-4:
The contiguous sub-namespace names after the “namespace prefix”
correspond to a subdirectory within a “base directory”, in which the
namespace separators represent directory separators. The subdirectory
name MUST match the case of the sub-namespace names.
You may need to adjust appRoot
to point to the directory that contains composer.json
.
<?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("https://stackoverflow.com/", $undefinedNamespaceFragments));
}
array_unshift($undefinedNamespaceFragments, array_pop($namespaceFragments));
}
return false;
}
}