Newwebshop/app/Core/EventDispatcher.php

643 lines
20 KiB
PHP

<?php
/**
* Copyright seit 2024 Webshop System
*
* Event-System für PrestaShop-Modul-Kompatibilität
*
* @author Webshop System
* @license GPL v3
*/
namespace App\Core;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception;
class EventDispatcher
{
private static $instance = null;
private $listeners = [];
private $events = [];
private $statistics = [];
private $cache = [];
private $enabled = true;
private function __construct()
{
$this->initializeDefaultEvents();
}
/**
* Singleton-Instanz abrufen
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Standard-Events initialisieren
*/
private function initializeDefaultEvents()
{
// System-Events
$this->registerEvent('system.boot', 'System wird gestartet');
$this->registerEvent('system.shutdown', 'System wird beendet');
$this->registerEvent('system.error', 'System-Fehler aufgetreten');
// User-Events
$this->registerEvent('user.login', 'Benutzer angemeldet');
$this->registerEvent('user.logout', 'Benutzer abgemeldet');
$this->registerEvent('user.register', 'Benutzer registriert');
$this->registerEvent('user.update', 'Benutzer aktualisiert');
$this->registerEvent('user.delete', 'Benutzer gelöscht');
// Product-Events
$this->registerEvent('product.create', 'Produkt erstellt');
$this->registerEvent('product.update', 'Produkt aktualisiert');
$this->registerEvent('product.delete', 'Produkt gelöscht');
$this->registerEvent('product.view', 'Produkt angesehen');
$this->registerEvent('product.add_to_cart', 'Produkt zum Warenkorb hinzugefügt');
// Order-Events
$this->registerEvent('order.create', 'Bestellung erstellt');
$this->registerEvent('order.update', 'Bestellung aktualisiert');
$this->registerEvent('order.delete', 'Bestellung gelöscht');
$this->registerEvent('order.paid', 'Bestellung bezahlt');
$this->registerEvent('order.shipped', 'Bestellung versendet');
$this->registerEvent('order.delivered', 'Bestellung geliefert');
$this->registerEvent('order.cancelled', 'Bestellung storniert');
// Cart-Events
$this->registerEvent('cart.add', 'Artikel zum Warenkorb hinzugefügt');
$this->registerEvent('cart.remove', 'Artikel aus Warenkorb entfernt');
$this->registerEvent('cart.update', 'Warenkorb aktualisiert');
$this->registerEvent('cart.clear', 'Warenkorb geleert');
// Payment-Events
$this->registerEvent('payment.process', 'Zahlung verarbeitet');
$this->registerEvent('payment.success', 'Zahlung erfolgreich');
$this->registerEvent('payment.failed', 'Zahlung fehlgeschlagen');
$this->registerEvent('payment.refund', 'Zahlung erstattet');
// Module-Events
$this->registerEvent('module.install', 'Modul installiert');
$this->registerEvent('module.uninstall', 'Modul deinstalliert');
$this->registerEvent('module.enable', 'Modul aktiviert');
$this->registerEvent('module.disable', 'Modul deaktiviert');
$this->registerEvent('module.update', 'Modul aktualisiert');
// Hook-Events
$this->registerEvent('hook.register', 'Hook registriert');
$this->registerEvent('hook.unregister', 'Hook deregistriert');
$this->registerEvent('hook.execute', 'Hook ausgeführt');
// Override-Events
$this->registerEvent('override.create', 'Override erstellt');
$this->registerEvent('override.update', 'Override aktualisiert');
$this->registerEvent('override.delete', 'Override gelöscht');
$this->registerEvent('override.enable', 'Override aktiviert');
$this->registerEvent('override.disable', 'Override deaktiviert');
// Cache-Events
$this->registerEvent('cache.clear', 'Cache geleert');
$this->registerEvent('cache.warm', 'Cache aufgewärmt');
$this->registerEvent('cache.invalidate', 'Cache invalidiert');
// Security-Events
$this->registerEvent('security.login_attempt', 'Anmeldeversuch');
$this->registerEvent('security.login_failed', 'Anmeldung fehlgeschlagen');
$this->registerEvent('security.logout', 'Abmeldung');
$this->registerEvent('security.permission_denied', 'Zugriff verweigert');
// Performance-Events
$this->registerEvent('performance.slow_query', 'Langsame Datenbankabfrage');
$this->registerEvent('performance.memory_high', 'Hoher Speicherverbrauch');
$this->registerEvent('performance.cpu_high', 'Hohe CPU-Last');
// Notification-Events
$this->registerEvent('notification.email_sent', 'E-Mail gesendet');
$this->registerEvent('notification.sms_sent', 'SMS gesendet');
$this->registerEvent('notification.push_sent', 'Push-Benachrichtigung gesendet');
}
/**
* Event registrieren
*/
public function registerEvent($eventName, $description = '')
{
$this->events[$eventName] = [
'name' => $eventName,
'description' => $description,
'listeners' => [],
'statistics' => [
'executions' => 0,
'total_time' => 0,
'avg_time' => 0,
'last_execution' => null
]
];
return $this;
}
/**
* Event-Listener registrieren
*/
public function addListener($eventName, $listener, $priority = 0, $moduleName = null)
{
if (!isset($this->events[$eventName])) {
$this->registerEvent($eventName);
}
$listenerId = $this->generateListenerId($listener, $moduleName);
$this->events[$eventName]['listeners'][$listenerId] = [
'listener' => $listener,
'priority' => $priority,
'module_name' => $moduleName,
'active' => true,
'executions' => 0,
'total_time' => 0,
'avg_time' => 0,
'last_execution' => null
];
// Nach Priorität sortieren (höhere Priorität zuerst)
uasort($this->events[$eventName]['listeners'], function($a, $b) {
return $b['priority'] - $a['priority'];
});
// Event in Datenbank speichern
$this->saveEventToDatabase($eventName, $listenerId, $listener, $priority, $moduleName);
return $this;
}
/**
* Event-Listener entfernen
*/
public function removeListener($eventName, $listener, $moduleName = null)
{
if (!isset($this->events[$eventName])) {
return $this;
}
$listenerId = $this->generateListenerId($listener, $moduleName);
if (isset($this->events[$eventName]['listeners'][$listenerId])) {
unset($this->events[$eventName]['listeners'][$listenerId]);
// Event aus Datenbank entfernen
$this->removeEventFromDatabase($eventName, $listenerId);
}
return $this;
}
/**
* Event ausführen
*/
public function dispatch($eventName, $event = null)
{
if (!$this->enabled) {
return $event;
}
if (!isset($this->events[$eventName])) {
$this->registerEvent($eventName);
}
$startTime = microtime(true);
$executedListeners = 0;
// Event-Objekt erstellen falls nicht vorhanden
if ($event === null) {
$event = new Event($eventName);
}
// Alle Listener ausführen
foreach ($this->events[$eventName]['listeners'] as $listenerId => $listenerData) {
if (!$listenerData['active']) {
continue;
}
try {
$listenerStartTime = microtime(true);
// Listener ausführen
$result = call_user_func($listenerData['listener'], $event, $eventName, $this);
$listenerEndTime = microtime(true);
$executionTime = ($listenerEndTime - $listenerStartTime) * 1000; // in ms
// Statistiken aktualisieren
$this->updateListenerStatistics($eventName, $listenerId, $executionTime);
$executedListeners++;
// Event abbrechen falls Listener es verlangt
if ($event->isPropagationStopped()) {
break;
}
} catch (\Exception $e) {
// Fehler loggen aber weitermachen
$this->logEventError($eventName, $listenerId, $e);
}
}
$endTime = microtime(true);
$totalTime = ($endTime - $startTime) * 1000; // in ms
// Event-Statistiken aktualisieren
$this->updateEventStatistics($eventName, $totalTime, $executedListeners);
// Event in Datenbank loggen
$this->logEventExecution($eventName, $totalTime, $executedListeners);
return $event;
}
/**
* Event-Listener aktivieren/deaktivieren
*/
public function setListenerActive($eventName, $listener, $moduleName, $active)
{
if (!isset($this->events[$eventName])) {
return $this;
}
$listenerId = $this->generateListenerId($listener, $moduleName);
if (isset($this->events[$eventName]['listeners'][$listenerId])) {
$this->events[$eventName]['listeners'][$listenerId]['active'] = $active;
// Status in Datenbank aktualisieren
$this->updateListenerStatus($eventName, $listenerId, $active);
}
return $this;
}
/**
* Event-System aktivieren/deaktivieren
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
return $this;
}
/**
* Event-System Status abrufen
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* Event-Statistiken abrufen
*/
public function getEventStatistics($eventName = null)
{
if ($eventName) {
return isset($this->events[$eventName]) ? $this->events[$eventName]['statistics'] : null;
}
$statistics = [];
foreach ($this->events as $name => $event) {
$statistics[$name] = $event['statistics'];
}
return $statistics;
}
/**
* Listener-Statistiken abrufen
*/
public function getListenerStatistics($eventName)
{
if (!isset($this->events[$eventName])) {
return [];
}
$statistics = [];
foreach ($this->events[$eventName]['listeners'] as $listenerId => $listenerData) {
$statistics[$listenerId] = [
'module_name' => $listenerData['module_name'],
'priority' => $listenerData['priority'],
'active' => $listenerData['active'],
'executions' => $listenerData['executions'],
'total_time' => $listenerData['total_time'],
'avg_time' => $listenerData['avg_time'],
'last_execution' => $listenerData['last_execution']
];
}
return $statistics;
}
/**
* Alle Events abrufen
*/
public function getEvents()
{
return $this->events;
}
/**
* Event-Listener abrufen
*/
public function getListeners($eventName)
{
if (!isset($this->events[$eventName])) {
return [];
}
return $this->events[$eventName]['listeners'];
}
/**
* Event-System zurücksetzen
*/
public function reset()
{
$this->listeners = [];
$this->events = [];
$this->statistics = [];
$this->cache = [];
$this->initializeDefaultEvents();
return $this;
}
/**
* Listener-ID generieren
*/
private function generateListenerId($listener, $moduleName)
{
if (is_string($listener)) {
return $moduleName . '_' . $listener;
} elseif (is_array($listener)) {
return $moduleName . '_' . get_class($listener[0]) . '_' . $listener[1];
} else {
return $moduleName . '_' . spl_object_hash($listener);
}
}
/**
* Listener-Statistiken aktualisieren
*/
private function updateListenerStatistics($eventName, $listenerId, $executionTime)
{
if (!isset($this->events[$eventName]['listeners'][$listenerId])) {
return;
}
$listener = &$this->events[$eventName]['listeners'][$listenerId];
$listener['executions']++;
$listener['total_time'] += $executionTime;
$listener['avg_time'] = $listener['total_time'] / $listener['executions'];
$listener['last_execution'] = date('Y-m-d H:i:s');
}
/**
* Event-Statistiken aktualisieren
*/
private function updateEventStatistics($eventName, $totalTime, $executedListeners)
{
if (!isset($this->events[$eventName])) {
return;
}
$statistics = &$this->events[$eventName]['statistics'];
$statistics['executions']++;
$statistics['total_time'] += $totalTime;
$statistics['avg_time'] = $statistics['total_time'] / $statistics['executions'];
$statistics['last_execution'] = date('Y-m-d H:i:s');
}
/**
* Event-Fehler loggen
*/
private function logEventError($eventName, $listenerId, $exception)
{
$errorMessage = sprintf(
'Event-Fehler: %s, Listener: %s, Fehler: %s',
$eventName,
$listenerId,
$exception->getMessage()
);
error_log($errorMessage);
// Fehler in Datenbank loggen
$this->logEventErrorToDatabase($eventName, $listenerId, $exception);
}
/**
* Event in Datenbank speichern
*/
private function saveEventToDatabase($eventName, $listenerId, $listener, $priority, $moduleName)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
INSERT INTO ws_event_listeners (
event_name, listener_id, listener_data, priority,
module_name, active, created_at
) VALUES (?, ?, ?, ?, ?, 1, NOW())
ON DUPLICATE KEY UPDATE
listener_data = ?, priority = ?, active = 1, updated_at = NOW()
');
$listenerData = serialize($listener);
$stmt->execute([
$eventName,
$listenerId,
$listenerData,
$priority,
$moduleName,
$listenerData,
$priority
]);
} catch (Exception $e) {
error_log('Event-Datenbank Fehler: ' . $e->getMessage());
}
}
/**
* Event aus Datenbank entfernen
*/
private function removeEventFromDatabase($eventName, $listenerId)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
DELETE FROM ws_event_listeners
WHERE event_name = ? AND listener_id = ?
');
$stmt->execute([$eventName, $listenerId]);
} catch (Exception $e) {
error_log('Event-Entfernung Fehler: ' . $e->getMessage());
}
}
/**
* Listener-Status in Datenbank aktualisieren
*/
private function updateListenerStatus($eventName, $listenerId, $active)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
UPDATE ws_event_listeners
SET active = ?, updated_at = NOW()
WHERE event_name = ? AND listener_id = ?
');
$stmt->execute([$active ? 1 : 0, $eventName, $listenerId]);
} catch (Exception $e) {
error_log('Event-Status Fehler: ' . $e->getMessage());
}
}
/**
* Event-Ausführung in Datenbank loggen
*/
private function logEventExecution($eventName, $executionTime, $executedListeners)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
INSERT INTO ws_event_logs (
event_name, execution_time, executed_listeners,
created_at
) VALUES (?, ?, ?, NOW())
');
$stmt->execute([
$eventName,
$executionTime,
$executedListeners
]);
} catch (Exception $e) {
error_log('Event-Log Fehler: ' . $e->getMessage());
}
}
/**
* Event-Fehler in Datenbank loggen
*/
private function logEventErrorToDatabase($eventName, $listenerId, $exception)
{
try {
$conn = DriverManager::getConnection([
'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop'
]);
$stmt = $conn->prepare('
INSERT INTO ws_event_errors (
event_name, listener_id, error_message,
error_trace, created_at
) VALUES (?, ?, ?, ?, NOW())
');
$stmt->execute([
$eventName,
$listenerId,
$exception->getMessage(),
$exception->getTraceAsString()
]);
} catch (Exception $e) {
error_log('Event-Error-Log Fehler: ' . $e->getMessage());
}
}
}
/**
* Event-Klasse für Event-Objekte
*/
class Event
{
private $name;
private $data;
private $propagationStopped = false;
public function __construct($name, $data = [])
{
$this->name = $name;
$this->data = $data;
}
public function getName()
{
return $this->name;
}
public function getData()
{
return $this->data;
}
public function setData($data)
{
$this->data = $data;
return $this;
}
public function get($key, $default = null)
{
return isset($this->data[$key]) ? $this->data[$key] : $default;
}
public function set($key, $value)
{
$this->data[$key] = $value;
return $this;
}
public function has($key)
{
return isset($this->data[$key]);
}
public function remove($key)
{
unset($this->data[$key]);
return $this;
}
public function stopPropagation()
{
$this->propagationStopped = true;
return $this;
}
public function isPropagationStopped()
{
return $this->propagationStopped;
}
}