1
0
mirror of https://github.com/danog/class-finder.git synced 2024-11-30 04:29:03 +01:00

Refactor agressively to namespaces that map to multiple directories.

This commit is contained in:
Hayden Pierce 2018-08-27 20:15:12 -05:00
parent f0423a39d4
commit a1a1926351
5 changed files with 140 additions and 47 deletions

View File

@ -3,6 +3,7 @@
namespace HaydenPierce\ClassFinder;
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
use HaydenPierce\ClassFinder\Finder\PSR4NamespaceFactory;
/**
* Class AppConfig
@ -11,11 +12,16 @@ use HaydenPierce\ClassFinder\Exception\ClassFinderException;
*/
class AppConfig
{
/** @var PSR4NamespaceFactory */
private $psr4NamespaceFactory;
/** @var string */
private $appRoot;
public function __construct()
public function __construct(PSR4NamespaceFactory $PSR4NamespaceFactory)
{
$this->appRoot = $this->findAppRoot();
$this->psr4NamespaceFactory = $PSR4NamespaceFactory;
}
/**
@ -67,9 +73,17 @@ class AppConfig
public function getPSR4Namespaces()
{
$namespaces = $this->getUserDefinedPSR4Namespaces();
$vendorNamespaces = require($this->getAppRoot() . 'vendor/composer/autoload_psr4.php');
// This is broken.
// $vendorNamespaces = require($this->getAppRoot() . 'vendor/composer/autoload_psr4.php');
$namespaces = array_merge($vendorNamespaces, $namespaces);
// There's some wackiness going on here for PHP 5.3 compatibility.
$names = array_keys($namespaces);
$directories = array_values($namespaces);
$self = $this;
$namespaces = array_map(function($index) use ($self, $names, $directories) {
return $self->psr4NamespaceFactory->createNamespace($names[$index], $directories[$index], $this->appRoot);
},range(0, count($namespaces) - 1));
return $namespaces;
}

View File

@ -2,6 +2,7 @@
namespace HaydenPierce\ClassFinder;
use HaydenPierce\ClassFinder\Finder\PSR4Finder;
use HaydenPierce\ClassFinder\Finder\PSR4NamespaceFactory;
class ClassFinder
{
@ -14,7 +15,8 @@ class ClassFinder
private static function initialize()
{
if (!(self::$config instanceof AppConfig)) {
self::$config = new AppConfig();
$PSR4Factory = new PSR4NamespaceFactory();
self::$config = new AppConfig($PSR4Factory);
}
if (!(self::$psr4 instanceof PSR4Finder)) {

View File

@ -13,58 +13,31 @@ class PSR4Finder implements FinderInterface
$this->config = $config;
}
public function findClasses($namespace)
{
$files = scandir($this->getNamespaceDirectory($namespace));
$classes = array_map(function($file) use ($namespace){
return $namespace . '\\' . str_replace('.php', '', $file);
}, $files);
$classes = array_filter($classes, function($possibleClass){
return class_exists($possibleClass);
});
return $classes;
}
/**
* @param $namespace
* @return bool|string
* @throws \Exception
* @throws ClassFinderException
*/
private function getNamespaceDirectory($namespace)
public function findClasses($namespace)
{
$appRoot = $this->config->getAppRoot();
$composerNamespaces = $this->config->getPSR4Namespaces();
$namespaceFragments = explode('\\', $namespace);
$undefinedNamespaceFragments = [];
while($namespaceFragments) {
$possibleNamespace = implode('\\', $namespaceFragments) . '\\';
if(array_key_exists($possibleNamespace, $composerNamespaces)){
$resolvedDirectory = $appRoot . $composerNamespaces[$possibleNamespace] . implode('/', $undefinedNamespaceFragments);
$realDirectory = realpath($resolvedDirectory);
if ($realDirectory !== false) {
return $realDirectory;
} else {
throw new ClassFinderException(sprintf("Unknown namespace '%s'. Checked for files in %s, but that directory did not exist. See %s for details.",
$namespace,
$resolvedDirectory,
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/unknownSubNamespace.md'
));
}
/** @var PSR4Namespace $bestNamespace */
$bestNamespace = array_reduce($composerNamespaces, function($carry, PSR4Namespace $potentialNamespace) use ($namespace) {
if ($potentialNamespace->matches($namespace)) {
return $potentialNamespace;
} else {
return $carry;
}
}, null);
array_unshift($undefinedNamespaceFragments, array_pop($namespaceFragments));
if ($bestNamespace instanceof PSR4Namespace) {
return $bestNamespace->findClasses($namespace);
} else {
throw new ClassFinderException(sprintf("Unknown namespace '%s'. You should add the namespace prefix to composer.json. See '%s' for details.",
$namespace,
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/unregisteredRoot.md'
));
}
throw new ClassFinderException(sprintf("Unknown namespace '%s'. You should add the namespace prefix to composer.json. See '%s' for details.",
$namespace,
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/unregisteredRoot.md'
));
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace HaydenPierce\ClassFinder\Finder;
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
class PSR4Namespace
{
private $namespace;
private $directories;
public function __construct($namespace, $directories)
{
$this->namespace = $namespace;
$this->directories = $directories;
}
public function matches($namespace)
{
$namespaceFragments = explode('\\', $namespace);
$undefinedNamespaceFragments = [];
while($namespaceFragments) {
$possibleNamespace = implode('\\', $namespaceFragments) . '\\';
if($this->namespace === $possibleNamespace){
return true;
}
array_unshift($undefinedNamespaceFragments, array_pop($namespaceFragments));
}
return false;
}
public function findClasses($namespace)
{
// TODO: revisit this. It probably doesn't work.
$relativePath = substr($namespace, strlen($this->namespace));
$directories = array_reduce($this->directories, function($carry, $directory) use ($relativePath, $namespace){
$realDirectory = realpath($directory . '/' . $relativePath);
if ($realDirectory !== false) {
return array_merge($carry, array($realDirectory));
} else {
throw new ClassFinderException(sprintf("Unknown namespace '%s'. Checked for files in %s, but that directory did not exist. See %s for details.",
$namespace,
$realDirectory,
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/unknownSubNamespace.md'
));
}
}, array());
$arraysOfClasses = array_map(function($directory) {
return scandir($directory);
}, $directories);
$potentialClassFiles = array_reduce($arraysOfClasses, function($carry, $arrayOfClasses) {
return array_merge($carry, $arrayOfClasses);
}, array());
$potentialClasses = array_map(function($file) use ($namespace){
return $namespace . '\\' . str_replace('.php', '', $file);
}, $potentialClassFiles);
return array_filter($potentialClasses, 'class_exists');
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace HaydenPierce\ClassFinder\Finder;
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
class PSR4NamespaceFactory
{
public function __construct()
{
}
/**
* TODO: Figure out a way to stop passing in $appRoot. This will probably be a refactoring.
* Creates a namespace from composer_psr4.php and composer.json's autoload.psr4 items
* @param $namespace
* @param $directories
* @throws ClassFinderException
*/
public function createNamespace($namespace, $directories, $appRoot)
{
if (is_string($directories)) {
// This is an acceptable format according to composer.json
$directories = array($appRoot . $directories);
} elseif (is_array($directories)) {
// composer_psr4.php seems to put everything in this format
} else {
throw new ClassFinderException('Unknown PSR4 definition.');
}
$directories = array_map(function($directory) {
return realpath($directory);
}, $directories);
return new PSR4Namespace($namespace, $directories);
}
}