chore: complete update
This commit is contained in:
8
src/Framework/Core/Events/AfterEmitResponse.php
Normal file
8
src/Framework/Core/Events/AfterEmitResponse.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
class AfterEmitResponse
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Framework/Core/Events/AfterHandleRequest.php
Normal file
8
src/Framework/Core/Events/AfterHandleRequest.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
class AfterHandleRequest
|
||||
{
|
||||
|
||||
}
|
||||
14
src/Framework/Core/Events/ApplicationBooted.php
Normal file
14
src/Framework/Core/Events/ApplicationBooted.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
final readonly class ApplicationBooted
|
||||
{
|
||||
public function __construct(
|
||||
public \DateTimeImmutable $bootTime,
|
||||
public string $environment,
|
||||
public string $version = 'dev',
|
||||
public \DateTimeImmutable $occurredAt = new \DateTimeImmutable()
|
||||
) {}
|
||||
}
|
||||
8
src/Framework/Core/Events/BeforeEmitResponse.php
Normal file
8
src/Framework/Core/Events/BeforeEmitResponse.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
class BeforeEmitResponse
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Framework/Core/Events/BeforeHandleRequest.php
Normal file
8
src/Framework/Core/Events/BeforeHandleRequest.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
class BeforeHandleRequest
|
||||
{
|
||||
|
||||
}
|
||||
14
src/Framework/Core/Events/ErrorOccurred.php
Normal file
14
src/Framework/Core/Events/ErrorOccurred.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
final readonly class ErrorOccurred
|
||||
{
|
||||
public function __construct(
|
||||
public \Throwable $error,
|
||||
public string $context = '',
|
||||
public ?string $requestId = null,
|
||||
public \DateTimeImmutable $occurredAt = new \DateTimeImmutable()
|
||||
) {}
|
||||
}
|
||||
40
src/Framework/Core/Events/EventCompilerPass.php
Normal file
40
src/Framework/Core/Events/EventCompilerPass.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
|
||||
/**
|
||||
* Führt abschließende Konfigurationen für das Event-System durch
|
||||
*/
|
||||
final class EventCompilerPass
|
||||
{
|
||||
/**
|
||||
* Richtet zusätzliche Event-Konfigurationen ein
|
||||
*
|
||||
* @param DefaultContainer $container Der DI-Container
|
||||
* @param EventDispatcher $dispatcher Der EventDispatcher
|
||||
*/
|
||||
public static function process(DefaultContainer $container, EventDispatcher $dispatcher): void
|
||||
{
|
||||
// Hier können weitere Event-Handler oder -Konfigurationen eingerichtet werden
|
||||
// Beispiel: System-Events registrieren, die nicht über Attribute verfügbar sind
|
||||
|
||||
// Beispiel für einen Shutdown-Handler
|
||||
register_shutdown_function(function () use ($dispatcher, $container) {
|
||||
$error = error_get_last();
|
||||
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
|
||||
$exception = new \ErrorException(
|
||||
$error['message'],
|
||||
0,
|
||||
$error['type'],
|
||||
$error['file'],
|
||||
$error['line']
|
||||
);
|
||||
|
||||
$dispatcher->dispatch(new ErrorOccurred($exception, 'shutdown'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
188
src/Framework/Core/Events/EventDispatcher.php
Normal file
188
src/Framework/Core/Events/EventDispatcher.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
|
||||
/**
|
||||
* Der EventDispatcher ist verantwortlich für die Verarbeitung von Events
|
||||
* und das Aufrufen der entsprechenden Event-Handler
|
||||
*/
|
||||
final class EventDispatcher
|
||||
{
|
||||
/** @var array<string, array> Mapping von Event-Typen zu Handlern */
|
||||
private array $handlers = [];
|
||||
|
||||
/** @var bool Gibt an, ob der EventDispatcher initialisiert wurde */
|
||||
private bool $initialized = false;
|
||||
|
||||
/**
|
||||
* @param DefaultContainer $container Der DI-Container
|
||||
* @param array|null $eventHandlers Array mit Event-Handlern aus der Autodiscovery
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly DefaultContainer $container,
|
||||
private readonly ?array $eventHandlers = null
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initialisiert den EventDispatcher mit den Event-Handlern
|
||||
*/
|
||||
private function initialize(): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->initialized = true;
|
||||
|
||||
if ($this->eventHandlers === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->eventHandlers as $handler) {
|
||||
$eventClass = $handler['event_class'];
|
||||
$this->handlers[$eventClass][] = $handler;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatcht ein Event an alle registrierten Handler
|
||||
*
|
||||
* @param object $event Das zu dispatchende Event-Objekt
|
||||
* @return array<mixed> Ergebnisse der Event-Handler
|
||||
*/
|
||||
public function dispatch(object $event): array
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$eventClass = get_class($event);
|
||||
$results = [];
|
||||
|
||||
// Exakte Übereinstimmung prüfen
|
||||
if (isset($this->handlers[$eventClass])) {
|
||||
foreach ($this->handlers[$eventClass] as $handler) {
|
||||
$result = $this->invokeHandler($handler, $event);
|
||||
$results[] = $result;
|
||||
|
||||
// Wenn ein Handler die Propagation stoppt, abbrechen
|
||||
if (isset($handler['attribute_data']) && $handler['attribute_data']['stopPropagation'] ?? false) {
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prüfen auf Handler für Basisklassen/Interfaces
|
||||
foreach ($this->handlers as $handledEventClass => $handlersList) {
|
||||
if ($handledEventClass === $eventClass) {
|
||||
continue; // Bereits verarbeitet
|
||||
}
|
||||
|
||||
if (is_subclass_of($event, $handledEventClass) ||
|
||||
($event instanceof $handledEventClass)) {
|
||||
foreach ($handlersList as $handler) {
|
||||
$result = $this->invokeHandler($handler, $event);
|
||||
$results[] = $result;
|
||||
|
||||
// Wenn ein Handler die Propagation stoppt, abbrechen
|
||||
if (isset($handler['attribute_data']) && $handler['attribute_data']['stopPropagation'] ?? false) {
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function __invoke(object $event): array
|
||||
{
|
||||
return $this->dispatch($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruft einen Event-Handler auf
|
||||
*
|
||||
* @param array $handler Handler-Definition
|
||||
* @param object $event Event-Objekt
|
||||
* @return mixed Ergebnis des Handler-Aufrufs
|
||||
*/
|
||||
private function invokeHandler(array $handler, object $event): mixed
|
||||
{
|
||||
if (isset($handler['callable'])) {
|
||||
// Callable direkt aufrufen
|
||||
return ($handler['callable'])($event);
|
||||
}
|
||||
|
||||
$handlerClass = $handler['class'];
|
||||
$methodName = $handler['method'];
|
||||
|
||||
// Handler-Instanz aus dem Container holen oder erstellen
|
||||
$handlerInstance = $this->container->get($handlerClass);
|
||||
|
||||
// Handler-Methode aufrufen
|
||||
return $handlerInstance->$methodName($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registriert einen Event-Handler manuell
|
||||
*
|
||||
* @param string $eventClass Vollqualifizierter Klassenname des Events
|
||||
* @param callable $handler Der Handler, der aufgerufen werden soll
|
||||
* @param int|null $priority Priorität des Handlers (höhere Werte werden zuerst ausgeführt)
|
||||
* @param bool $stopPropagation Gibt an, ob die Event-Propagation nach diesem Handler gestoppt werden soll
|
||||
* @return self
|
||||
*/
|
||||
public function addHandler(
|
||||
string $eventClass,
|
||||
callable $handler,
|
||||
?int $priority = null,
|
||||
bool $stopPropagation = false
|
||||
): self {
|
||||
$this->initialize();
|
||||
|
||||
// OnEvent-Attribut für die Priorität und Propagation erstellen
|
||||
$attribute = new OnEvent($priority, $stopPropagation);
|
||||
|
||||
$this->handlers[$eventClass][] = [
|
||||
'callable' => $handler,
|
||||
'attribute_data' => [
|
||||
'priority' => $priority ?? 0,
|
||||
'stopPropagation' => $stopPropagation
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
// Handlers nach Priorität sortieren
|
||||
if (isset($this->handlers[$eventClass]) && count($this->handlers[$eventClass]) > 1) {
|
||||
usort($this->handlers[$eventClass], function ($a, $b) {
|
||||
$priorityA = isset($a['attribute_data']) ? ($a['attribute_data']['priority'] ?? 0) : 0;
|
||||
$priorityB = isset($b['attribute_data']) ? ($b['attribute_data']['priority'] ?? 0) : 0;
|
||||
|
||||
return $priorityB <=> $priorityA;
|
||||
});
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob Handler für den angegebenen Event-Typ registriert sind
|
||||
*
|
||||
* @param string $eventClass Event-Klasse
|
||||
* @return bool True, wenn Handler existieren
|
||||
*/
|
||||
public function hasHandlersFor(string $eventClass): bool
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
if (isset($this->handlers[$eventClass]) && !empty($this->handlers[$eventClass])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prüfen auf Handler für Basisklassen/Interfaces
|
||||
return array_any(array_keys($this->handlers), fn($handledEventClass) => is_subclass_of($eventClass, $handledEventClass));
|
||||
|
||||
}
|
||||
}
|
||||
21
src/Framework/Core/Events/EventDispatcherInitializer.php
Normal file
21
src/Framework/Core/Events/EventDispatcherInitializer.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
use App\Framework\DI\Container;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\Discovery\Results\DiscoveryResults;
|
||||
|
||||
final readonly class EventDispatcherInitializer
|
||||
{
|
||||
public function __construct(
|
||||
private Container $container,
|
||||
private DiscoveryResults $results
|
||||
){}
|
||||
|
||||
#[Initializer]
|
||||
public function __invoke(): EventDispatcher
|
||||
{
|
||||
return new EventDispatcher($this->container, $this->results->get(OnEvent::class));
|
||||
}
|
||||
}
|
||||
41
src/Framework/Core/Events/EventHandlerCompiler.php
Normal file
41
src/Framework/Core/Events/EventHandlerCompiler.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
use App\Framework\Core\AttributeCompiler;
|
||||
|
||||
/**
|
||||
* Compiler für Event-Handler
|
||||
*/
|
||||
final class EventHandlerCompiler implements AttributeCompiler
|
||||
{
|
||||
/**
|
||||
* Gibt die Attributklasse zurück, die dieser Compiler verarbeitet
|
||||
*/
|
||||
public function getAttributeClass(): string
|
||||
{
|
||||
return OnEvent::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kompiliert die Event-Handler
|
||||
*
|
||||
* @param array $attributeData Array mit Attributdaten aus dem Mapper
|
||||
* @return array Kompilierte Event-Handler
|
||||
*/
|
||||
public function compile(array $attributeData): array
|
||||
{
|
||||
// Sortieren nach Priorität (höhere Werte zuerst)
|
||||
usort($attributeData, function ($a, $b) {
|
||||
$priorityA = $a['attribute']->priority ?? 0;
|
||||
$priorityB = $b['attribute']->priority ?? 0;
|
||||
|
||||
return $priorityB <=> $priorityA;
|
||||
});
|
||||
|
||||
// Weitere Kompilierung wenn nötig (z.B. Validierung)
|
||||
|
||||
return $attributeData;
|
||||
}
|
||||
}
|
||||
80
src/Framework/Core/Events/EventHandlerMapper.php
Normal file
80
src/Framework/Core/Events/EventHandlerMapper.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
use App\Framework\Core\AttributeMapper;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Mapper für das OnEvent-Attribut
|
||||
*/
|
||||
final class EventHandlerMapper implements AttributeMapper
|
||||
{
|
||||
/**
|
||||
* Gibt die Attributklasse zurück, die dieser Mapper verarbeitet
|
||||
*/
|
||||
public function getAttributeClass(): string
|
||||
{
|
||||
return OnEvent::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementiert die map-Methode aus dem AttributeMapper-Interface
|
||||
*
|
||||
* @param object $reflectionTarget Das Reflektionsobjekt (ReflectionClass|ReflectionMethod)
|
||||
* @param object $attributeInstance Die Attributinstanz
|
||||
* @return array|null Die Attributdaten oder null, wenn nicht verarbeitet werden kann
|
||||
*/
|
||||
public function map(object $reflectionTarget, object $attributeInstance): ?array
|
||||
{
|
||||
if (!($reflectionTarget instanceof ReflectionMethod)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parameters = $reflectionTarget->getParameters();
|
||||
|
||||
// Event-Handler müssen mindestens einen Parameter haben (das Event)
|
||||
if (count($parameters) < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$eventType = $parameters[0]->getType();
|
||||
if (!$eventType || $eventType->isBuiltin()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$eventClassName = $eventType->getName();
|
||||
|
||||
return [
|
||||
'class' => $reflectionTarget->getDeclaringClass()->getName(),
|
||||
'method' => $reflectionTarget->getName(),
|
||||
'event_class' => $eventClassName,
|
||||
#'reflection' => $reflectionTarget,
|
||||
'attribute_data' => [
|
||||
'priority' => $attributeInstance->priority ?? 0,
|
||||
'stopPropagation' => $attributeInstance->stopPropagation ?? false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verarbeitet ein Event-Handler-Attribut
|
||||
*
|
||||
* @param array $attributeData Array mit Attributdaten aus der Discovery
|
||||
* @return array Verarbeitete Attributdaten
|
||||
*/
|
||||
public function process(array $attributeData): array
|
||||
{
|
||||
// Nach Priorität sortieren (höhere Werte zuerst)
|
||||
usort($attributeData, function ($a, $b) {
|
||||
$priorityA = $a['attribute']->priority ?? 0;
|
||||
$priorityB = $b['attribute']->priority ?? 0;
|
||||
|
||||
return $priorityB <=> $priorityA;
|
||||
});
|
||||
|
||||
return $attributeData;
|
||||
}
|
||||
}
|
||||
24
src/Framework/Core/Events/OnEvent.php
Normal file
24
src/Framework/Core/Events/OnEvent.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
/**
|
||||
* Attribut, das eine Methode als Event-Handler kennzeichnet.
|
||||
*
|
||||
* Beispiel:
|
||||
* #[OnEvent]
|
||||
* public function handleUserRegistered(UserRegistered $event): void { ... }
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD)]
|
||||
final class OnEvent
|
||||
{
|
||||
/**
|
||||
* @param int|null $priority Priorität des Handlers (höhere Werte werden zuerst ausgeführt)
|
||||
* @param bool $stopPropagation Gibt an, ob die Event-Propagation nach diesem Handler gestoppt werden soll
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly ?int $priority = null,
|
||||
public readonly bool $stopPropagation = false
|
||||
) {}
|
||||
}
|
||||
18
src/Framework/Core/Events/UserRegistered.php
Normal file
18
src/Framework/Core/Events/UserRegistered.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core\Events;
|
||||
|
||||
/**
|
||||
* Event, das nach der Registrierung eines Benutzers ausgelöst wird
|
||||
*/
|
||||
final readonly class UserRegistered
|
||||
{
|
||||
public function __construct(
|
||||
public string $userId,
|
||||
public string $email,
|
||||
public string $username,
|
||||
public \DateTimeImmutable $registeredAt = new \DateTimeImmutable(),
|
||||
public \DateTimeImmutable $occurredAt = new \DateTimeImmutable()
|
||||
) {}
|
||||
}
|
||||
Reference in New Issue
Block a user