moduleManager = ModuleManager::getInstance(); $this->plugin = Plugin::getInstance(); $this->extension = Extension::getInstance(); $this->eventDispatcher = EventDispatcher::getInstance(); $this->cache = Cache::getInstance(); $this->logger = Logger::getInstance(); $this->loadDependencyGraph(); $this->loadConflictResolutions(); } /** * Singleton-Instanz abrufen */ public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Dependency-Graph laden */ private function loadDependencyGraph() { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' SELECT * FROM ws_dependencies WHERE active = 1 ORDER BY priority DESC '); $stmt->execute(); $dependencies = $stmt->fetchAllAssociative(); foreach ($dependencies as $dependency) { $this->dependencyGraph[$dependency['dependent_name']][] = [ 'type' => $dependency['dependency_type'], 'name' => $dependency['dependency_name'], 'version' => $dependency['dependency_version'], 'required' => (bool)$dependency['required'], 'priority' => $dependency['priority'] ]; } } catch (Exception $e) { $this->logger->error('Dependency-Graph laden Fehler', [ 'error' => $e->getMessage() ]); } } /** * Konflikt-Lösungen laden */ private function loadConflictResolutions() { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' SELECT * FROM ws_conflict_resolutions WHERE active = 1 '); $stmt->execute(); $resolutions = $stmt->fetchAllAssociative(); foreach ($resolutions as $resolution) { $this->conflictResolutions[] = [ 'conflict_type' => $resolution['conflict_type'], 'item1_name' => $resolution['item1_name'], 'item2_name' => $resolution['item2_name'], 'resolution_type' => $resolution['resolution_type'], 'resolution_action' => $resolution['resolution_action'], 'priority' => $resolution['priority'] ]; } } catch (Exception $e) { $this->logger->error('Konflikt-Lösungen laden Fehler', [ 'error' => $e->getMessage() ]); } } /** * Dependencies für Item prüfen */ public function checkDependencies($itemName, $itemType = 'module') { $dependencies = $this->getDependencies($itemName, $itemType); $results = []; foreach ($dependencies as $dependency) { $result = $this->checkSingleDependency($dependency); $results[] = $result; } return $results; } /** * Einzelne Dependency prüfen */ private function checkSingleDependency($dependency) { $type = $dependency['type']; $name = $dependency['name']; $version = $dependency['version']; $required = $dependency['required']; $result = [ 'type' => $type, 'name' => $name, 'version' => $version, 'required' => $required, 'satisfied' => false, 'current_version' => null, 'error' => null ]; try { switch ($type) { case 'module': $module = $this->moduleManager->getModule($name); if ($module && $module['active']) { $result['satisfied'] = true; $result['current_version'] = $module['version']; if ($version && version_compare($module['version'], $version, '<')) { $result['satisfied'] = false; $result['error'] = 'Version zu alt'; } } else { if ($required) { $result['error'] = 'Modul nicht gefunden oder inaktiv'; } } break; case 'plugin': $plugin = $this->plugin->getPlugin($name); if ($plugin && $plugin['active']) { $result['satisfied'] = true; $result['current_version'] = $plugin['version']; if ($version && version_compare($plugin['version'], $version, '<')) { $result['satisfied'] = false; $result['error'] = 'Version zu alt'; } } else { if ($required) { $result['error'] = 'Plugin nicht gefunden oder inaktiv'; } } break; case 'extension': $extension = $this->extension->getExtension($name); if ($extension && $extension['active']) { $result['satisfied'] = true; $result['current_version'] = $extension['version']; if ($version && version_compare($extension['version'], $version, '<')) { $result['satisfied'] = false; $result['error'] = 'Version zu alt'; } } else { if ($required) { $result['error'] = 'Extension nicht gefunden oder inaktiv'; } } break; case 'php': $currentVersion = PHP_VERSION; $result['current_version'] = $currentVersion; if (version_compare($currentVersion, $version, '>=')) { $result['satisfied'] = true; } else { $result['error'] = 'PHP-Version zu alt'; } break; case 'extension_php': if (extension_loaded($name)) { $result['satisfied'] = true; $result['current_version'] = phpversion($name); if ($version && version_compare(phpversion($name), $version, '<')) { $result['satisfied'] = false; $result['error'] = 'Extension-Version zu alt'; } } else { if ($required) { $result['error'] = 'PHP-Extension nicht geladen'; } } break; } } catch (\Exception $e) { $result['error'] = 'Prüfung fehlgeschlagen: ' . $e->getMessage(); } return $result; } /** * Dependencies für Item abrufen */ public function getDependencies($itemName, $itemType = 'module') { $cacheKey = 'dependencies_' . $itemType . '_' . $itemName; // Cache prüfen $cached = $this->cache->get($cacheKey); if ($cached !== null) { return $cached; } $dependencies = []; // Direkte Dependencies if (isset($this->dependencyGraph[$itemName])) { $dependencies = array_merge($dependencies, $this->dependencyGraph[$itemName]); } // Rekursive Dependencies foreach ($dependencies as $dependency) { $subDependencies = $this->getDependencies($dependency['name'], $dependency['type']); $dependencies = array_merge($dependencies, $subDependencies); } // Duplikate entfernen $dependencies = $this->removeDuplicateDependencies($dependencies); // Cache setzen $this->cache->set($cacheKey, $dependencies, 3600); // 1 Stunde return $dependencies; } /** * Duplikate aus Dependencies entfernen */ private function removeDuplicateDependencies($dependencies) { $unique = []; $seen = []; foreach ($dependencies as $dependency) { $key = $dependency['type'] . ':' . $dependency['name']; if (!isset($seen[$key])) { $seen[$key] = true; $unique[] = $dependency; } } return $unique; } /** * Dependency hinzufügen */ public function addDependency($dependentName, $dependentType, $dependencyName, $dependencyType, $dependencyVersion = null, $required = true, $priority = 10) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' INSERT INTO ws_dependencies ( dependent_name, dependent_type, dependency_name, dependency_type, dependency_version, required, priority, active, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, 1, NOW()) ON DUPLICATE KEY UPDATE dependency_version = ?, required = ?, priority = ?, updated_at = NOW() '); $stmt->execute([ $dependentName, $dependentType, $dependencyName, $dependencyType, $dependencyVersion, $required ? 1 : 0, $priority, $dependencyVersion, $required ? 1 : 0, $priority ]); // Dependency-Graph neu laden $this->loadDependencyGraph(); // Cache invalidieren $this->cache->delete('dependencies_' . $dependentType . '_' . $dependentName); $this->logger->info('Dependency hinzugefügt', [ 'dependent_name' => $dependentName, 'dependent_type' => $dependentType, 'dependency_name' => $dependencyName, 'dependency_type' => $dependencyType, 'dependency_version' => $dependencyVersion, 'required' => $required, 'priority' => $priority ]); return true; } catch (Exception $e) { $this->logger->error('Dependency hinzufügen Fehler', [ 'error' => $e->getMessage() ]); return false; } } /** * Dependency entfernen */ public function removeDependency($dependentName, $dependentType, $dependencyName, $dependencyType) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' DELETE FROM ws_dependencies WHERE dependent_name = ? AND dependent_type = ? AND dependency_name = ? AND dependency_type = ? '); $stmt->execute([ $dependentName, $dependentType, $dependencyName, $dependencyType ]); // Dependency-Graph neu laden $this->loadDependencyGraph(); // Cache invalidieren $this->cache->delete('dependencies_' . $dependentType . '_' . $dependentName); $this->logger->info('Dependency entfernt', [ 'dependent_name' => $dependentName, 'dependent_type' => $dependentType, 'dependency_name' => $dependencyName, 'dependency_type' => $dependencyType ]); return true; } catch (Exception $e) { $this->logger->error('Dependency entfernen Fehler', [ 'error' => $e->getMessage() ]); return false; } } /** * Konflikte prüfen */ public function checkConflicts($itemName, $itemType = 'module') { $conflicts = []; // Bekannte Konflikte prüfen foreach ($this->conflictResolutions as $resolution) { if ($this->matchesConflict($itemName, $itemType, $resolution)) { $conflicts[] = $resolution; } } // Dynamische Konflikt-Prüfung $dynamicConflicts = $this->checkDynamicConflicts($itemName, $itemType); $conflicts = array_merge($conflicts, $dynamicConflicts); return $conflicts; } /** * Konflikt-Matching */ private function matchesConflict($itemName, $itemType, $resolution) { return ($resolution['item1_name'] === $itemName && $resolution['item1_type'] === $itemType) || ($resolution['item2_name'] === $itemName && $resolution['item2_type'] === $itemType); } /** * Dynamische Konflikt-Prüfung */ private function checkDynamicConflicts($itemName, $itemType) { $conflicts = []; // Hook-Konflikte prüfen $hookConflicts = $this->checkHookConflicts($itemName, $itemType); $conflicts = array_merge($conflicts, $hookConflicts); // Namespace-Konflikte prüfen $namespaceConflicts = $this->checkNamespaceConflicts($itemName, $itemType); $conflicts = array_merge($conflicts, $namespaceConflicts); // Resource-Konflikte prüfen $resourceConflicts = $this->checkResourceConflicts($itemName, $itemType); $conflicts = array_merge($conflicts, $resourceConflicts); return $conflicts; } /** * Hook-Konflikte prüfen */ private function checkHookConflicts($itemName, $itemType) { $conflicts = []; // Hook-Registrierungen prüfen try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' SELECT hook_name, COUNT(*) as count FROM ws_hook_registry WHERE item_name != ? AND active = 1 GROUP BY hook_name HAVING count > 1 '); $stmt->execute([$itemName]); $hookConflicts = $stmt->fetchAllAssociative(); foreach ($hookConflicts as $conflict) { $conflicts[] = [ 'type' => 'hook_conflict', 'hook_name' => $conflict['hook_name'], 'description' => 'Mehrere Items registrieren denselben Hook', 'severity' => 'warning' ]; } } catch (Exception $e) { $this->logger->error('Hook-Konflikt-Prüfung Fehler', [ 'error' => $e->getMessage() ]); } return $conflicts; } /** * Namespace-Konflikte prüfen */ private function checkNamespaceConflicts($itemName, $itemType) { $conflicts = []; // Namespace-Kollisionen prüfen $namespaces = $this->getItemNamespaces($itemName, $itemType); foreach ($namespaces as $namespace) { if ($this->namespaceExists($namespace)) { $conflicts[] = [ 'type' => 'namespace_conflict', 'namespace' => $namespace, 'description' => 'Namespace bereits belegt', 'severity' => 'error' ]; } } return $conflicts; } /** * Resource-Konflikte prüfen */ private function checkResourceConflicts($itemName, $itemType) { $conflicts = []; // Asset-Konflikte prüfen $assets = $this->getItemAssets($itemName, $itemType); foreach ($assets as $asset) { if ($this->assetExists($asset)) { $conflicts[] = [ 'type' => 'resource_conflict', 'resource' => $asset, 'description' => 'Resource bereits vorhanden', 'severity' => 'warning' ]; } } return $conflicts; } /** * Namespaces für Item abrufen */ private function getItemNamespaces($itemName, $itemType) { $namespaces = []; switch ($itemType) { case 'module': $namespaces[] = 'App\\Modules\\' . ucfirst($itemName); break; case 'plugin': $namespaces[] = 'App\\Plugins\\' . ucfirst($itemName); break; case 'extension': $namespaces[] = 'App\\Extensions\\' . ucfirst($itemName); break; } return $namespaces; } /** * Assets für Item abrufen */ private function getItemAssets($itemName, $itemType) { $assets = []; $basePath = __DIR__ . '/../../../'; switch ($itemType) { case 'module': $assets[] = $basePath . 'modules/' . $itemName . '/assets/'; break; case 'plugin': $assets[] = $basePath . 'plugins/' . $itemName . '/assets/'; break; case 'extension': $assets[] = $basePath . 'extensions/' . $itemName . '/assets/'; break; } return $assets; } /** * Namespace-Existenz prüfen */ private function namespaceExists($namespace) { // Vereinfachte Prüfung - in der Praxis würde hier eine vollständige Namespace-Analyse stehen return class_exists($namespace . '\\Main'); } /** * Asset-Existenz prüfen */ private function assetExists($asset) { return file_exists($asset); } /** * Konflikt-Lösung hinzufügen */ public function addConflictResolution($conflictType, $item1Name, $item1Type, $item2Name, $item2Type, $resolutionType, $resolutionAction, $priority = 10) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' INSERT INTO ws_conflict_resolutions ( conflict_type, item1_name, item1_type, item2_name, item2_type, resolution_type, resolution_action, priority, active, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, NOW()) '); $stmt->execute([ $conflictType, $item1Name, $item1Type, $item2Name, $item2Type, $resolutionType, $resolutionAction, $priority ]); // Konflikt-Lösungen neu laden $this->loadConflictResolutions(); $this->logger->info('Konflikt-Lösung hinzugefügt', [ 'conflict_type' => $conflictType, 'item1_name' => $item1Name, 'item1_type' => $item1Type, 'item2_name' => $item2Name, 'item2_type' => $item2Type, 'resolution_type' => $resolutionType, 'resolution_action' => $resolutionAction, 'priority' => $priority ]); return true; } catch (Exception $e) { $this->logger->error('Konflikt-Lösung hinzufügen Fehler', [ 'error' => $e->getMessage() ]); return false; } } /** * Dependency-Resolver */ public function resolveDependencies($itemName, $itemType = 'module') { $dependencies = $this->getDependencies($itemName, $itemType); $resolved = []; $unresolved = []; foreach ($dependencies as $dependency) { $result = $this->checkSingleDependency($dependency); if ($result['satisfied']) { $resolved[] = $result; } else { $unresolved[] = $result; } } return [ 'resolved' => $resolved, 'unresolved' => $unresolved, 'all_satisfied' => empty($unresolved) ]; } /** * Dependency-Manager aktivieren/deaktivieren */ public function setEnabled($enabled) { $this->enabled = $enabled; return $this; } /** * Dependency-Manager Status prüfen */ public function isEnabled() { return $this->enabled; } /** * Dependency-Graph abrufen */ public function getDependencyGraph() { return $this->dependencyGraph; } /** * Konflikt-Lösungen abrufen */ public function getConflictResolutions() { return $this->conflictResolutions; } }