Skip to content

Commit

Permalink
Merge pull request #59 from ray-di/php8.4
Browse files Browse the repository at this point in the history
Enable PHP 8.4 comat
  • Loading branch information
koriym authored Nov 18, 2024
2 parents 718bcf2 + 3695ce2 commit c89eaab
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 34 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"php": "^8.1",
"ext-pdo": "*",
"ext-mbstring": "*",
"ext-tokenizer": "*",
"aura/sql": "^4.0 || ^5.0",
"doctrine/annotations": "^1.12 || ^2.0",
"guzzlehttp/guzzle": "^6.3 || ^7.2",
Expand All @@ -24,7 +25,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": {
Expand Down
137 changes: 104 additions & 33 deletions src/ClassesInDirectories.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,122 @@
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 RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;

use function assert;
use function class_exists;
use function count;
use function file_get_contents;
use function interface_exists;
use function is_array;
use function is_string;
use function token_get_all;

use const T_CLASS;
use const T_INTERFACE;
use const T_NAME_QUALIFIED;
use const T_NAMESPACE;
use const T_STRING;

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<string> $directories
+ * @param string ...$directories
*
* @return Generator<int, class-string>
*
* 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),
);

/** @psalm-suppress MixedAssignment */
foreach ($iterator as $file) {
if (! $file instanceof SplFileInfo || $file->getExtension() !== 'php') {
continue;
}

$className = self::getClassFromFile($file->getRealPath());

if ($className === null || ! class_exists($className) && ! interface_exists($className)) {
continue;
}

yield $className;
}
}
}

private static function getClassFromFile(string $filePath): string|null
{
$content = file_get_contents($filePath);
if ($content === false) {
return null;
}

$tokens = token_get_all($content);
/** @var array<int, mixed> $tokens */

$namespace = self::extractNamespace($tokens);
$class = self::extractClassName($tokens);

if ($class === null) {
return null;
}

if ($namespace === null) {
return $class;
}

return $namespace . '\\' . $class;
}

/** @param array<int, mixed> $tokens*/
private static function extractNamespace(array $tokens): string|null
{
/** @var list<string> $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;
/** @psalm-suppress MixedAssignment */
foreach ($tokens as $index => $token) {
if (is_array($token) && $token[0] !== T_NAMESPACE) {
continue;
}

for ($j = $index + 1, $count = count($tokens); $j < $count; $j++) {
if (isset($tokens[$j][0]) && $tokens[$j][0] === T_NAME_QUALIFIED) { // @phpstan-ignore-line
assert(isset($tokens[$j][1])); // @phpstan-ignore-line
$string = $tokens[$j][1];
assert(is_string($string));

return $string;
}
}
}

return null;
}

/** @param array<int, mixed> $tokens */
private static function extractClassName(array $tokens): string|null
{
/** @psalm-suppress MixedAssignment */
foreach ($tokens as $index => $token) {
if (isset($token[0]) && $token[0] !== T_CLASS && $token[0] !== T_INTERFACE) { // @phpstan-ignore-line
continue;
}

for ($j = $index + 1, $count = count($tokens); $j < $count; $j++) {
if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
$string = $tokens[$j][1];
assert(is_string($string));

return $string;
}
}
}

return null;
}
}

0 comments on commit c89eaab

Please sign in to comment.