Skip to content

Commit

Permalink
add reflector factory for runtime-based finders
Browse files Browse the repository at this point in the history
  • Loading branch information
alekitto committed Mar 31, 2021
1 parent e8b9c81 commit 18898c2
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 30 deletions.
13 changes: 11 additions & 2 deletions lib/Finder/ComposerFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Reflector>
*/
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);
Expand Down
11 changes: 10 additions & 1 deletion lib/Finder/Psr0Finder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand All @@ -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<Reflector>
*/
public function getIterator(): Iterator
{
return $this->applyFilters(new Psr0Iterator($this->namespace, $this->path));
return $this->applyFilters(new Psr0Iterator($this->namespace, $this->path, $this->reflectorFactory));
}
}
11 changes: 10 additions & 1 deletion lib/Finder/Psr4Finder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand All @@ -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<Reflector>
*/
public function getIterator(): Iterator
{
return $this->applyFilters(new Psr4Iterator($this->namespace, $this->path));
return $this->applyFilters(new Psr4Iterator($this->namespace, $this->path, $this->reflectorFactory));
}
}
13 changes: 8 additions & 5 deletions lib/Iterator/ComposerIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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);
}
Expand All @@ -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 {
Expand All @@ -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());
}
}
}
Expand Down
15 changes: 9 additions & 6 deletions lib/Iterator/FilteredComposerIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,6 +30,7 @@
final class FilteredComposerIterator extends ClassIterator
{
private ClassLoader $classLoader;
private ReflectorFactoryInterface $reflectorFactory;

/** @var string[]|null */
private ?array $namespaces = null;
Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
}

Expand Down
17 changes: 13 additions & 4 deletions lib/Iterator/Psr0Iterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,6 +28,7 @@ final class Psr0Iterator extends ClassIterator

private string $namespace;
private int $pathLen;
private ReflectorFactoryInterface $reflectorFactory;

/** @var string[] */
private array $classMap;
Expand All @@ -38,10 +40,17 @@ final class Psr0Iterator extends ClassIterator
* @param array<string, mixed> $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;
Expand Down Expand Up @@ -100,7 +109,7 @@ protected function getGenerator(): Generator
continue;
}

yield $class => new ReflectionClass($class);
yield $class => $this->reflectorFactory->reflect($class);
}
}
}
17 changes: 13 additions & 4 deletions lib/Iterator/Psr4Iterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,6 +28,7 @@ final class Psr4Iterator extends ClassIterator

private string $namespace;
private int $prefixLen;
private ReflectorFactoryInterface $reflectorFactory;

/** @var array<string, mixed> */
private array $classMap;
Expand All @@ -38,10 +40,17 @@ final class Psr4Iterator extends ClassIterator
* @param array<string, mixed> $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;
Expand Down Expand Up @@ -96,7 +105,7 @@ protected function getGenerator(): Generator
continue;
}

yield $class => new ReflectionClass($class);
yield $class => $this->reflectorFactory->reflect($class);
}
}
}
15 changes: 15 additions & 0 deletions lib/Reflection/NativeReflectorFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Kcs\ClassFinder\Reflection;

use ReflectionClass;

class NativeReflectorFactory implements ReflectorFactoryInterface
{
public function reflect(string $className): object
{
return new ReflectionClass($className);
}
}
18 changes: 18 additions & 0 deletions lib/Reflection/ReflectorFactoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Kcs\ClassFinder\Reflection;

interface ReflectorFactoryInterface
{
/**
* Builds a reflector object.
*
* Default implementation (NativeReflectorFactory) will always return an instance
* of ReflectionClass, but this is not guaranteed for other factories.
*
* @phpstan-param class-string $className
*/
public function reflect(string $className): object;
}
2 changes: 1 addition & 1 deletion tests/Iterator/ComposerIteratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public function testComposerIteratorShouldSkipNonInstantiableClass(): void
];
}, null, ClassLoader::class))();

$iterator = new ComposerIterator($loader, ClassIterator::SKIP_NON_INSTANTIABLE);
$iterator = new ComposerIterator($loader, null, ClassIterator::SKIP_NON_INSTANTIABLE);

self::assertEquals([
Psr4\BarBar::class => new ReflectionClass(Psr4\BarBar::class),
Expand Down
4 changes: 2 additions & 2 deletions tests/Iterator/FilteredComposerIteratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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),
Expand Down

0 comments on commit 18898c2

Please sign in to comment.