eventDispatcher = EventDispatcher::getInstance(); $this->cache = Cache::getInstance(); $this->logger = Logger::getInstance(); $this->loadExtensions(); } /** * Singleton-Instanz abrufen */ public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Extensions laden */ private function loadExtensions() { $extensionsDir = __DIR__ . '/../../../extensions/'; if (!is_dir($extensionsDir)) { mkdir($extensionsDir, 0755, true); return; } $extensionDirs = scandir($extensionsDir); foreach ($extensionDirs as $dir) { if ($dir !== '.' && $dir !== '..' && is_dir($extensionsDir . $dir)) { $configFile = $extensionsDir . $dir . '/extension.json'; if (file_exists($configFile)) { $config = json_decode(file_get_contents($configFile), true); if ($config) { $this->extensions[$dir] = array_merge($config, [ 'directory' => $dir, 'path' => $extensionsDir . $dir, 'active' => $config['active'] ?? false, 'version' => $config['version'] ?? '1.0.0', 'dependencies' => $config['dependencies'] ?? [], 'hooks' => $config['hooks'] ?? [], 'settings' => $config['settings'] ?? [], 'type' => $config['type'] ?? 'general' ]); } } } } } /** * Extension registrieren */ public function registerExtension($name, $config) { $extension = array_merge($config, [ 'name' => $name, 'active' => $config['active'] ?? false, 'version' => $config['version'] ?? '1.0.0', 'dependencies' => $config['dependencies'] ?? [], 'hooks' => $config['hooks'] ?? [], 'settings' => $config['settings'] ?? [], 'type' => $config['type'] ?? 'general' ]); $this->extensions[$name] = $extension; // Extension in Datenbank speichern $this->saveExtensionToDatabase($name, $extension); // Event auslösen $this->eventDispatcher->dispatch('extension.register', [ 'extension_name' => $name, 'extension_config' => $extension ]); $this->logger->info('Extension registriert', [ 'extension_name' => $name, 'version' => $extension['version'], 'type' => $extension['type'] ]); return $this; } /** * Extension aktivieren */ public function activateExtension($name) { if (!isset($this->extensions[$name])) { return false; } $extension = $this->extensions[$name]; // Dependencies prüfen if (!$this->checkDependencies($extension['dependencies'])) { $this->logger->error('Extension-Aktivierung fehlgeschlagen - Dependencies nicht erfüllt', [ 'extension_name' => $name, 'dependencies' => $extension['dependencies'] ]); return false; } // Extension-Klasse laden $extensionClass = $this->loadExtensionClass($name); if (!$extensionClass) { return false; } try { // Extension initialisieren $instance = new $extensionClass(); if (method_exists($instance, 'activate')) { $instance->activate(); } // Hooks registrieren $this->registerExtensionHooks($name, $extension['hooks']); // Extension als aktiv markieren $this->extensions[$name]['active'] = true; $this->extensions[$name]['instance'] = $instance; // Datenbank aktualisieren $this->updateExtensionStatus($name, true); // Event auslösen $this->eventDispatcher->dispatch('extension.activate', [ 'extension_name' => $name, 'extension_config' => $extension ]); $this->logger->info('Extension aktiviert', [ 'extension_name' => $name, 'version' => $extension['version'], 'type' => $extension['type'] ]); return true; } catch (\Exception $e) { $this->logger->error('Extension-Aktivierung Fehler', [ 'extension_name' => $name, 'error' => $e->getMessage() ]); return false; } } /** * Extension deaktivieren */ public function deactivateExtension($name) { if (!isset($this->extensions[$name]) || !$this->extensions[$name]['active']) { return false; } $extension = $this->extensions[$name]; try { // Extension-Instance deaktivieren if (isset($extension['instance']) && method_exists($extension['instance'], 'deactivate')) { $extension['instance']->deactivate(); } // Hooks deregistrieren $this->unregisterExtensionHooks($name); // Extension als inaktiv markieren $this->extensions[$name]['active'] = false; unset($this->extensions[$name]['instance']); // Datenbank aktualisieren $this->updateExtensionStatus($name, false); // Event auslösen $this->eventDispatcher->dispatch('extension.deactivate', [ 'extension_name' => $name, 'extension_config' => $extension ]); $this->logger->info('Extension deaktiviert', [ 'extension_name' => $name, 'version' => $extension['version'], 'type' => $extension['type'] ]); return true; } catch (\Exception $e) { $this->logger->error('Extension-Deaktivierung Fehler', [ 'extension_name' => $name, 'error' => $e->getMessage() ]); return false; } } /** * Extension löschen */ public function deleteExtension($name) { if (!isset($this->extensions[$name])) { return false; } // Extension deaktivieren falls aktiv if ($this->extensions[$name]['active']) { $this->deactivateExtension($name); } try { // Extension-Verzeichnis löschen $extensionPath = $this->extensions[$name]['path']; if (is_dir($extensionPath)) { $this->removeDirectory($extensionPath); } // Extension aus Array entfernen unset($this->extensions[$name]); // Extension aus Datenbank entfernen $this->deleteExtensionFromDatabase($name); // Event auslösen $this->eventDispatcher->dispatch('extension.delete', [ 'extension_name' => $name ]); $this->logger->info('Extension gelöscht', [ 'extension_name' => $name ]); return true; } catch (\Exception $e) { $this->logger->error('Extension-Löschung Fehler', [ 'extension_name' => $name, 'error' => $e->getMessage() ]); return false; } } /** * Extension-Update */ public function updateExtension($name, $newVersion) { if (!isset($this->extensions[$name])) { return false; } $extension = $this->extensions[$name]; $wasActive = $extension['active']; try { // Extension deaktivieren falls aktiv if ($wasActive) { $this->deactivateExtension($name); } // Update-Logic hier implementieren // (Download, Backup, Install, etc.) // Extension-Konfiguration aktualisieren $this->extensions[$name]['version'] = $newVersion; $this->extensions[$name]['updated_at'] = date('Y-m-d H:i:s'); // Datenbank aktualisieren $this->updateExtensionVersion($name, $newVersion); // Extension wieder aktivieren falls es vorher aktiv war if ($wasActive) { $this->activateExtension($name); } // Event auslösen $this->eventDispatcher->dispatch('extension.update', [ 'extension_name' => $name, 'old_version' => $extension['version'], 'new_version' => $newVersion ]); $this->logger->info('Extension aktualisiert', [ 'extension_name' => $name, 'old_version' => $extension['version'], 'new_version' => $newVersion ]); return true; } catch (\Exception $e) { $this->logger->error('Extension-Update Fehler', [ 'extension_name' => $name, 'error' => $e->getMessage() ]); return false; } } /** * Extension-Klasse laden */ private function loadExtensionClass($name) { $extensionPath = $this->extensions[$name]['path']; $mainFile = $extensionPath . '/Extension.php'; if (!file_exists($mainFile)) { return false; } require_once $mainFile; $className = ucfirst($name) . 'Extension'; if (!class_exists($className)) { return false; } return $className; } /** * Dependencies prüfen */ private function checkDependencies($dependencies) { foreach ($dependencies as $dependency) { if (is_string($dependency)) { // Extension-Dependency if (!isset($this->extensions[$dependency]) || !$this->extensions[$dependency]['active']) { return false; } } elseif (is_array($dependency)) { // Erweiterte Dependency-Prüfung $type = $dependency['type'] ?? 'extension'; $name = $dependency['name'] ?? ''; $version = $dependency['version'] ?? ''; switch ($type) { case 'extension': if (!isset($this->extensions[$name]) || !$this->extensions[$name]['active']) { return false; } if ($version && version_compare($this->extensions[$name]['version'], $version, '<')) { return false; } break; case 'plugin': // Plugin-Dependency prüfen $pluginManager = Plugin::getInstance(); $plugin = $pluginManager->getPlugin($name); if (!$plugin || !$plugin['active']) { return false; } if ($version && version_compare($plugin['version'], $version, '<')) { return false; } break; case 'php': if (version_compare(PHP_VERSION, $version, '<')) { return false; } break; case 'extension_php': if (!extension_loaded($name)) { return false; } break; } } } return true; } /** * Extension-Hooks registrieren */ private function registerExtensionHooks($extensionName, $hooks) { foreach ($hooks as $hook) { $hookName = $hook['name'] ?? ''; $callback = $hook['callback'] ?? ''; $priority = $hook['priority'] ?? 10; if ($hookName && $callback) { $this->eventDispatcher->addListener($hookName, function($event) use ($extensionName, $callback) { return $this->executeExtensionCallback($extensionName, $callback, $event); }, $priority, $extensionName); } } } /** * Extension-Hooks deregistrieren */ private function unregisterExtensionHooks($extensionName) { // Hooks für diese Extension entfernen // (Implementierung hängt vom Event-System ab) } /** * Extension-Callback ausführen */ private function executeExtensionCallback($extensionName, $callback, $event) { if (!isset($this->extensions[$extensionName]['instance'])) { return $event; } $instance = $this->extensions[$extensionName]['instance']; if (method_exists($instance, $callback)) { try { return $instance->$callback($event); } catch (\Exception $e) { $this->logger->error('Extension-Callback Fehler', [ 'extension_name' => $extensionName, 'callback' => $callback, 'error' => $e->getMessage() ]); } } return $event; } /** * Extension in Datenbank speichern */ private function saveExtensionToDatabase($name, $extension) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' INSERT INTO ws_extensions ( extension_name, extension_config, version, type, dependencies, hooks, settings, active, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE extension_config = ?, version = ?, type = ?, dependencies = ?, hooks = ?, settings = ?, updated_at = NOW() '); $config = json_encode($extension); $dependencies = json_encode($extension['dependencies']); $hooks = json_encode($extension['hooks']); $settings = json_encode($extension['settings']); $stmt->execute([ $name, $config, $extension['version'], $extension['type'], $dependencies, $hooks, $settings, $extension['active'] ? 1 : 0, $config, $extension['version'], $extension['type'], $dependencies, $hooks, $settings ]); } catch (Exception $e) { $this->logger->error('Extension-Datenbank Fehler', [ 'error' => $e->getMessage() ]); } } /** * Extension-Status in Datenbank aktualisieren */ private function updateExtensionStatus($name, $active) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' UPDATE ws_extensions SET active = ?, updated_at = NOW() WHERE extension_name = ? '); $stmt->execute([$active ? 1 : 0, $name]); } catch (Exception $e) { $this->logger->error('Extension-Status Update Fehler', [ 'error' => $e->getMessage() ]); } } /** * Extension-Version in Datenbank aktualisieren */ private function updateExtensionVersion($name, $version) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' UPDATE ws_extensions SET version = ?, updated_at = NOW() WHERE extension_name = ? '); $stmt->execute([$version, $name]); } catch (Exception $e) { $this->logger->error('Extension-Version Update Fehler', [ 'error' => $e->getMessage() ]); } } /** * Extension aus Datenbank löschen */ private function deleteExtensionFromDatabase($name) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' DELETE FROM ws_extensions WHERE extension_name = ? '); $stmt->execute([$name]); } catch (Exception $e) { $this->logger->error('Extension-Datenbank Löschung Fehler', [ 'error' => $e->getMessage() ]); } } /** * Verzeichnis rekursiv löschen */ private function removeDirectory($dir) { if (!is_dir($dir)) { return false; } $files = array_diff(scandir($dir), ['.', '..']); foreach ($files as $file) { $path = $dir . '/' . $file; if (is_dir($path)) { $this->removeDirectory($path); } else { unlink($path); } } return rmdir($dir); } /** * Alle Extensions abrufen */ public function getAllExtensions() { return $this->extensions; } /** * Aktive Extensions abrufen */ public function getActiveExtensions() { return array_filter($this->extensions, function($extension) { return $extension['active']; }); } /** * Extensions nach Typ abrufen */ public function getExtensionsByType($type) { return array_filter($this->extensions, function($extension) use ($type) { return $extension['type'] === $type; }); } /** * Extension abrufen */ public function getExtension($name) { return isset($this->extensions[$name]) ? $this->extensions[$name] : null; } /** * Extension-Statistiken abrufen */ public function getExtensionStatistics() { $total = count($this->extensions); $active = count($this->getActiveExtensions()); $inactive = $total - $active; $types = []; $versions = []; foreach ($this->extensions as $extension) { $type = $extension['type']; $version = $extension['version']; $types[$type] = ($types[$type] ?? 0) + 1; $versions[$version] = ($versions[$version] ?? 0) + 1; } return [ 'total' => $total, 'active' => $active, 'inactive' => $inactive, 'types' => $types, 'versions' => $versions ]; } /** * Extension-System aktivieren/deaktivieren */ public function setEnabled($enabled) { $this->enabled = $enabled; return $this; } /** * Extension-System Status prüfen */ public function isEnabled() { return $this->enabled; } } /** * Basis-Extension-Klasse */ abstract class BaseExtension { protected $name; protected $version; protected $description; protected $author; protected $type; protected $config; public function __construct() { $this->loadConfig(); } /** * Extension aktivieren */ abstract public function activate(); /** * Extension deaktivieren */ abstract public function deactivate(); /** * Extension-Konfiguration laden */ protected function loadConfig() { $configFile = dirname((new \ReflectionClass($this))->getFileName()) . '/extension.json'; if (file_exists($configFile)) { $this->config = json_decode(file_get_contents($configFile), true); $this->name = $this->config['name'] ?? ''; $this->version = $this->config['version'] ?? '1.0.0'; $this->description = $this->config['description'] ?? ''; $this->author = $this->config['author'] ?? ''; $this->type = $this->config['type'] ?? 'general'; } } /** * Extension-Konfiguration abrufen */ public function getConfig($key = null) { if ($key === null) { return $this->config; } return $this->config[$key] ?? null; } /** * Extension-Konfiguration setzen */ public function setConfig($key, $value) { $this->config[$key] = $value; return $this; } /** * Extension-Name abrufen */ public function getName() { return $this->name; } /** * Extension-Version abrufen */ public function getVersion() { return $this->version; } /** * Extension-Beschreibung abrufen */ public function getDescription() { return $this->description; } /** * Extension-Author abrufen */ public function getAuthor() { return $this->author; } /** * Extension-Typ abrufen */ public function getType() { return $this->type; } }