move code to repository root
This commit is contained in:
parent
cd9cc89726
commit
47e227257c
35 changed files with 0 additions and 2025 deletions
|
@ -1,51 +0,0 @@
|
||||||
{
|
|
||||||
"name": "lubian/attribute-magic",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Lubian\\AttributeMagic\\": "src/"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"src/Infrastructure/functions.php"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "lubiana",
|
|
||||||
"email": "lubiana123@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"require": {
|
|
||||||
"php": ">=8.1",
|
|
||||||
"php-di/php-di": "^6.4",
|
|
||||||
"nikic/fast-route": "^1.3",
|
|
||||||
"symfony/http-foundation": "^6.1.7"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"phpstan/phpstan": "^1.9.1",
|
|
||||||
"lubiana/code-quality": "^1.1",
|
|
||||||
"phpstan/extension-installer": "^1.2",
|
|
||||||
"phpstan/phpstan-strict-rules": "^1.4",
|
|
||||||
"thecodingmachine/phpstan-strict-rules": "^1.0"
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"allow-plugins": {
|
|
||||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
|
||||||
"phpstan/extension-installer": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"serve": [
|
|
||||||
"Composer\\Config::disableProcessTimeout",
|
|
||||||
"php -S 0.0.0.0:1235 -t public"
|
|
||||||
],
|
|
||||||
"phpstan": "./vendor/bin/phpstan analyze",
|
|
||||||
"baseline": "./vendor/bin/phpstan analyze --generate-baseline",
|
|
||||||
"style": "./vendor/bin/ecs",
|
|
||||||
"fix": [
|
|
||||||
"rector process" ,"ecs --fix", "ecs --fix"
|
|
||||||
],
|
|
||||||
"rector": "./vendor/bin/rector process"
|
|
||||||
},
|
|
||||||
"minimum-stability": "dev",
|
|
||||||
"prefer-stable": true
|
|
||||||
}
|
|
1162
app/composer.lock
generated
1162
app/composer.lock
generated
File diff suppressed because it is too large
Load diff
18
app/ecs.php
18
app/ecs.php
|
@ -1,18 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
use Lubiana\CodeQuality\LubiSetList;
|
|
||||||
use Symplify\EasyCodingStandard\Config\ECSConfig;
|
|
||||||
|
|
||||||
return static function (ECSConfig $c): void {
|
|
||||||
$c->parallel();
|
|
||||||
$c->paths([
|
|
||||||
__DIR__ . '/src',
|
|
||||||
__DIR__ . '/config',
|
|
||||||
__DIR__ . '/ecs.php',
|
|
||||||
__DIR__ . '/rector.php',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$c->sets([
|
|
||||||
LubiSetList::ECS,
|
|
||||||
]);
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
includes:
|
|
||||||
- phpstan-baseline.neon
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
level: max
|
|
||||||
paths:
|
|
||||||
- src
|
|
||||||
- config
|
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
|
@ -1,3 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
require __DIR__ . '/../src/Bootstrap.php';
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
use Lubiana\CodeQuality\LubiSetList;
|
|
||||||
use Rector\Config\RectorConfig;
|
|
||||||
|
|
||||||
return static function (RectorConfig $c): void {
|
|
||||||
$c->paths([__DIR__ . '/src', __DIR__ . '/config', __DIR__ . '/ecs.php', __DIR__ . '/rector.php']);
|
|
||||||
|
|
||||||
$c->importNames();
|
|
||||||
|
|
||||||
$c->sets([
|
|
||||||
LubiSetList::RECTOR,
|
|
||||||
]);
|
|
||||||
};
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic;
|
|
||||||
|
|
||||||
use DI\ContainerBuilder;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\Dispatcher;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\DispatcherFactory;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Finder;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\WebApp\Request\RequestEvent;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\WebApp\Route\HandlerResolver;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
|
|
||||||
use function assert;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
|
||||||
|
|
||||||
$cached = false;
|
|
||||||
$container = (new ContainerBuilder)->addDefinitions([
|
|
||||||
Finder::class => static fn (): Finder => new Finder(__DIR__, [], $cached),
|
|
||||||
Dispatcher::class => static fn (DispatcherFactory $f): Dispatcher => $f->getDispatcher($cached),
|
|
||||||
HandlerResolver::class => static fn (Dispatcher $d): HandlerResolver => new HandlerResolver($d, $cached),
|
|
||||||
])->build();
|
|
||||||
$dispatcher = $container->get(Dispatcher::class);
|
|
||||||
assert($dispatcher instanceof Dispatcher);
|
|
||||||
$request = new RequestEvent(Request::createFromGlobals());
|
|
||||||
|
|
||||||
$dispatcher->dispatch($request);
|
|
||||||
|
|
||||||
$request->response?->send();
|
|
||||||
exit;
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Handler;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\AsHandler;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\HttpMethod;
|
|
||||||
|
|
||||||
final class Auchnoch
|
|
||||||
{
|
|
||||||
#[AsHandler(HttpMethod::GET, '/auchnoch')]
|
|
||||||
public function undso(): void
|
|
||||||
{
|
|
||||||
echo 'AUCHNOCH';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Handler;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\AsHandler;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\HttpMethod;
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
final class HalloDimi
|
|
||||||
{
|
|
||||||
#[AsHandler(HttpMethod::GET, '/dimi')]
|
|
||||||
public function hallo(): Response
|
|
||||||
{
|
|
||||||
return new JsonResponse(['Hallo Dimi']);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Handler;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\Dispatcher;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\AsHandler;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\HttpMethod;
|
|
||||||
use Lubian\AttributeMagic\Listener\LauschEvent;
|
|
||||||
|
|
||||||
final class Lol
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private readonly Dispatcher $dispatcher,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[AsHandler(HttpMethod::GET, '/')]
|
|
||||||
public function lol(): void
|
|
||||||
{
|
|
||||||
$event = new LauschEvent('aaaaaaaalso');
|
|
||||||
$this->dispatcher->dispatch($event);
|
|
||||||
echo $event->message;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\Event;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD)]
|
|
||||||
final class AsListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param class-string $eventClass
|
|
||||||
* @param int<-100, 100> $priority
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
public readonly string $eventClass,
|
|
||||||
public readonly int $priority = 0,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\Event;
|
|
||||||
|
|
||||||
use Invoker\InvokerInterface;
|
|
||||||
|
|
||||||
use function Lubian\AttributeMagic\Infrastructure\arrayFilter;
|
|
||||||
use function usort;
|
|
||||||
|
|
||||||
final class Dispatcher
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param Listener[] $listeners
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
private readonly InvokerInterface $invoker,
|
|
||||||
private array $listeners = [],
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addListener(Listener $listener): void
|
|
||||||
{
|
|
||||||
$this->listeners[] = $listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Listener[]
|
|
||||||
*/
|
|
||||||
public function getListenerForEvent(GenericEvent $event): array
|
|
||||||
{
|
|
||||||
return arrayFilter(
|
|
||||||
$this->listeners,
|
|
||||||
static fn (Listener $l): bool => $l->eventClass === $event::class,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Listener[]
|
|
||||||
*/
|
|
||||||
public function getSortedListenerForEvent(GenericEvent $event): array
|
|
||||||
{
|
|
||||||
$filtered = $this->getListenerForEvent($event);
|
|
||||||
usort($filtered, static fn (Listener $a, Listener $b): int => $a->priority <=> $b->priority);
|
|
||||||
return $filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dispatch(GenericEvent $event): void
|
|
||||||
{
|
|
||||||
foreach ($this->getSortedListenerForEvent($event) as $listener) {
|
|
||||||
if ($event->stopped === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->invoker->call(
|
|
||||||
[$listener->listenerClass, $listener->listenerMethod],
|
|
||||||
[$event],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\Event;
|
|
||||||
|
|
||||||
use Invoker\InvokerInterface;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Finder;
|
|
||||||
|
|
||||||
use function file_exists;
|
|
||||||
use function file_get_contents;
|
|
||||||
use function file_put_contents;
|
|
||||||
use function serialize;
|
|
||||||
use function unserialize;
|
|
||||||
|
|
||||||
final class DispatcherFactory
|
|
||||||
{
|
|
||||||
public const CACHE_FILE = __DIR__ . '/../../../var/listenerCache';
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private readonly Finder $finder,
|
|
||||||
private readonly InvokerInterface $invoker
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDispatcher(bool $cached = false): Dispatcher
|
|
||||||
{
|
|
||||||
return new Dispatcher($this->invoker, $this->getListeners($cached));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Listener[]
|
|
||||||
*/
|
|
||||||
private function getListeners(bool $cached = false): array
|
|
||||||
{
|
|
||||||
if ($cached === true && file_exists(self::CACHE_FILE)) {
|
|
||||||
$data = file_get_contents(self::CACHE_FILE);
|
|
||||||
if ($data === false) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var Listener[] $listeners */
|
|
||||||
$listeners = unserialize($data);
|
|
||||||
return $listeners;
|
|
||||||
}
|
|
||||||
|
|
||||||
$listeners = $this->finder->getListeners();
|
|
||||||
if ($cached === true) {
|
|
||||||
file_put_contents(self::CACHE_FILE, serialize($listeners));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $listeners;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\Event;
|
|
||||||
|
|
||||||
abstract class GenericEvent
|
|
||||||
{
|
|
||||||
public bool $stopped = false;
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\Event;
|
|
||||||
|
|
||||||
final class Listener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param class-string $eventClass
|
|
||||||
* @param int<-100, 100> $priority
|
|
||||||
* @param class-string $listenerClass
|
|
||||||
* @param non-empty-string $listenerMethod
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
public readonly string $eventClass,
|
|
||||||
public readonly int $priority,
|
|
||||||
public readonly string $listenerClass,
|
|
||||||
public readonly string $listenerMethod,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
<?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(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\Route;
|
|
||||||
|
|
||||||
use Attribute;
|
|
||||||
|
|
||||||
#[Attribute(Attribute::TARGET_METHOD)]
|
|
||||||
final class AsHandler
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param non-empty-string $path
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
public readonly HttpMethod $method,
|
|
||||||
public readonly string $path,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\Route;
|
|
||||||
|
|
||||||
final class Handler
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param non-empty-string $handlerClass
|
|
||||||
* @param non-empty-string $handlerMethod
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
public readonly HttpMethod $method,
|
|
||||||
public readonly string $path,
|
|
||||||
public readonly string $handlerClass,
|
|
||||||
public readonly string $handlerMethod,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\Route;
|
|
||||||
|
|
||||||
enum HttpMethod: string
|
|
||||||
{
|
|
||||||
case GET = 'GET';
|
|
||||||
case POST = 'POST';
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\WebApp\Request;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\GenericEvent;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\Handler;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
final class RequestEvent extends GenericEvent
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
public Request $request,
|
|
||||||
public Response|null $response = null,
|
|
||||||
public Handler|null $handler = null
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\WebApp\Route;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\AsListener;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Finder;
|
|
||||||
|
|
||||||
final class AttributeRouteCollector
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private readonly Finder $finder
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[AsListener(CollectRoutes::class)]
|
|
||||||
public function collect(CollectRoutes $event): void
|
|
||||||
{
|
|
||||||
$event->routes = $this->finder->getHandlers();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\WebApp\Route;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\AsListener;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\WebApp\Request\RequestEvent;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
use function apcu_add;
|
|
||||||
use function md5;
|
|
||||||
use function serialize;
|
|
||||||
use function unserialize;
|
|
||||||
|
|
||||||
final class CachedResponse
|
|
||||||
{
|
|
||||||
#[AsListener(RequestEvent::class, -99)]
|
|
||||||
public function cachedResponse(RequestEvent $event): void
|
|
||||||
{
|
|
||||||
$response = new Response('lol');
|
|
||||||
$serialized = serialize($response);
|
|
||||||
/** @var Response|null $response */
|
|
||||||
$response = unserialize($serialized);
|
|
||||||
$event->response = $response;
|
|
||||||
|
|
||||||
$event->stopped = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[AsListener(RequestEvent::class, 99)]
|
|
||||||
public function doCache(RequestEvent $event): void
|
|
||||||
{
|
|
||||||
if ($event->response === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = md5($event->request->getPathInfo());
|
|
||||||
apcu_add($path, serialize($event->response));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\WebApp\Route;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\AsListener;
|
|
||||||
|
|
||||||
use function file_exists;
|
|
||||||
use function file_get_contents;
|
|
||||||
use function file_put_contents;
|
|
||||||
use function serialize;
|
|
||||||
use function unserialize;
|
|
||||||
|
|
||||||
final class CachedRouteCollector
|
|
||||||
{
|
|
||||||
public const ROUTES_DIR = __DIR__ . '/../../../../var/routesCache';
|
|
||||||
|
|
||||||
#[AsListener(CollectRoutes::class, -10)]
|
|
||||||
public function getCached(CollectRoutes $event): void
|
|
||||||
{
|
|
||||||
if ($event->cached === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! file_exists(self::ROUTES_DIR)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$event->routes = unserialize(file_get_contents(self::ROUTES_DIR)); //@phpstan-ignore-line
|
|
||||||
$event->stopped = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[AsListener(CollectRoutes::class, 10)]
|
|
||||||
public function setCached(CollectRoutes $event): void
|
|
||||||
{
|
|
||||||
if ($event->cached === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
file_put_contents(
|
|
||||||
self::ROUTES_DIR,
|
|
||||||
serialize($event->routes),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\WebApp\Route;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\GenericEvent;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\Handler;
|
|
||||||
|
|
||||||
final class CollectRoutes extends GenericEvent
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Handler[]
|
|
||||||
*/
|
|
||||||
public array $routes = [];
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public readonly bool $cached = false,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\WebApp\Route;
|
|
||||||
|
|
||||||
use Invoker\InvokerInterface;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\AsListener;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\WebApp\Request\RequestEvent;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
use function ob_get_clean;
|
|
||||||
use function ob_start;
|
|
||||||
|
|
||||||
final class HandlerCaller
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private readonly InvokerInterface $invoker
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[AsListener(RequestEvent::class, -50)]
|
|
||||||
public function callHandler(RequestEvent $event): void
|
|
||||||
{
|
|
||||||
if ($event->handler === null) {
|
|
||||||
$event->stopped = true;
|
|
||||||
$event->response = new Response('not resolved', 500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$response = $this->invoker->call(
|
|
||||||
[$event->handler->handlerClass, $event->handler->handlerMethod],
|
|
||||||
$event->request->attributes->all()
|
|
||||||
);
|
|
||||||
$output = (string) ob_get_clean();
|
|
||||||
|
|
||||||
if ($response instanceof Response) {
|
|
||||||
$event->response = $response;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$event->response = new Response($output, 200);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure\WebApp\Route;
|
|
||||||
|
|
||||||
use FastRoute\RouteCollector;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\AsListener;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\Dispatcher;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\Handler;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\HttpMethod;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\WebApp\Request\RequestEvent;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
use function FastRoute\cachedDispatcher;
|
|
||||||
|
|
||||||
final class HandlerResolver
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private readonly Dispatcher $dispatcher,
|
|
||||||
private readonly bool $cached = false,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[AsListener(RequestEvent::class, -90)]
|
|
||||||
public function resolveHandler(RequestEvent $event): void
|
|
||||||
{
|
|
||||||
$routesEvent = new CollectRoutes($this->cached);
|
|
||||||
$this->dispatcher->dispatch($routesEvent);
|
|
||||||
$dispatcher = $this->dispatcher;
|
|
||||||
|
|
||||||
$routeDispatcher = cachedDispatcher(static function (RouteCollector $r) use (
|
|
||||||
$dispatcher,
|
|
||||||
$routesEvent
|
|
||||||
): void {
|
|
||||||
$dispatcher->dispatch($routesEvent);
|
|
||||||
foreach ($routesEvent->routes as $h) {
|
|
||||||
$r->addRoute($h->method->value, $h->path, [$h->handlerClass, $h->handlerMethod]);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
'cacheFile' => __DIR__ . '/../../../../var/route.cache',
|
|
||||||
'cacheDisabled' => ! $this->cached,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$routeInfo = $routeDispatcher->dispatch(
|
|
||||||
$event->request->getMethod(),
|
|
||||||
$event->request->getPathInfo(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($routeInfo[0] === \FastRoute\Dispatcher::NOT_FOUND) {
|
|
||||||
$event->response = new Response('Not Found', 404);
|
|
||||||
$event->stopped = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($routeInfo[0] === \FastRoute\Dispatcher::METHOD_NOT_ALLOWED) {
|
|
||||||
$event->response = new Response('Not Allowed', 403);
|
|
||||||
$event->stopped = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$event->handler = new Handler(
|
|
||||||
HttpMethod::from($event->request->getMethod()),
|
|
||||||
$event->request->getPathInfo(),
|
|
||||||
...$routeInfo[1],
|
|
||||||
);
|
|
||||||
|
|
||||||
$event->request->attributes->add($routeInfo[2]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Infrastructure;
|
|
||||||
|
|
||||||
use function array_filter;
|
|
||||||
use function array_values;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @param array<T> $input
|
|
||||||
* @return array<int, T>
|
|
||||||
*/
|
|
||||||
function arrayFilter(
|
|
||||||
array $input,
|
|
||||||
callable $callable
|
|
||||||
): array {
|
|
||||||
return array_values(
|
|
||||||
array_filter(
|
|
||||||
$input,
|
|
||||||
$callable,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Listener;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\GenericEvent;
|
|
||||||
|
|
||||||
final class LauschEvent extends GenericEvent
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
public string $message
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Listener;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Event\AsListener;
|
|
||||||
|
|
||||||
final class Lauschdoch
|
|
||||||
{
|
|
||||||
#[AsListener(LauschEvent::class, 0)]
|
|
||||||
public function ichmachschon(LauschEvent $event): LauschEvent
|
|
||||||
{
|
|
||||||
$event->message = $event->message . ' lolduuuuu';
|
|
||||||
return $event;
|
|
||||||
}
|
|
||||||
}
|
|
57
app/test.php
57
app/test.php
|
@ -1,57 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
$template = <<<'EOT'
|
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
|
|
||||||
namespace Lubian\AttributeMagic\Handler\Viel;
|
|
||||||
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\AsHandler;
|
|
||||||
use Lubian\AttributeMagic\Infrastructure\Route\HttpMethod;
|
|
||||||
|
|
||||||
final class Template
|
|
||||||
{
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template0')]
|
|
||||||
public function template0(): void {}
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template1')]
|
|
||||||
public function template2(): void {}
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template2')]
|
|
||||||
public function template3(): void {}
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template3')]
|
|
||||||
public function template4(): void {}
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template4')]
|
|
||||||
public function template5(): void {}
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template5')]
|
|
||||||
public function template6(): void {}
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template7')]
|
|
||||||
public function template7(): void {}
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template8')]
|
|
||||||
public function template8(): void {}
|
|
||||||
#[AsHandler(HttpMethod::GET, '/Viel/template9')]
|
|
||||||
public function template9(): void {}
|
|
||||||
}
|
|
||||||
EOT;
|
|
||||||
|
|
||||||
function randomString(int $length = 20) {
|
|
||||||
$str = '';
|
|
||||||
$chars = 'abzdefghijklmnopqrstuvwxyz';
|
|
||||||
$chars = $chars . strtoupper($chars);
|
|
||||||
$chars = str_split($chars, 1);
|
|
||||||
$upper = count($chars) -1;
|
|
||||||
for ($i = 0; $i < $length; $i++) {
|
|
||||||
$int = random_int(0, $upper);
|
|
||||||
$str = $str . $chars[$int];
|
|
||||||
}
|
|
||||||
return $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for($c = 0; $c < 1000; $c++) {
|
|
||||||
$name = randomString();
|
|
||||||
$content = str_replace(['Template', 'template'], [$name, $name], $template);
|
|
||||||
file_put_contents(__DIR__ . '/src/Handler/Viel/' . $name . '.php', $content);
|
|
||||||
}
|
|
Loading…
Reference in a new issue