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