eventDispatcher = EventDispatcher::getInstance(); $this->cache = Cache::getInstance(); $this->logger = Logger::getInstance(); $this->loadPlugins(); } /** * Singleton-Instanz abrufen */ public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Plugins laden */ private function loadPlugins() { $pluginsDir = __DIR__ . '/../../../plugins/'; if (!is_dir($pluginsDir)) { mkdir($pluginsDir, 0755, true); return; } $pluginDirs = scandir($pluginsDir); foreach ($pluginDirs as $dir) { if ($dir !== '.' && $dir !== '..' && is_dir($pluginsDir . $dir)) { $configFile = $pluginsDir . $dir . '/plugin.json'; if (file_exists($configFile)) { $config = json_decode(file_get_contents($configFile), true); if ($config) { $this->plugins[$dir] = array_merge($config, [ 'directory' => $dir, 'path' => $pluginsDir . $dir, 'active' => $config['active'] ?? false, 'version' => $config['version'] ?? '1.0.0', 'dependencies' => $config['dependencies'] ?? [], 'hooks' => $config['hooks'] ?? [], 'settings' => $config['settings'] ?? [] ]); } } } } } /** * Plugin registrieren */ public function registerPlugin($name, $config) { $plugin = array_merge($config, [ 'name' => $name, 'active' => $config['active'] ?? false, 'version' => $config['version'] ?? '1.0.0', 'dependencies' => $config['dependencies'] ?? [], 'hooks' => $config['hooks'] ?? [], 'settings' => $config['settings'] ?? [] ]); $this->plugins[$name] = $plugin; // Plugin in Datenbank speichern $this->savePluginToDatabase($name, $plugin); // Event auslösen $this->eventDispatcher->dispatch('plugin.register', [ 'plugin_name' => $name, 'plugin_config' => $plugin ]); $this->logger->info('Plugin registriert', [ 'plugin_name' => $name, 'version' => $plugin['version'] ]); return $this; } /** * Plugin aktivieren */ public function activatePlugin($name) { if (!isset($this->plugins[$name])) { return false; } $plugin = $this->plugins[$name]; // Dependencies prüfen if (!$this->checkDependencies($plugin['dependencies'])) { $this->logger->error('Plugin-Aktivierung fehlgeschlagen - Dependencies nicht erfüllt', [ 'plugin_name' => $name, 'dependencies' => $plugin['dependencies'] ]); return false; } // Plugin-Klasse laden $pluginClass = $this->loadPluginClass($name); if (!$pluginClass) { return false; } try { // Plugin initialisieren $instance = new $pluginClass(); if (method_exists($instance, 'activate')) { $instance->activate(); } // Hooks registrieren $this->registerPluginHooks($name, $plugin['hooks']); // Plugin als aktiv markieren $this->plugins[$name]['active'] = true; $this->plugins[$name]['instance'] = $instance; // Datenbank aktualisieren $this->updatePluginStatus($name, true); // Event auslösen $this->eventDispatcher->dispatch('plugin.activate', [ 'plugin_name' => $name, 'plugin_config' => $plugin ]); $this->logger->info('Plugin aktiviert', [ 'plugin_name' => $name, 'version' => $plugin['version'] ]); return true; } catch (\Exception $e) { $this->logger->error('Plugin-Aktivierung Fehler', [ 'plugin_name' => $name, 'error' => $e->getMessage() ]); return false; } } /** * Plugin deaktivieren */ public function deactivatePlugin($name) { if (!isset($this->plugins[$name]) || !$this->plugins[$name]['active']) { return false; } $plugin = $this->plugins[$name]; try { // Plugin-Instance deaktivieren if (isset($plugin['instance']) && method_exists($plugin['instance'], 'deactivate')) { $plugin['instance']->deactivate(); } // Hooks deregistrieren $this->unregisterPluginHooks($name); // Plugin als inaktiv markieren $this->plugins[$name]['active'] = false; unset($this->plugins[$name]['instance']); // Datenbank aktualisieren $this->updatePluginStatus($name, false); // Event auslösen $this->eventDispatcher->dispatch('plugin.deactivate', [ 'plugin_name' => $name, 'plugin_config' => $plugin ]); $this->logger->info('Plugin deaktiviert', [ 'plugin_name' => $name, 'version' => $plugin['version'] ]); return true; } catch (\Exception $e) { $this->logger->error('Plugin-Deaktivierung Fehler', [ 'plugin_name' => $name, 'error' => $e->getMessage() ]); return false; } } /** * Plugin löschen */ public function deletePlugin($name) { if (!isset($this->plugins[$name])) { return false; } // Plugin deaktivieren falls aktiv if ($this->plugins[$name]['active']) { $this->deactivatePlugin($name); } try { // Plugin-Verzeichnis löschen $pluginPath = $this->plugins[$name]['path']; if (is_dir($pluginPath)) { $this->removeDirectory($pluginPath); } // Plugin aus Array entfernen unset($this->plugins[$name]); // Plugin aus Datenbank entfernen $this->deletePluginFromDatabase($name); // Event auslösen $this->eventDispatcher->dispatch('plugin.delete', [ 'plugin_name' => $name ]); $this->logger->info('Plugin gelöscht', [ 'plugin_name' => $name ]); return true; } catch (\Exception $e) { $this->logger->error('Plugin-Löschung Fehler', [ 'plugin_name' => $name, 'error' => $e->getMessage() ]); return false; } } /** * Plugin-Update */ public function updatePlugin($name, $newVersion) { if (!isset($this->plugins[$name])) { return false; } $plugin = $this->plugins[$name]; $wasActive = $plugin['active']; try { // Plugin deaktivieren falls aktiv if ($wasActive) { $this->deactivatePlugin($name); } // Update-Logic hier implementieren // (Download, Backup, Install, etc.) // Plugin-Konfiguration aktualisieren $this->plugins[$name]['version'] = $newVersion; $this->plugins[$name]['updated_at'] = date('Y-m-d H:i:s'); // Datenbank aktualisieren $this->updatePluginVersion($name, $newVersion); // Plugin wieder aktivieren falls es vorher aktiv war if ($wasActive) { $this->activatePlugin($name); } // Event auslösen $this->eventDispatcher->dispatch('plugin.update', [ 'plugin_name' => $name, 'old_version' => $plugin['version'], 'new_version' => $newVersion ]); $this->logger->info('Plugin aktualisiert', [ 'plugin_name' => $name, 'old_version' => $plugin['version'], 'new_version' => $newVersion ]); return true; } catch (\Exception $e) { $this->logger->error('Plugin-Update Fehler', [ 'plugin_name' => $name, 'error' => $e->getMessage() ]); return false; } } /** * Plugin-Klasse laden */ private function loadPluginClass($name) { $pluginPath = $this->plugins[$name]['path']; $mainFile = $pluginPath . '/Plugin.php'; if (!file_exists($mainFile)) { return false; } require_once $mainFile; $className = ucfirst($name) . 'Plugin'; if (!class_exists($className)) { return false; } return $className; } /** * Dependencies prüfen */ private function checkDependencies($dependencies) { foreach ($dependencies as $dependency) { if (is_string($dependency)) { // Plugin-Dependency if (!isset($this->plugins[$dependency]) || !$this->plugins[$dependency]['active']) { return false; } } elseif (is_array($dependency)) { // Erweiterte Dependency-Prüfung $type = $dependency['type'] ?? 'plugin'; $name = $dependency['name'] ?? ''; $version = $dependency['version'] ?? ''; switch ($type) { case 'plugin': if (!isset($this->plugins[$name]) || !$this->plugins[$name]['active']) { return false; } if ($version && version_compare($this->plugins[$name]['version'], $version, '<')) { return false; } break; case 'php': if (version_compare(PHP_VERSION, $version, '<')) { return false; } break; case 'extension': if (!extension_loaded($name)) { return false; } break; } } } return true; } /** * Plugin-Hooks registrieren */ private function registerPluginHooks($pluginName, $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 ($pluginName, $callback) { return $this->executePluginCallback($pluginName, $callback, $event); }, $priority, $pluginName); } } } /** * Plugin-Hooks deregistrieren */ private function unregisterPluginHooks($pluginName) { // Hooks für dieses Plugin entfernen // (Implementierung hängt vom Event-System ab) } /** * Plugin-Callback ausführen */ private function executePluginCallback($pluginName, $callback, $event) { if (!isset($this->plugins[$pluginName]['instance'])) { return $event; } $instance = $this->plugins[$pluginName]['instance']; if (method_exists($instance, $callback)) { try { return $instance->$callback($event); } catch (\Exception $e) { $this->logger->error('Plugin-Callback Fehler', [ 'plugin_name' => $pluginName, 'callback' => $callback, 'error' => $e->getMessage() ]); } } return $event; } /** * Plugin in Datenbank speichern */ private function savePluginToDatabase($name, $plugin) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' INSERT INTO ws_plugins ( plugin_name, plugin_config, version, dependencies, hooks, settings, active, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE plugin_config = ?, version = ?, dependencies = ?, hooks = ?, settings = ?, updated_at = NOW() '); $config = json_encode($plugin); $dependencies = json_encode($plugin['dependencies']); $hooks = json_encode($plugin['hooks']); $settings = json_encode($plugin['settings']); $stmt->execute([ $name, $config, $plugin['version'], $dependencies, $hooks, $settings, $plugin['active'] ? 1 : 0, $config, $plugin['version'], $dependencies, $hooks, $settings ]); } catch (Exception $e) { $this->logger->error('Plugin-Datenbank Fehler', [ 'error' => $e->getMessage() ]); } } /** * Plugin-Status in Datenbank aktualisieren */ private function updatePluginStatus($name, $active) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' UPDATE ws_plugins SET active = ?, updated_at = NOW() WHERE plugin_name = ? '); $stmt->execute([$active ? 1 : 0, $name]); } catch (Exception $e) { $this->logger->error('Plugin-Status Update Fehler', [ 'error' => $e->getMessage() ]); } } /** * Plugin-Version in Datenbank aktualisieren */ private function updatePluginVersion($name, $version) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' UPDATE ws_plugins SET version = ?, updated_at = NOW() WHERE plugin_name = ? '); $stmt->execute([$version, $name]); } catch (Exception $e) { $this->logger->error('Plugin-Version Update Fehler', [ 'error' => $e->getMessage() ]); } } /** * Plugin aus Datenbank löschen */ private function deletePluginFromDatabase($name) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' DELETE FROM ws_plugins WHERE plugin_name = ? '); $stmt->execute([$name]); } catch (Exception $e) { $this->logger->error('Plugin-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 Plugins abrufen */ public function getAllPlugins() { return $this->plugins; } /** * Aktive Plugins abrufen */ public function getActivePlugins() { return array_filter($this->plugins, function($plugin) { return $plugin['active']; }); } /** * Plugin abrufen */ public function getPlugin($name) { return isset($this->plugins[$name]) ? $this->plugins[$name] : null; } /** * Plugin-Statistiken abrufen */ public function getPluginStatistics() { $total = count($this->plugins); $active = count($this->getActivePlugins()); $inactive = $total - $active; $versions = []; foreach ($this->plugins as $plugin) { $version = $plugin['version']; $versions[$version] = ($versions[$version] ?? 0) + 1; } return [ 'total' => $total, 'active' => $active, 'inactive' => $inactive, 'versions' => $versions ]; } /** * Plugin-System aktivieren/deaktivieren */ public function setEnabled($enabled) { $this->enabled = $enabled; return $this; } /** * Plugin-System Status prüfen */ public function isEnabled() { return $this->enabled; } } /** * Basis-Plugin-Klasse */ abstract class BasePlugin { protected $name; protected $version; protected $description; protected $author; protected $config; public function __construct() { $this->loadConfig(); } /** * Plugin aktivieren */ abstract public function activate(); /** * Plugin deaktivieren */ abstract public function deactivate(); /** * Plugin-Konfiguration laden */ protected function loadConfig() { $configFile = dirname((new \ReflectionClass($this))->getFileName()) . '/plugin.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'] ?? ''; } } /** * Plugin-Konfiguration abrufen */ public function getConfig($key = null) { if ($key === null) { return $this->config; } return $this->config[$key] ?? null; } /** * Plugin-Konfiguration setzen */ public function setConfig($key, $value) { $this->config[$key] = $value; return $this; } /** * Plugin-Name abrufen */ public function getName() { return $this->name; } /** * Plugin-Version abrufen */ public function getVersion() { return $this->version; } /** * Plugin-Beschreibung abrufen */ public function getDescription() { return $this->description; } /** * Plugin-Author abrufen */ public function getAuthor() { return $this->author; } }