126 lines
3.5 KiB
PHP
126 lines
3.5 KiB
PHP
<?php declare(strict_types=1);
|
|
|
|
namespace Lubian\AttributeMagic\Infrastructure;
|
|
|
|
use Iterator;
|
|
use Lubian\AttributeMagic\Infrastructure\Event\AsListener;
|
|
use Lubian\AttributeMagic\Infrastructure\Event\Listener;
|
|
use Lubian\AttributeMagic\Infrastructure\Route\AsHandler;
|
|
use Lubian\AttributeMagic\Infrastructure\Route\Handler;
|
|
use RecursiveDirectoryIterator;
|
|
use RecursiveIteratorIterator;
|
|
use ReflectionClass;
|
|
use SplFileInfo;
|
|
|
|
use function array_diff;
|
|
use function array_map;
|
|
use function file_exists;
|
|
use function file_get_contents;
|
|
use function file_put_contents;
|
|
use function get_declared_classes;
|
|
use function iterator_to_array;
|
|
use function serialize;
|
|
use function str_ends_with;
|
|
use function unserialize;
|
|
|
|
final class Finder
|
|
{
|
|
public const CACHE_FILE = __DIR__ . '/../../var/classesCache';
|
|
|
|
/**
|
|
* @param class-string[] $classNames
|
|
*/
|
|
public function __construct(
|
|
private readonly string $path,
|
|
private array $classNames = [],
|
|
private readonly bool $cached = false,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @return Listener[]
|
|
*/
|
|
public function getListeners(): array
|
|
{
|
|
$this->populateClassnames();
|
|
return array_map(
|
|
static fn (array $h): Listener
|
|
=> new Listener($h[0]->eventClass, $h[0]->priority, $h[1], $h[2]),
|
|
iterator_to_array($this->getAttributes(AsListener::class)),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return Handler[]
|
|
*/
|
|
public function getHandlers(): array
|
|
{
|
|
$this->populateClassnames();
|
|
return array_map(
|
|
static fn (array $h): Handler
|
|
=> new Handler($h[0]->method, $h[0]->path, $h[1], $h[2]),
|
|
iterator_to_array($this->getAttributes(AsHandler::class)),
|
|
);
|
|
}
|
|
|
|
private function populateClassnames(): void
|
|
{
|
|
if ($this->classNames !== []) {
|
|
return;
|
|
}
|
|
|
|
if ($this->cached === true && file_exists(self::CACHE_FILE)) {
|
|
$data = file_get_contents(self::CACHE_FILE);
|
|
|
|
if ($data === false) {
|
|
return;
|
|
}
|
|
|
|
/** @var class-string[] $result */
|
|
$result = unserialize($data);
|
|
$this->classNames = $result;
|
|
return;
|
|
}
|
|
|
|
$it = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($this->path),
|
|
);
|
|
|
|
/** @var SplFileInfo $file */
|
|
foreach ($it as $file) {
|
|
if (! str_ends_with((string) $file, '.php')) {
|
|
continue;
|
|
}
|
|
|
|
$classesBeforeLoad = get_declared_classes();
|
|
require_once (string) $file;
|
|
$classesAfterLoad = get_declared_classes();
|
|
$this->classNames = [
|
|
...$this->classNames,
|
|
...array_diff($classesAfterLoad, $classesBeforeLoad),
|
|
];
|
|
}
|
|
|
|
if ($this->cached === true) {
|
|
file_put_contents(self::CACHE_FILE, serialize($this->classNames));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @param class-string<T> $attributeClass
|
|
* @return Iterator<array{T, class-string, non-empty-string}>
|
|
*/
|
|
private function getAttributes(string $attributeClass): Iterator
|
|
{
|
|
foreach ($this->classNames as $class) {
|
|
$reflectionClass = new ReflectionClass($class);
|
|
|
|
foreach ($reflectionClass->getMethods() as $method) {
|
|
foreach ($method->getAttributes($attributeClass) as $attribute) {
|
|
yield [$attribute->newInstance(), $class, $method->getName()];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|