mirror of
https://github.com/danog/class-finder.git
synced 2024-11-26 20:14:59 +01:00
Merge branch 'recursive-mode' into 'master'
Recursive mode See merge request hpierce1102/ClassFinder!4
This commit is contained in:
commit
cdc8b1473b
51
README.md
51
README.md
@ -26,13 +26,23 @@ No other installation methods are currently supported.
|
||||
Supported Autoloading Methods
|
||||
--------------------------------
|
||||
|
||||
* PSR-4
|
||||
* Classmaps
|
||||
* Files (Experimental, must be explicitly enabled via `ClassFinder::enableExperimentalFilesSupport()`)
|
||||
| Method | Supported | with `ClassFinder::RECURSIVE_MODE` |
|
||||
| ---------- | --------- | ---------------------------------- |
|
||||
| PSR-4 | ✔️ | ✔️ |
|
||||
| PSR-0 | ❌️* | ❌️* |
|
||||
| Classmap | ✔️ | ✔️ |
|
||||
| Files | ✔️^ | ❌️** |
|
||||
|
||||
\^ Experimental.
|
||||
|
||||
Example
|
||||
-------
|
||||
\* Planned.
|
||||
|
||||
\** Not planned. Open an issue if you need this feature.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
**Standard Mode**
|
||||
|
||||
```
|
||||
<?php
|
||||
@ -50,6 +60,31 @@ $classes = ClassFinder::getClassesInNamespace('TestApp1\Foo');
|
||||
*/
|
||||
var_dump($classes);
|
||||
```
|
||||
|
||||
**Recursive Mode** *(in v0.3-beta)*
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$classes = ClassFinder::getClassesInNamespace('TestApp1\Foo', ClassFinder::RECURSIVE_MODE);
|
||||
|
||||
/**
|
||||
* array(
|
||||
* 'TestApp1\Foo\Bar',
|
||||
* 'TestApp1\Foo\Baz',
|
||||
* 'TestApp1\Foo\Foo',
|
||||
* 'TestApp1\Foo\Box\Bar',
|
||||
* 'TestApp1\Foo\Box\Baz',
|
||||
* 'TestApp1\Foo\Box\Foo',
|
||||
* 'TestApp1\Foo\Box\Lon\Bar',
|
||||
* 'TestApp1\Foo\Box\Lon\Baz',
|
||||
* 'TestApp1\Foo\Box\Lon\Foo',
|
||||
* )
|
||||
*/
|
||||
var_dump($classes);
|
||||
```
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
@ -87,10 +122,10 @@ may be introduced in minor 0.X.Y versions, where X changes.
|
||||
|
||||
Various ideas:
|
||||
|
||||
* `ClassFinder::getClassesInNamespace('TestApp1\Foo', ClassFinder::RECURSIVE_MODE)`.
|
||||
Providing classes multiple namespaces deep.
|
||||
* ~~`ClassFinder::getClassesInNamespace('TestApp1\Foo', ClassFinder::RECURSIVE_MODE)`.
|
||||
Providing classes multiple namespaces deep.~~ (included v0.3-beta)
|
||||
|
||||
* `ClassFinder::getClassesImplementingInterface('TestApp1\Foo', 'TestApp1\FooInterface', ClassFinder::RECURSIVE_MODE)`.
|
||||
* `ClassFinder::getClassesImplementingInterface('TestApp1\Foo', 'TestApp1\FooInterface', ClassFinder::RECURSIVE_MODE)`.
|
||||
Filtering classes to only classes that implement a namespace.
|
||||
|
||||
* `ClassFinder::debugRenderReport('TestApp1\Foo\Baz')`
|
||||
|
@ -1,3 +1,58 @@
|
||||
Version 0.3.0 Beta
|
||||
------------------
|
||||
|
||||
* [#3](https://gitlab.com/hpierce1102/ClassFinder/issues/3) Added support for "recursive mode". Invoking `ClassFinder::getClassesInNamespace()`
|
||||
in this mode will result in classes in subnamespaces being turned up.
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$classes = ClassFinder::getClassesInNamespace('TestApp1\Foo', ClassFinder::RECURSIVE_MODE);
|
||||
|
||||
/**
|
||||
* array(
|
||||
* 'TestApp1\Foo\Bar',
|
||||
* 'TestApp1\Foo\Baz',
|
||||
* 'TestApp1\Foo\Foo',
|
||||
* 'TestApp1\Foo\Box\Bar',
|
||||
* 'TestApp1\Foo\Box\Baz',
|
||||
* 'TestApp1\Foo\Box\Foo',
|
||||
* 'TestApp1\Foo\Box\Lon\Bar',
|
||||
* 'TestApp1\Foo\Box\Lon\Baz',
|
||||
* 'TestApp1\Foo\Box\Lon\Foo',
|
||||
* )
|
||||
*/
|
||||
var_dump($classes);
|
||||
```
|
||||
|
||||
* Added **experimental** support for classes that have been included via `files` entries in `composer.json`. Including this feature
|
||||
is a significant drain on performance, so it must be explicitly enabled.
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
ClassFinder::enableExperimentalFilesSupport();
|
||||
|
||||
$classes = ClassFinder::getClassesInNamespace('TestApp1\Foo');
|
||||
```
|
||||
|
||||
* PSR4 and Classmap features can now be disabled. Disabling autoloading features that you don't need will probably improve performance.
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
ClassFinder::disablePSR4Support();
|
||||
ClassFinder::disableClassmapSupport();
|
||||
|
||||
$classes = ClassFinder::getClassesInNamespace('TestApp1\Foo');
|
||||
```
|
||||
|
||||
Version 0.2.0
|
||||
-------------
|
||||
|
||||
|
@ -11,6 +11,9 @@ use HaydenPierce\ClassFinder\PSR4\PSR4NamespaceFactory;
|
||||
|
||||
class ClassFinder
|
||||
{
|
||||
const STANDARD_MODE = 1;
|
||||
const RECURSIVE_MODE = 2;
|
||||
|
||||
/** @var AppConfig */
|
||||
private static $config;
|
||||
|
||||
@ -24,7 +27,13 @@ class ClassFinder
|
||||
private static $files;
|
||||
|
||||
/** @var boolean */
|
||||
private static $useFilesSupport;
|
||||
private static $useFilesSupport = false;
|
||||
|
||||
/** @var boolean */
|
||||
private static $usePSR4Support = true;
|
||||
|
||||
/** @var boolean */
|
||||
private static $useClassmapSupport = true;
|
||||
|
||||
private static function initialize()
|
||||
{
|
||||
@ -50,10 +59,11 @@ class ClassFinder
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @param $options
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getClassesInNamespace($namespace)
|
||||
public static function getClassesInNamespace($namespace, $options = self::STANDARD_MODE)
|
||||
{
|
||||
self::initialize();
|
||||
|
||||
@ -68,8 +78,8 @@ class ClassFinder
|
||||
));
|
||||
}
|
||||
|
||||
$classes = array_reduce($findersWithNamespace, function($carry, FinderInterface $finder) use ($namespace){
|
||||
return array_merge($carry, $finder->findClasses($namespace));
|
||||
$classes = array_reduce($findersWithNamespace, function($carry, FinderInterface $finder) use ($namespace, $options){
|
||||
return array_merge($carry, $finder->findClasses($namespace, $options));
|
||||
}, array());
|
||||
|
||||
return array_unique($classes);
|
||||
@ -91,15 +101,48 @@ class ClassFinder
|
||||
self::$useFilesSupport = false;
|
||||
}
|
||||
|
||||
public static function enablePSR4Support()
|
||||
{
|
||||
self::$usePSR4Support = true;
|
||||
}
|
||||
|
||||
public static function disablePSR4Support()
|
||||
{
|
||||
self::$usePSR4Support = false;
|
||||
}
|
||||
|
||||
public static function enableClassmapSupport()
|
||||
{
|
||||
self::$useClassmapSupport = true;
|
||||
}
|
||||
|
||||
public static function disableClassmapSupport()
|
||||
{
|
||||
self::$useClassmapSupport = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private static function getSupportedFinders()
|
||||
{
|
||||
$supportedFinders = array(
|
||||
self::$psr4,
|
||||
self::$classmap
|
||||
);
|
||||
$supportedFinders = array();
|
||||
|
||||
/*
|
||||
* This is done for testing. For some tests, allowing PSR4 classes contaminates the test results. This could also be
|
||||
* disabled for performance reasons (less finders in use means less work), but most people probably won't do that.
|
||||
*/
|
||||
if (self::$usePSR4Support) {
|
||||
$supportedFinders[] = self::$psr4;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is done for testing. For some tests, allowing classmap classes contaminates the test results. This could also be
|
||||
* disabled for performance reasons (less finders in use means less work), but most people probably won't do that.
|
||||
*/
|
||||
if (self::$useClassmapSupport) {
|
||||
$supportedFinders[] = self::$classmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Files support is tucked away behind a flag because it will need to use some kind of shell access via exec, or
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace HaydenPierce\ClassFinder\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
|
||||
class ClassmapEntry
|
||||
{
|
||||
private $className;
|
||||
@ -16,13 +18,39 @@ class ClassmapEntry
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the class is a DIRECT child of the given namespace. Currently, no other finders support "recursively"
|
||||
* discovering classes, so the Classmap module will not be the exception to that rule.
|
||||
*
|
||||
* @param $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function matches($namespace)
|
||||
public function matches($namespace, $options)
|
||||
{
|
||||
if ($options === ClassFinder::RECURSIVE_MODE) {
|
||||
return $this->doesMatchAnyNamespace($namespace);
|
||||
} else {
|
||||
return $this->doesMatchDirectNamespace($namespace);
|
||||
}
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the class is a child or subchild of the given namespace.
|
||||
* @param $namespace
|
||||
* @return bool
|
||||
*/
|
||||
private function doesMatchAnyNamespace($namespace)
|
||||
{
|
||||
return strpos($this->getClassName(),$namespace) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the class is a DIRECT child of the given namespace.
|
||||
* @param $namespace
|
||||
* @return bool
|
||||
*/
|
||||
private function doesMatchDirectNamespace($namespace)
|
||||
{
|
||||
$classNameFragments = explode('\\', $this->getClassName());
|
||||
array_pop($classNameFragments);
|
||||
@ -33,9 +61,4 @@ class ClassmapEntry
|
||||
return $namespace === $classNamespace;
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace HaydenPierce\ClassFinder\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
use HaydenPierce\ClassFinder\FinderInterface;
|
||||
|
||||
class ClassmapFinder implements FinderInterface
|
||||
@ -27,15 +28,16 @@ class ClassmapFinder implements FinderInterface
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @param $options
|
||||
* @return bool|string
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function findClasses($namespace)
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
$classmapEntries = $this->factory->getClassmapEntries();
|
||||
|
||||
$matchingEntries = array_filter($classmapEntries, function(ClassmapEntry $entry) use ($namespace) {
|
||||
return $entry->matches($namespace);
|
||||
$matchingEntries = array_filter($classmapEntries, function(ClassmapEntry $entry) use ($namespace, $options) {
|
||||
return $entry->matches($namespace, $options);
|
||||
});
|
||||
|
||||
return array_map(function(ClassmapEntry $entry) {
|
||||
|
@ -40,9 +40,11 @@ class FilesFinder implements FinderInterface
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @param $options
|
||||
* @return bool|string
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function findClasses($namespace)
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
$filesEntries = $this->factory->getFilesEntries();
|
||||
|
||||
|
@ -3,7 +3,7 @@ namespace HaydenPierce\ClassFinder;
|
||||
|
||||
interface FinderInterface
|
||||
{
|
||||
public function findClasses($namespace);
|
||||
public function findClasses($namespace, $options);
|
||||
|
||||
/**
|
||||
* A namespace is "known" if a Finder can determine that the autoloader can create classes from that namespace.
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace HaydenPierce\ClassFinder\PSR4;
|
||||
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use HaydenPierce\ClassFinder\FinderInterface;
|
||||
|
||||
class PSR4Finder implements FinderInterface
|
||||
@ -15,18 +15,30 @@ class PSR4Finder implements FinderInterface
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @param $options
|
||||
* @return array
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function findClasses($namespace)
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
$bestNamespace = $this->findBestPSR4Namespace($namespace);
|
||||
|
||||
if ($bestNamespace instanceof PSR4Namespace) {
|
||||
return $bestNamespace->findClasses($namespace);
|
||||
} else {
|
||||
return array();
|
||||
if ($options === ClassFinder::RECURSIVE_MODE) {
|
||||
$applicableNamespaces = $this->findAllApplicableNamespaces($namespace);
|
||||
}
|
||||
|
||||
if (empty($applicableNamespaces)) {
|
||||
$bestNamespace = $this->findBestPSR4Namespace($namespace);
|
||||
$applicableNamespaces = array($bestNamespace);
|
||||
}
|
||||
|
||||
return array_reduce($applicableNamespaces, function($carry, $psr4NamespaceOrNull) use ($namespace, $options) {
|
||||
if ($psr4NamespaceOrNull instanceof PSR4Namespace) {
|
||||
$classes = $psr4NamespaceOrNull->findClasses($namespace, $options);
|
||||
} else {
|
||||
$classes = array();
|
||||
}
|
||||
|
||||
return array_merge($carry, $classes);
|
||||
}, array());
|
||||
|
||||
}
|
||||
|
||||
public function isNamespaceKnown($namespace)
|
||||
@ -42,6 +54,21 @@ class PSR4Finder implements FinderInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @return PSR4Namespace[]
|
||||
*/
|
||||
private function findAllApplicableNamespaces($namespace)
|
||||
{
|
||||
$composerNamespaces = $this->factory->getPSR4Namespaces();
|
||||
|
||||
$acceptableNamespaces = array_filter($composerNamespaces, function(PSR4Namespace $potentialNamespace) use ($namespace){
|
||||
return $potentialNamespace->isAcceptableNamespaceRecursiveMode($namespace);
|
||||
});
|
||||
|
||||
return $acceptableNamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @return PSR4Namespace
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace HaydenPierce\ClassFinder\PSR4;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class PSR4Namespace
|
||||
@ -8,6 +9,9 @@ class PSR4Namespace
|
||||
private $namespace;
|
||||
private $directories;
|
||||
|
||||
/** @var PSR4Namespace[] */
|
||||
private $directSubnamespaces;
|
||||
|
||||
public function __construct($namespace, $directories)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
@ -85,7 +89,57 @@ class PSR4Namespace
|
||||
return $namespaceSegments === $matchingSegments;
|
||||
}
|
||||
|
||||
public function findClasses($namespace)
|
||||
public function isAcceptableNamespaceRecursiveMode($namespace)
|
||||
{
|
||||
// Remove prefix backslash (TODO: review if we do this eariler).
|
||||
$namespace = ltrim($namespace, '\\');
|
||||
|
||||
return strpos($this->namespace, $namespace) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to identify subnamespaces.
|
||||
*/
|
||||
public function findDirectories()
|
||||
{
|
||||
$self = $this;
|
||||
$directories = array_reduce($this->directories, function($carry, $directory) use ($self){
|
||||
$path = $self->normalizePath($directory, '');
|
||||
$realDirectory = realpath($path);
|
||||
if ($realDirectory !== false) {
|
||||
return array_merge($carry, array($realDirectory));
|
||||
} else {
|
||||
return $carry;
|
||||
}
|
||||
}, array());
|
||||
|
||||
$arraysOfClasses = array_map(function($directory) use ($self) {
|
||||
$files = scandir($directory);
|
||||
return array_map(function($file) use ($directory, $self) {
|
||||
return $self->normalizePath($directory, $file);
|
||||
}, $files);
|
||||
}, $directories);
|
||||
|
||||
$potentialDirectories = array_reduce($arraysOfClasses, function($carry, $arrayOfClasses) {
|
||||
return array_merge($carry, $arrayOfClasses);
|
||||
}, array());
|
||||
|
||||
// Remove '.' and '..' directories
|
||||
$potentialDirectories = array_filter($potentialDirectories, function($potentialDirectory) {
|
||||
$segments = explode('/', $potentialDirectory);
|
||||
$lastSegment = array_pop($segments);
|
||||
|
||||
return $lastSegment !== '.' && $lastSegment !== '..';
|
||||
});
|
||||
|
||||
$confirmedDirectories = array_filter($potentialDirectories, function($potentialDirectory) {
|
||||
return is_dir($potentialDirectory);
|
||||
});
|
||||
|
||||
return $confirmedDirectories;
|
||||
}
|
||||
|
||||
public function findClasses($namespace, $options = ClassFinder::STANDARD_MODE)
|
||||
{
|
||||
$relativePath = substr($namespace, strlen($this->namespace));
|
||||
|
||||
@ -112,6 +166,46 @@ class PSR4Namespace
|
||||
return $namespace . '\\' . str_replace('.php', '', $file);
|
||||
}, $potentialClassFiles);
|
||||
|
||||
if ($options == ClassFinder::RECURSIVE_MODE) {
|
||||
return $this->getClassesFromListRecursively($namespace);
|
||||
} else {
|
||||
return array_filter($potentialClasses, function($potentialClass) {
|
||||
if (function_exists($potentialClass)) {
|
||||
// For some reason calling class_exists() on a namespace'd function raises a Fatal Error (tested PHP 7.0.8)
|
||||
// Example: DeepCopy\deep_copy
|
||||
return false;
|
||||
} else {
|
||||
return class_exists($potentialClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private function getDirectClassesOnly()
|
||||
{
|
||||
$self = $this;
|
||||
$directories = array_reduce($this->directories, function($carry, $directory) use ($self){
|
||||
$path = $self->normalizePath($directory, '');
|
||||
$realDirectory = realpath($path);
|
||||
if ($realDirectory !== false) {
|
||||
return array_merge($carry, array($realDirectory));
|
||||
} else {
|
||||
return $carry;
|
||||
}
|
||||
}, 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 ($self) {
|
||||
return $self->namespace . str_replace('.php', '', $file);
|
||||
}, $potentialClassFiles);
|
||||
|
||||
return array_filter($potentialClasses, function($potentialClass) {
|
||||
if (function_exists($potentialClass)) {
|
||||
// For some reason calling class_exists() on a namespace'd function raises a Fatal Error (tested PHP 7.0.8)
|
||||
@ -123,6 +217,19 @@ class PSR4Namespace
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @return string[]
|
||||
*/
|
||||
public function getClassesFromListRecursively($namespace)
|
||||
{
|
||||
$initialClasses = strpos( $this->namespace, $namespace) !== false ? $this->getDirectClassesOnly() : array();
|
||||
|
||||
return array_reduce($this->getDirectSubnamespaces(), function($carry, PSR4Namespace $subNamespace) use ($namespace) {
|
||||
return array_merge($carry, $subNamespace->getClassesFromListRecursively($namespace));
|
||||
}, $initialClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file path based on an absolute path to a directory and a relative path in a way
|
||||
* that will be compatible with both Linux and Windows. This method is also extracted so that
|
||||
@ -136,4 +243,28 @@ class PSR4Namespace
|
||||
$path = str_replace('\\', '/', $directory . '/' . $relativePath);
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PSR4Namespace[]
|
||||
*/
|
||||
public function getDirectSubnamespaces()
|
||||
{
|
||||
return $this->directSubnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PSR4Namespace[] $directSubnamespaces
|
||||
*/
|
||||
public function setDirectSubnamespaces($directSubnamespaces)
|
||||
{
|
||||
$this->directSubnamespaces = $directSubnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return trim($this->namespace, '\\');
|
||||
}
|
||||
}
|
@ -82,7 +82,29 @@ class PSR4NamespaceFactory
|
||||
return realpath($directory);
|
||||
}, $directories);
|
||||
|
||||
return new PSR4Namespace($namespace, $directories);
|
||||
$psr4Namespace = new PSR4Namespace($namespace, $directories);
|
||||
|
||||
$subNamespaces = $this->getSubnamespaces($psr4Namespace);
|
||||
$psr4Namespace->setDirectSubnamespaces($subNamespaces);
|
||||
|
||||
return $psr4Namespace;
|
||||
}
|
||||
|
||||
private function getSubnamespaces(PSR4Namespace $psr4Namespace)
|
||||
{
|
||||
// Scan it's own directories.
|
||||
$directoreies = $psr4Namespace->findDirectories();
|
||||
|
||||
$self = $this;
|
||||
$subnamespaces = array_map(function($directory) use ($self, $psr4Namespace){
|
||||
$segments = explode('/', $directory);
|
||||
$subnamespaceSegment = array_pop($segments);
|
||||
|
||||
$namespace = $psr4Namespace->getNamespace() . "\\" . $subnamespaceSegment . "\\";
|
||||
return $self->createNamespace($namespace, $directory);
|
||||
}, $directoreies);
|
||||
|
||||
return $subnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
|
7
test/app1/classmap/woc/wox.php
Normal file
7
test/app1/classmap/woc/wox.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace TestApp1\ClassmapClasses\NestedClasses;
|
||||
|
||||
class NestedClass1 {};
|
||||
class NestedClass2 {};
|
||||
class NestedClass3 {};
|
@ -68,4 +68,61 @@ class ClassmapTest extends \PHPUnit_Framework_TestCase
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider classesInNamespaceRecursivelyDataProvider
|
||||
*/
|
||||
public function testClassesInNamespaceRecursively($namespace, $expected, $message)
|
||||
{
|
||||
ClassFinder::disablePSR4Support();
|
||||
|
||||
try {
|
||||
$classes = ClassFinder::getClassesInNamespace($namespace, ClassFinder::RECURSIVE_MODE);
|
||||
} catch (Exception $e) {
|
||||
$this->assertFalse(true, 'An exception occurred: ' . $e->getMessage());
|
||||
$classes = array();
|
||||
}
|
||||
|
||||
ClassFinder::enablePSR4Support();
|
||||
|
||||
$this->assertEquals($expected, $classes, $message);
|
||||
}
|
||||
|
||||
public function classesInNamespaceRecursivelyDataProvider()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'TestApp1\ClassmapClasses',
|
||||
array(
|
||||
'TestApp1\ClassmapClasses\Bik',
|
||||
'TestApp1\ClassmapClasses\Bil',
|
||||
'TestApp1\ClassmapClasses\Bir',
|
||||
'TestApp1\ClassmapClasses\Mik',
|
||||
'TestApp1\ClassmapClasses\Mil',
|
||||
'TestApp1\ClassmapClasses\Mir',
|
||||
'TestApp1\ClassmapClasses\NestedClasses\NestedClass1',
|
||||
'TestApp1\ClassmapClasses\NestedClasses\NestedClass2',
|
||||
'TestApp1\ClassmapClasses\NestedClasses\NestedClass3',
|
||||
'TestApp1\ClassmapClasses\Tik',
|
||||
'TestApp1\ClassmapClasses\Til',
|
||||
'TestApp1\ClassmapClasses\Tir'
|
||||
),
|
||||
'Classfinder should be able to load classes recursively based on a classmap.'
|
||||
),
|
||||
array(
|
||||
'HaydenPierce',
|
||||
array(
|
||||
'HaydenPierce\Classmap2\Classmap2ClassmapINC',
|
||||
'HaydenPierce\Classmap2\Classmap2ClassmapPHP',
|
||||
'HaydenPierce\Classmap2\Classmap3ClassesPHP',
|
||||
'HaydenPierce\Classmap2\ClassmapClassmap2PHP',
|
||||
'HaydenPierce\Classmap\Classmap2ClassmapINC',
|
||||
'HaydenPierce\Classmap\Classmap2ClassmapPHP',
|
||||
'HaydenPierce\Classmap\Classmap3ClassesPHP',
|
||||
'HaydenPierce\Classmap\ClassmapClassmap2PHP',
|
||||
),
|
||||
'Classfinder should be able to load third party classes recursively based on a classmap.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -13,14 +13,15 @@ class PSR4Test extends \PHPUnit_Framework_TestCase
|
||||
// Reset ClassFinder back to normal.
|
||||
ClassFinder::setAppRoot(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider classFinderDataProvider
|
||||
* @dataProvider getClassesInNamespaceDataProvider
|
||||
*/
|
||||
public function testClassFinder($namespace, $expected, $message)
|
||||
public function testGetClassesInNamespace($namespace, $expected, $message)
|
||||
{
|
||||
try {
|
||||
$classes = ClassFinder::getClassesInNamespace($namespace);
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
$this->assertFalse(true, 'An exception occurred: ' . $e->getMessage());
|
||||
$classes = array();
|
||||
}
|
||||
@ -28,7 +29,7 @@ class PSR4Test extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($expected, $classes, $message);
|
||||
}
|
||||
|
||||
public function classFinderDataProvider()
|
||||
public function getClassesInNamespaceDataProvider()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
@ -97,7 +98,89 @@ class PSR4Test extends \PHPUnit_Framework_TestCase
|
||||
array(
|
||||
'TestApp1\Foo\Empty',
|
||||
array(),
|
||||
'ClassFinder should return an empty array if the namesapce is known, but contains no classes.'
|
||||
'ClassFinder should return an empty array if the namespace is known, but contains no classes.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getClassesInNamespaceRecursivelyDataProvider
|
||||
*/
|
||||
public function testGetClassesInNamespaceRecursively($namespace, $expected, $message)
|
||||
{
|
||||
ClassFinder::disableClassmapSupport();
|
||||
|
||||
try {
|
||||
$classes = ClassFinder::getClassesInNamespace($namespace, ClassFinder::RECURSIVE_MODE);
|
||||
} catch (\Exception $e) {
|
||||
$this->assertFalse(true, 'An exception occurred: ' . $e->getMessage());
|
||||
$classes = array();
|
||||
}
|
||||
|
||||
// ClassFinder has the ability to find itself. This ability, while intended, is incontinent for tests
|
||||
// because of the 'HaydenPierce' test case. Whenever ClassFinder would be updated, we would need to update the
|
||||
// test. To prevent the flakiness, we just remove ClassFinder's classes.
|
||||
$classes = array_filter($classes, function($class) {
|
||||
return strpos($class, 'HaydenPierce\ClassFinder') !== 0;
|
||||
});
|
||||
|
||||
ClassFinder::enableClassmapSupport();
|
||||
|
||||
$this->assertEquals($expected, $classes, $message);
|
||||
}
|
||||
|
||||
public function getClassesInNamespaceRecursivelyDataProvider()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'TestApp1\Foo',
|
||||
array(
|
||||
'TestApp1\Foo\Bar',
|
||||
'TestApp1\Foo\Baz',
|
||||
'TestApp1\Foo\Foo',
|
||||
'TestApp1\Foo\Loo\Lar',
|
||||
'TestApp1\Foo\Loo\Laz',
|
||||
'TestApp1\Foo\Loo\Loo'
|
||||
),
|
||||
'ClassFinder should be able to find 1st party classes recursively, multiple namespaces deep.'
|
||||
),
|
||||
array(
|
||||
'TestApp1\Foo\Loo',
|
||||
array(
|
||||
'TestApp1\Foo\Loo\Lar',
|
||||
'TestApp1\Foo\Loo\Laz',
|
||||
'TestApp1\Foo\Loo\Loo'
|
||||
),
|
||||
'ClassFinder should not turn up other classes when running in recursive mode.'
|
||||
),
|
||||
array(
|
||||
'TestApp1\Multi',
|
||||
array(
|
||||
'TestApp1\Multi\Uij',
|
||||
'TestApp1\Multi\Yij',
|
||||
'TestApp1\Multi\Uik',
|
||||
'TestApp1\Multi\Yik',
|
||||
'TestApp1\Multi\Yop\Rik',
|
||||
'TestApp1\Multi\Yop\Tik',
|
||||
'TestApp1\Multi\Yop\Eij',
|
||||
'TestApp1\Multi\Yop\Rij'
|
||||
),
|
||||
'ClassFinder should be able to find 1st party classes recursively when a provided namespace root maps to multiple directories (Example: "HaydenPierce\\SandboxAppMulti\\": ["multi/Bop", "multi/Bot"] )'
|
||||
),
|
||||
array(
|
||||
'HaydenPierce',
|
||||
array(
|
||||
'HaydenPierce\SandboxApp\Foy',
|
||||
'HaydenPierce\SandboxApp\Fob\Soz',
|
||||
'HaydenPierce\SandboxApp\Foo\Larc',
|
||||
'HaydenPierce\SandboxApp\Foo\Bar\Barc',
|
||||
'HaydenPierce\SandboxApp\Foo\Bar\Barp',
|
||||
'HaydenPierce\SandboxAppMulti\Zip',
|
||||
'HaydenPierce\SandboxAppMulti\Zop',
|
||||
'HaydenPierce\SandboxAppMulti\Zap',
|
||||
'HaydenPierce\SandboxAppMulti\Zit'
|
||||
),
|
||||
'ClassFinder should be able to find 3rd party classes'
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -110,4 +193,16 @@ class PSR4Test extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
ClassFinder::getClassesInNamespace('TestApp1\DoesNotExist');
|
||||
}
|
||||
|
||||
public function testCanFindSelf()
|
||||
{
|
||||
try {
|
||||
$classes = ClassFinder::getClassesInNamespace('HaydenPierce\ClassFinder', ClassFinder::RECURSIVE_MODE);
|
||||
} catch (\Exception $e) {
|
||||
$this->assertFalse(true, 'An exception occurred: ' . $e->getMessage());
|
||||
$classes = array();
|
||||
}
|
||||
|
||||
$this->assertGreaterThan(0, count($classes), 'ClassFinder should be able to find its own internal classes');
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace HaydenPierce\ClassFinder\UnitTest\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use HaydenPierce\ClassFinder\Classmap\ClassmapEntry;
|
||||
|
||||
class ClassmapEntryTest extends \PHPUnit_Framework_TestCase
|
||||
@ -22,11 +23,11 @@ class ClassmapEntryTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$entry = new ClassmapEntry("MyClassmap\Foo\Bar");
|
||||
|
||||
$this->assertTrue($entry->matches("MyClassmap\Foo"));
|
||||
$this->assertTrue($entry->matches("MyClassmap\Foo", ClassFinder::STANDARD_MODE));
|
||||
|
||||
$this->assertFalse($entry->matches("MyClassmap"), "Providing only a single segment of a namespace should not be a match.");
|
||||
$this->assertFalse($entry->matches("MyClassmap\Foo\Bar"), "Providing the fully qualified classname doesn't match because only the class's namespace should match.");
|
||||
$this->assertFalse($entry->matches("MyClassmap\Bar"));
|
||||
$this->assertFalse($entry->matches("MyClassmap\Foo\Bar\Baz"));
|
||||
$this->assertFalse($entry->matches("MyClassmap", ClassFinder::STANDARD_MODE), "Providing only a single segment of a namespace should not be a match.");
|
||||
$this->assertFalse($entry->matches("MyClassmap\Foo\Bar", ClassFinder::STANDARD_MODE), "Providing the fully qualified classname doesn't match because only the class's namespace should match.");
|
||||
$this->assertFalse($entry->matches("MyClassmap\Bar", ClassFinder::STANDARD_MODE));
|
||||
$this->assertFalse($entry->matches("MyClassmap\Foo\Bar\Baz", ClassFinder::STANDARD_MODE));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user