diff --git a/composer.json b/composer.json index 2930e01e..39102f86 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,6 @@ "ray/aop": "^2.10.4", "ray/aura-sql-module": "^1.12.0", "ray/di": "^2.14", - "roave/better-reflection": "^4.12 || ^5.6 || ^6.25", "symfony/polyfill-php81": "^1.24" }, "require-dev": { diff --git a/src/ClassesInDirectories.php b/src/ClassesInDirectories.php index c814c686..1d34dbd7 100644 --- a/src/ClassesInDirectories.php +++ b/src/ClassesInDirectories.php @@ -5,51 +5,84 @@ namespace Ray\MediaQuery; use Generator; -use Roave\BetterReflection\BetterReflection; -use Roave\BetterReflection\Reflector\DefaultReflector; -use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator; -use Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator; -use Roave\BetterReflection\SourceLocator\Type\DirectoriesSourceLocator; - -use function assert; -use function class_exists; -use function interface_exists; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use SplFileInfo; final class ClassesInDirectories { /** - * get a list of all classes in the given directories. - * - * Based on: https://github.com/Roave/BetterReflection/blob/396a07c9d276cb9ffba581b24b2dadbb542d542e/demo/parsing-whole-directory/example2.php. - * * @param list $directories - * * @return Generator - * - * This function code is taken from https://github.com/WyriHaximus/php-list-classes-in-directory/blob/master/src/functions.php - * and modified for roave/better-reflection 5.x - * - * @see https://github.com/WyriHaximus/php-list-classes-in-directory - * @psalm-suppress MixedReturnTypeCoercion - * @phpstan-ignore-next-line/ */ - public static function list(string ...$directories): iterable + public static function list(string ...$directories): Generator + { + foreach ($directories as $directory) { + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($directory) + ); + + foreach ($iterator as $file) { + if (! $file instanceof SplFileInfo) { + continue; + } + + if ($file->getExtension() !== 'php') { + continue; + } + + $className = self::getClassFromFile($file->getRealPath()); + if ($className === null) { + continue; + } + + if (class_exists($className) || interface_exists($className)) { + yield $className; + } + } + } + } + + private static function getClassFromFile(string $filePath): ?string { - /** @var list $directories */ - $sourceLocator = new AggregateSourceLocator([ - new DirectoriesSourceLocator( - $directories, - (new BetterReflection())->astLocator(), - ), - // ↓ required to autoload parent classes/interface from another directory than /src (e.g. /vendor) - new AutoloadSourceLocator((new BetterReflection())->astLocator()), - ]); - - foreach ((new DefaultReflector($sourceLocator))->reflectAllClasses() as $class) { - $className = $class->getName(); - assert(class_exists($className) || interface_exists($className)); - - yield $className; + $content = file_get_contents($filePath); + if ($content === false) { + return null; } + + $namespace = ''; + $class = ''; + $tokens = token_get_all($content); + $count = count($tokens); + + for ($i = 0; $i < $count; $i++) { + if (!isset($tokens[$i][0])) { + continue; + } + + if ($tokens[$i][0] === T_NAMESPACE) { + for ($j = $i + 1; $j < $count; $j++) { + if ($tokens[$j][0] === T_NAME_QUALIFIED) { + $namespace = $tokens[$j][1]; + break; + } + } + } + + if ($tokens[$i][0] === T_CLASS || $tokens[$i][0] === T_INTERFACE) { + for ($j = $i + 1; $j < $count; $j++) { + if ($tokens[$j][0] === T_STRING) { + $class = $tokens[$j][1]; + break 2; + } + } + } + } + + if ($class === '') { + return null; + } + + return $namespace ? $namespace . '\\' . $class : $class; } }