chore: complete update

This commit is contained in:
2025-07-17 16:24:20 +02:00
parent 899227b0a4
commit 64a7051137
1300 changed files with 85570 additions and 2756 deletions

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Framework\Core\Events;
class AfterEmitResponse
{
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Framework\Core\Events;
class AfterHandleRequest
{
}

View 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()
) {}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Framework\Core\Events;
class BeforeEmitResponse
{
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Framework\Core\Events;
class BeforeHandleRequest
{
}

View 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()
) {}
}

View 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'));
}
});
}
}

View 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));
}
}

View 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));
}
}

View 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;
}
}

View 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;
}
}

View 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
) {}
}

View 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()
) {}
}