$oModule) { /** @var iTopCoreModule $oModule */ $aDependsOnModuleName[$oModule->GetModuleName()]=[]; } foreach ($aUnresolvedDependencyModules as $sModuleId => $oModule) { $iInDegreeCounter = 0; /** @var iTopCoreModule $oModule */ $aUnresolvedDependencyModuleNames = $oModule->GetUnresolvedDependencyModuleNames(); foreach ($aUnresolvedDependencyModuleNames as $sModuleName) { if (array_key_exists($sModuleName, $aDependsOnModuleName)) { $aDependsOnModuleName[$sModuleName][] = $sModuleId; $iInDegreeCounter++; } } //include all modules $iInDegreeCounterIncludingOutsideModules = count($oModule->GetUnresolvedDependencyModuleNames()); $aCountDepsByModuleId[$sModuleId] = [$iInDegreeCounter, $iInDegreeCounterIncludingOutsideModules, $sModuleId]; } $aRes=[]; while(count($aUnresolvedDependencyModules)>0) { asort($aCountDepsByModuleId); uasort($aCountDepsByModuleId, function (array $aDeps1, array $aDeps2){ //compare $iInDegreeCounter $res = $aDeps1[0] - $aDeps2[0]; if ($res != 0){ return $res; } //compare $iInDegreeCounterIncludingOutsideModules $res = $aDeps1[1] - $aDeps2[1]; if ($res != 0){ return $res; } //alphabetical order at least return strcmp($aDeps1[2], $aDeps2[2]); }); $bOneLoopAtLeast=false; foreach ($aCountDepsByModuleId as $sModuleId => $iInDegreeCounter){ $oModule=$aUnresolvedDependencyModules[$sModuleId]; if ($bOneLoopAtLeast && $iInDegreeCounter>0){ break; } unset($aUnresolvedDependencyModules[$sModuleId]); unset($aCountDepsByModuleId[$sModuleId]); $aRes[$sModuleId]=$oModule; //when 2 versions of the same module (name) below array has been removed already if (array_key_exists($oModule->GetModuleName(), $aDependsOnModuleName)) { foreach ($aDependsOnModuleName[$oModule->GetModuleName()] as $sModuleId2) { if (! array_key_exists($sModuleId2, $aCountDepsByModuleId)){ continue; } $aDepCount = $aCountDepsByModuleId[$sModuleId2]; $iInDegreeCounter = $aDepCount[0] - 1; $iInDegreeCounterIncludingOutsideModules = $aDepCount[1]; $aCountDepsByModuleId[$sModuleId2] = [$iInDegreeCounter, $iInDegreeCounterIncludingOutsideModules, $sModuleId2]; } unset($aDependsOnModuleName[$oModule->GetModuleName()]); } $bOneLoopAtLeast=true; } } $aUnresolvedDependencyModules=$aRes; } /** * Arrange an list of modules, based on their (inter) dependencies * @param array $aModules The list of modules to process: 'id' => $aModuleInfo * @param bool $bAbortOnMissingDependency ... * @param array $aModulesToLoad List of modules to search for, defaults to all if omitted * @return array * @throws \MissingDependencyException */ public static function OrderModulesByDependencies($aModules, $bAbortOnMissingDependency = false, $aModulesToLoad = null) { // Order the modules to take into account their inter-dependencies $aUnresolvedDependencyModules = []; $aSelectedModules = []; foreach($aModules as $sModuleId => $aModule) { $oModule = new iTopCoreModule($sModuleId); $sModuleName = $oModule->GetModuleName(); if (is_null($aModulesToLoad) || in_array($sModuleName, $aModulesToLoad)) { $oModule->SetDependencies($aModule['dependencies']); $aUnresolvedDependencyModules[$sModuleId]=$oModule; $aSelectedModules[$sModuleName] = true; } } ksort($aUnresolvedDependencyModules); $aOrderedModules = []; $aModuleVersions=[]; $iLoopCount = 1; while(($iLoopCount < count($aModules)+1) && (count($aUnresolvedDependencyModules) > 0) ) { foreach($aUnresolvedDependencyModules as $sModuleId => $oModule) { /** @var iTopCoreModule $oModule */ if ($oModule->IsModuleResolved($aModuleVersions, $aSelectedModules)){ $aOrderedModules[] = $sModuleId; $aModuleVersions[$oModule->GetModuleName()] = $oModule->GetVersion(); unset($aUnresolvedDependencyModules[$sModuleId]); } } $iLoopCount++; } if ($bAbortOnMissingDependency && count($aUnresolvedDependencyModules) > 0) { self::SortModulesByCountOfDepencenciesDescending($aUnresolvedDependencyModules); $aModulesInfo = []; $aModuleDeps = []; foreach($aUnresolvedDependencyModules as $sModuleId => $oModule) { $aModule = $aModules[$sModuleId]; $aDepsWithIcons = []; foreach($oModule->aAllDependencies as $sIndex => $sDepId) { if (array_key_exists($sDepId, $oModule->aOngoingDependencies)) { $aDepsWithIcons[$sIndex] = '❌ ' . $sDepId; } else { $aDepsWithIcons[$sIndex] = '✅ ' . $sDepId; } } $aModuleDeps[] = "{$aModule['label']} (id: $sModuleId) depends on: ".implode(' + ', $aDepsWithIcons); $aModulesInfo[$sModuleId] = array('module' => $aModule, 'dependencies' => $aDepsWithIcons); } $sMessage = "The following modules have unmet dependencies:\n".implode(",\n", $aModuleDeps); $oException = new MissingDependencyException($sMessage); $oException->aModulesInfo = $aModulesInfo; throw $oException; } // Return the ordered list, so that the dependencies are met... $aResult = []; foreach($aOrderedModules as $sId) { $aResult[$sId] = $aModules[$sId]; } return $aResult; } }