diff --git a/lib/Finder/ComposerFinder.php b/lib/Finder/ComposerFinder.php index cd69089..b8ad03d 100644 --- a/lib/Finder/ComposerFinder.php +++ b/lib/Finder/ComposerFinder.php @@ -8,6 +8,7 @@ use Iterator; use Kcs\ClassFinder\Iterator\ComposerIterator; use Kcs\ClassFinder\Iterator\FilteredComposerIterator; +use Kcs\ClassFinder\Reflection\ReflectorFactoryInterface; use Reflector; use RuntimeException; use Symfony\Component\Debug\DebugClassLoader; @@ -25,21 +26,29 @@ final class ComposerFinder implements FinderInterface use ReflectionFilterTrait; private ClassLoader $loader; + private ?ReflectorFactoryInterface $reflectorFactory = null; public function __construct(?ClassLoader $loader = null) { $this->loader = $loader ?? self::getValidLoader(); } + public function setReflectorFactory(?ReflectorFactoryInterface $reflectorFactory): self + { + $this->reflectorFactory = $reflectorFactory; + + return $this; + } + /** * @return Iterator */ public function getIterator(): Iterator { if ($this->namespaces || $this->dirs || $this->notNamespaces) { - $iterator = new FilteredComposerIterator($this->loader, $this->namespaces, $this->notNamespaces, $this->dirs); + $iterator = new FilteredComposerIterator($this->loader, $this->reflectorFactory, $this->namespaces, $this->notNamespaces, $this->dirs); } else { - $iterator = new ComposerIterator($this->loader); + $iterator = new ComposerIterator($this->loader, $this->reflectorFactory); } return $this->applyFilters($iterator); diff --git a/lib/Finder/Psr0Finder.php b/lib/Finder/Psr0Finder.php index 0890d94..8e2a063 100644 --- a/lib/Finder/Psr0Finder.php +++ b/lib/Finder/Psr0Finder.php @@ -7,6 +7,7 @@ use Iterator; use Kcs\ClassFinder\Iterator\Psr0Iterator; use Kcs\ClassFinder\PathNormalizer; +use Kcs\ClassFinder\Reflection\ReflectorFactoryInterface; use Reflector; use function Safe\substr; @@ -22,6 +23,7 @@ final class Psr0Finder implements FinderInterface private string $namespace; private string $path; + private ?ReflectorFactoryInterface $reflectorFactory = null; public function __construct(string $namespace, string $path) { @@ -38,11 +40,18 @@ public function __construct(string $namespace, string $path) $this->path = PathNormalizer::resolvePath($path); } + public function setReflectorFactory(?ReflectorFactoryInterface $reflectorFactory): self + { + $this->reflectorFactory = $reflectorFactory; + + return $this; + } + /** * @return Iterator */ public function getIterator(): Iterator { - return $this->applyFilters(new Psr0Iterator($this->namespace, $this->path)); + return $this->applyFilters(new Psr0Iterator($this->namespace, $this->path, $this->reflectorFactory)); } } diff --git a/lib/Finder/Psr4Finder.php b/lib/Finder/Psr4Finder.php index 1f3be20..40206e8 100644 --- a/lib/Finder/Psr4Finder.php +++ b/lib/Finder/Psr4Finder.php @@ -7,6 +7,7 @@ use Iterator; use Kcs\ClassFinder\Iterator\Psr4Iterator; use Kcs\ClassFinder\PathNormalizer; +use Kcs\ClassFinder\Reflection\ReflectorFactoryInterface; use Reflector; use function Safe\substr; @@ -22,6 +23,7 @@ final class Psr4Finder implements FinderInterface private string $namespace; private string $path; + private ?ReflectorFactoryInterface $reflectorFactory = null; public function __construct(string $namespace, string $path) { @@ -38,11 +40,18 @@ public function __construct(string $namespace, string $path) $this->path = $path; } + public function setReflectorFactory(?ReflectorFactoryInterface $reflectorFactory): self + { + $this->reflectorFactory = $reflectorFactory; + + return $this; + } + /** * @return Iterator */ public function getIterator(): Iterator { - return $this->applyFilters(new Psr4Iterator($this->namespace, $this->path)); + return $this->applyFilters(new Psr4Iterator($this->namespace, $this->path, $this->reflectorFactory)); } } diff --git a/lib/Iterator/ComposerIterator.php b/lib/Iterator/ComposerIterator.php index 7953546..de16831 100644 --- a/lib/Iterator/ComposerIterator.php +++ b/lib/Iterator/ComposerIterator.php @@ -6,8 +6,9 @@ use Composer\Autoload\ClassLoader; use Generator; +use Kcs\ClassFinder\Reflection\NativeReflectorFactory; +use Kcs\ClassFinder\Reflection\ReflectorFactoryInterface; use Kcs\ClassFinder\Util\ErrorHandler; -use ReflectionClass; use Throwable; /** @@ -16,10 +17,12 @@ final class ComposerIterator extends ClassIterator { private ClassLoader $classLoader; + private ReflectorFactoryInterface $reflectorFactory; - public function __construct(ClassLoader $classLoader, int $flags = 0) + public function __construct(ClassLoader $classLoader, ?ReflectorFactoryInterface $reflectorFactory = null, int $flags = 0) { $this->classLoader = $classLoader; + $this->reflectorFactory = $reflectorFactory ?? new NativeReflectorFactory(); parent::__construct($flags); } @@ -38,7 +41,7 @@ private function searchInClassMap(): Generator foreach ($this->classLoader->getClassMap() as $class => $file) { ErrorHandler::register(); try { - $reflectionClass = new ReflectionClass($class); + $reflectionClass = $this->reflectorFactory->reflect($class); } catch (Throwable $e) { /** @phpstan-ignore-line */ continue; } finally { @@ -64,13 +67,13 @@ private function searchInPsrMap(): Generator foreach ($this->classLoader->getPrefixesPsr4() as $ns => $dirs) { foreach ($dirs as $dir) { - yield from new Psr4Iterator($ns, $dir, 0, $this->classLoader->getClassMap()); + yield from new Psr4Iterator($ns, $dir, $this->reflectorFactory, 0, $this->classLoader->getClassMap()); } } foreach ($this->classLoader->getPrefixes() as $ns => $dirs) { foreach ($dirs as $dir) { - yield from new Psr0Iterator($ns, $dir, 0, $this->classLoader->getClassMap()); + yield from new Psr0Iterator($ns, $dir, $this->reflectorFactory, 0, $this->classLoader->getClassMap()); } } } diff --git a/lib/Iterator/FilteredComposerIterator.php b/lib/Iterator/FilteredComposerIterator.php index 1837dab..2a8324f 100644 --- a/lib/Iterator/FilteredComposerIterator.php +++ b/lib/Iterator/FilteredComposerIterator.php @@ -7,8 +7,9 @@ use Composer\Autoload\ClassLoader; use Generator; use Kcs\ClassFinder\PathNormalizer; +use Kcs\ClassFinder\Reflection\NativeReflectorFactory; +use Kcs\ClassFinder\Reflection\ReflectorFactoryInterface; use Kcs\ClassFinder\Util\ErrorHandler; -use ReflectionClass; use Throwable; use function array_map; @@ -29,6 +30,7 @@ final class FilteredComposerIterator extends ClassIterator { private ClassLoader $classLoader; + private ReflectorFactoryInterface $reflectorFactory; /** @var string[]|null */ private ?array $namespaces = null; @@ -37,16 +39,17 @@ final class FilteredComposerIterator extends ClassIterator private ?array $notNamespaces = null; /** @var string[]|null */ - private ?array $dirs = null; + private ?array $dirs; /** * @param string[]|null $namespaces * @param string[]|null $notNamespaces * @param string[]|null $dirs */ - public function __construct(ClassLoader $classLoader, ?array $namespaces, ?array $notNamespaces, ?array $dirs, int $flags = 0) + public function __construct(ClassLoader $classLoader, ?ReflectorFactoryInterface $reflectorFactory = null, ?array $namespaces, ?array $notNamespaces, ?array $dirs, int $flags = 0) { $this->classLoader = $classLoader; + $this->reflectorFactory = $reflectorFactory ?? new NativeReflectorFactory(); $this->dirs = $dirs !== null ? array_map(PathNormalizer::class . '::resolvePath', $dirs) : $dirs; if ($namespaces !== null) { @@ -86,7 +89,7 @@ private function searchInClassMap(): Generator ErrorHandler::register(); try { - $reflectionClass = new ReflectionClass($class); + $reflectionClass = $this->reflectorFactory->reflect($class); } catch (Throwable $e) { /** @phpstan-ignore-line */ continue; } finally { @@ -111,11 +114,11 @@ private function searchInPsrMap(): Generator } foreach ($this->traversePrefixes($this->classLoader->getPrefixesPsr4()) as $ns => $dir) { - yield from new Psr4Iterator($ns, $dir, 0, $this->classLoader->getClassMap(), $this->notNamespaces); + yield from new Psr4Iterator($ns, $dir, $this->reflectorFactory, 0, $this->classLoader->getClassMap(), $this->notNamespaces); } foreach ($this->traversePrefixes($this->classLoader->getPrefixes()) as $ns => $dir) { - yield from new Psr0Iterator($ns, $dir, 0, $this->classLoader->getClassMap(), $this->notNamespaces); + yield from new Psr0Iterator($ns, $dir, $this->reflectorFactory, 0, $this->classLoader->getClassMap(), $this->notNamespaces); } } diff --git a/lib/Iterator/Psr0Iterator.php b/lib/Iterator/Psr0Iterator.php index 53569a4..e0ccbe9 100644 --- a/lib/Iterator/Psr0Iterator.php +++ b/lib/Iterator/Psr0Iterator.php @@ -7,8 +7,9 @@ use Closure; use Generator; use Kcs\ClassFinder\PathNormalizer; +use Kcs\ClassFinder\Reflection\NativeReflectorFactory; +use Kcs\ClassFinder\Reflection\ReflectorFactoryInterface; use Kcs\ClassFinder\Util\ErrorHandler; -use ReflectionClass; use Throwable; use function array_map; @@ -27,6 +28,7 @@ final class Psr0Iterator extends ClassIterator private string $namespace; private int $pathLen; + private ReflectorFactoryInterface $reflectorFactory; /** @var string[] */ private array $classMap; @@ -38,10 +40,17 @@ final class Psr0Iterator extends ClassIterator * @param array $classMap * @param string[] $excludeNamespaces */ - public function __construct(string $namespace, string $path, int $flags = 0, array $classMap = [], ?array $excludeNamespaces = null) - { + public function __construct( + string $namespace, + string $path, + ?ReflectorFactoryInterface $reflectorFactory = null, + int $flags = 0, + array $classMap = [], + ?array $excludeNamespaces = null + ) { $this->namespace = $namespace; $this->path = PathNormalizer::resolvePath($path); + $this->reflectorFactory = $reflectorFactory ?? new NativeReflectorFactory(); $this->pathLen = strlen($this->path); $this->classMap = array_map(PathNormalizer::class . '::resolvePath', $classMap); $this->excludeNamespaces = $excludeNamespaces; @@ -100,7 +109,7 @@ protected function getGenerator(): Generator continue; } - yield $class => new ReflectionClass($class); + yield $class => $this->reflectorFactory->reflect($class); } } } diff --git a/lib/Iterator/Psr4Iterator.php b/lib/Iterator/Psr4Iterator.php index f76c818..c3bc468 100644 --- a/lib/Iterator/Psr4Iterator.php +++ b/lib/Iterator/Psr4Iterator.php @@ -7,8 +7,9 @@ use Closure; use Generator; use Kcs\ClassFinder\PathNormalizer; +use Kcs\ClassFinder\Reflection\NativeReflectorFactory; +use Kcs\ClassFinder\Reflection\ReflectorFactoryInterface; use Kcs\ClassFinder\Util\ErrorHandler; -use ReflectionClass; use Throwable; use function array_map; @@ -27,6 +28,7 @@ final class Psr4Iterator extends ClassIterator private string $namespace; private int $prefixLen; + private ReflectorFactoryInterface $reflectorFactory; /** @var array */ private array $classMap; @@ -38,10 +40,17 @@ final class Psr4Iterator extends ClassIterator * @param array $classMap * @param string[] $excludeNamespaces */ - public function __construct(string $namespace, string $path, int $flags = 0, array $classMap = [], ?array $excludeNamespaces = null) - { + public function __construct( + string $namespace, + string $path, + ?ReflectorFactoryInterface $reflectorFactory = null, + int $flags = 0, + array $classMap = [], + ?array $excludeNamespaces = null + ) { $this->namespace = $namespace; $this->path = PathNormalizer::resolvePath($path); + $this->reflectorFactory = $reflectorFactory ?? new NativeReflectorFactory(); $this->prefixLen = strlen($this->path); $this->classMap = array_map(PathNormalizer::class . '::resolvePath', $classMap); $this->excludeNamespaces = $excludeNamespaces; @@ -96,7 +105,7 @@ protected function getGenerator(): Generator continue; } - yield $class => new ReflectionClass($class); + yield $class => $this->reflectorFactory->reflect($class); } } } diff --git a/lib/Reflection/NativeReflectorFactory.php b/lib/Reflection/NativeReflectorFactory.php new file mode 100644 index 0000000..e09734c --- /dev/null +++ b/lib/Reflection/NativeReflectorFactory.php @@ -0,0 +1,15 @@ + new ReflectionClass(Psr4\BarBar::class), diff --git a/tests/Iterator/FilteredComposerIteratorTest.php b/tests/Iterator/FilteredComposerIteratorTest.php index be4e4ed..6281265 100644 --- a/tests/Iterator/FilteredComposerIteratorTest.php +++ b/tests/Iterator/FilteredComposerIteratorTest.php @@ -44,7 +44,7 @@ protected function setUp(): void public function testComposerIteratorShouldWork(): void { - $iterator = new FilteredComposerIterator($this->loader, null, null, null); + $iterator = new FilteredComposerIterator($this->loader, null, null, null, null); self::assertEquals([ self::class => new ReflectionClass(self::class), @@ -68,7 +68,7 @@ public function testComposerIteratorShouldFilterNotIntersectingPath(): void // intersects perfectly with the requested dirs. The upper finder should filter out the // non-matching results. - $iterator = new FilteredComposerIterator($this->loader, null, null, [__DIR__ . '/../..' . '/data/Composer/Psr4/SubNs']); + $iterator = new FilteredComposerIterator($this->loader, null, null, null, [__DIR__ . '/../..' . '/data/Composer/Psr4/SubNs']); self::assertEquals([ Psr4\BarBar::class => new ReflectionClass(Psr4\BarBar::class), diff --git a/tests/Iterator/Psr0IteratorTest.php b/tests/Iterator/Psr0IteratorTest.php index 799deb3..03ec481 100644 --- a/tests/Iterator/Psr0IteratorTest.php +++ b/tests/Iterator/Psr0IteratorTest.php @@ -6,6 +6,7 @@ use Kcs\ClassFinder\Fixtures\Psr0; use Kcs\ClassFinder\Iterator\Psr0Iterator; +use Kcs\ClassFinder\Reflection\NativeReflectorFactory; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -14,11 +15,12 @@ class Psr0IteratorTest extends TestCase { - public function testIteratorShouldWork() + public function testIteratorShouldWork(): void { $iterator = new Psr0Iterator( 'Kcs\\ClassFinder\\Fixtures\\Psr0\\', - realpath(__DIR__ . '/../../data/Composer/Psr0') + realpath(__DIR__ . '/../../data/Composer/Psr0'), + new NativeReflectorFactory() ); self::assertEquals([ diff --git a/tests/Iterator/Psr4IteratorTest.php b/tests/Iterator/Psr4IteratorTest.php index 5fcabe5..314bebd 100644 --- a/tests/Iterator/Psr4IteratorTest.php +++ b/tests/Iterator/Psr4IteratorTest.php @@ -6,6 +6,7 @@ use Kcs\ClassFinder\Fixtures\Psr4; use Kcs\ClassFinder\Iterator\Psr4Iterator; +use Kcs\ClassFinder\Reflection\NativeReflectorFactory; use PHPUnit\Framework\TestCase; use ReflectionClass; @@ -14,11 +15,12 @@ class Psr4IteratorTest extends TestCase { - public function testIteratorShouldWork() + public function testIteratorShouldWork(): void { $iterator = new Psr4Iterator( 'Kcs\\ClassFinder\\Fixtures\\Psr4\\', - realpath(__DIR__ . '/../../data/Composer/Psr4') + realpath(__DIR__ . '/../../data/Composer/Psr4'), + new NativeReflectorFactory() ); self::assertEquals([