diff --git a/src/Classes/DependencyBuilder.php b/src/Classes/DependencyBuilder.php deleted file mode 100644 index 52b1f0f4..00000000 --- a/src/Classes/DependencyBuilder.php +++ /dev/null @@ -1,229 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace RobinTheHood\ModifiedModuleLoaderClient; - -use RobinTheHood\ModifiedModuleLoaderClient\Loader\ModuleLoader; - -class DependencyBuilder -{ - // protected $comparator; - - // public function __construct() - // { - // $this->comparator = new Comparator(new Parser()); - // } - - // public function getInstalledModules() - // { - // $localModuleLoader = LocalModuleLoader::getModuleLoader(); - // $modules = $localModuleLoader->loadAllVersions(); - // $installedModules = ModuleFilter::filterInstalled($modules); - // return $installedModules; - // } - - // public function getAllModules($module) - // { - // $requireModulesTree = $this->buildTreeByModule($module); - - // $requireModules = []; - // $this->flattenTree($requireModulesTree, $requireModules); - - // $requireModules = $this->getUniqueRequireModules($requireModules); - - // $modules = []; - // foreach ($requireModules as $requireModule) { - // $modules[] = $requireModule['module']; - // } - - // return $modules; - // } - - // public function canBeInstalled($module) - // { - // $modules = $this->getAllModules($module); - // $modules[] = $module; - // foreach ($modules as $module) { - // $this->canBeInstalledTestRequiers($module, $modules); - // $this->canBeInstalledTestSelected($module, $modules); - // $this->canBeInstalledTestInstalled($module); - // } - // } - - // public function canBeInstalledTestInstalled($module) - // { - // $installedModules = $this->getInstalledModules(); - // $this->canBeInstalledTestSelected($module, $installedModules); - // } - - // public function canBeInstalledTestSelected($module, $modules) - // { - // $usedByEntrys = $this->getUsedByEntrys($module, $modules); - // foreach ($usedByEntrys as $usedByEntry) { - // if (!$this->comparator->satisfies($module->getVersion(), $usedByEntry['requiredVersion'])) { - // $a = $module->getArchiveName(); - // $av = $module->getVersion(); - // $b = $usedByEntry['module']->getArchiveName(); - // $bv = $usedByEntry['requiredVersion']; - // die("Module $a version $av can not be installed because module $b requires version $bv"); - // } - // } - // } - - // public function canBeInstalledTestRequiers($module, $modules) - // { - // foreach ($module->getRequire() as $archiveName => $version) { - // $moduleFound = false; - // foreach ($modules as $selectedModule) { - // if ($selectedModule->getArchiveName() != $archiveName) { - // continue; - // } - - // $moduleFound = true; - // if (!$this->comparator->satisfies($selectedModule->getVersion(), $version)) { - // $a = $selectedModule->getArchiveName(); - // $av = $module->getVersion(); - // die("Module $a version $av can not be installed because module $archiveName version $version is required"); - // } - // } - - // if (!$moduleFound) { - // die("Module $archiveName version $version can not be installed because module was not found."); - // } - // } - // } - - // Liefert alle Module aus $selectedModules die das Modul $module verwenden - // inkl. die benötigte Versionsnummer. - // public function getUsedByEntrys($module, $selectedModules) - // { - // $usedByEntrys = []; - // foreach ($selectedModules as $selectedModule) { - // foreach ($selectedModule->getRequire() as $archiveName => $version) { - // if ($archiveName == $module->getArchiveName()) { - // $usedByEntrys[] = [ - // 'module' => $selectedModule, - // 'requiredVersion' => $version - // ]; - // } - // } - // } - // return $usedByEntrys; - // } - - public function flattenTree($moduleTree, &$modules = null) - { - if (!$moduleTree) { - return; - } - - foreach ($moduleTree as $entry) { - $modules[] = [ - 'module' => $entry['module'], - 'requestedVersion' => $entry['requestedVersion'], - 'selectedVersion' => $entry['selectedVersion'] - ]; - $this->flattenTree($entry['require'], $modules); - } - } - - // public function getUniqueRequireModules($requireModules) - // { - // $uniqueModules = []; - // foreach ($requireModules as $requireModule) { - // $index = $requireModule['module']->getArchiveName() . ':' . $requireModule['selectedVersion']; - // $uniqueModules[$index] = [ - // 'module' => $requireModule['module'], - // 'requestedVersion' => $requireModule['requestedVersion'], - // 'selectedVersion' => $requireModule['selectedVersion'] - // ]; - // } - - // return array_values($uniqueModules); - // } - - // private function buildTreeByArchiveName($archiveName, $version) - // { - // $moduleLoader = ModuleLO - // $module = $this->loadModuleByArchiveName($archiveName, $version); - // return $this->buildTreeByModule($module); - // } - - // private function buildTreeByModule($module) - // { - // $requireModulesTree = $this->buildTreeByModuleRecursive($module); - // return $requireModulesTree; - // } - - public function test() - { - $moduleLoader = ModuleLoader::getModuleLoader(); - $module = $moduleLoader->loadLatestVersionByArchiveName('firstweb/multi-order'); - - // var_dump($module); - // die(); - $tree = $this->buildTreeByModuleRecursive($module); - - echo '
';
- print_r($tree);
- }
-
- private function buildTreeByModuleRecursive(Module $module, int $depth = 0): array
- {
- if ($depth >= 5) {
- return false;
- }
-
- $require = $module->getRequire();
-
- $requireModulesTree = [];
- foreach ($require as $archiveName => $versionConstraint) {
- $moduleLoader = ModuleLoader::getModuleLoader();
-
- // An dieser Stelle wird zurzeit immer die neuste Variante ausgewählt.
- // Eigentlich müssen hier alle Varianten die zu $versionConstraint passen
- // ausgewählt und weiter verarbeitet werden. Zurzeit wird $versionConstraint
- // aber nicht beachtet. Das muss bei einer späteren Version verbessert werden.
- $selectedModule = $moduleLoader->loadLatestVersionByArchiveName($archiveName);
-
- $entry = [];
- if ($selectedModule) {
- // $entry['module'] = $selectedModule;
- $entry['archiveName'] = $selectedModule->getArchiveName();
- $entry['requestedVersion'] = $versionConstraint;
- $entry['selectedVersion'] = $selectedModule->getVersion();
- $entry['require'] = [];
- $requireModules = $this->buildTreeByModuleRecursive($selectedModule, ++$depth);
-
- if ($requireModules) {
- $entry['require'] = $requireModules;
- }
-
- $requireModulesTree[] = $entry;
- }
- }
-
- return $requireModulesTree;
- }
-
- // private function loadLatestVersionByArchiveName(string $archiveName): ?Module
- // {
- // $modules = [];
- // $localModuleLoader = LocalModuleLoader::getModuleLoader();
- // $modules[] = $localModuleLoader->loadLatestVersionByArchiveName($archiveName);
-
- // $remoteModuleLoader = RemoteModuleLoader::getModuleLoader();
- // $modules[] = $remoteModuleLoader->loadLatestVersionByArchiveName($archiveName);
-
- // $latestVersion = ModuleFilter::getLatestVersion($modules);
- // return $latestVersion;
- // }
-}
diff --git a/src/Classes/DependencyManager/Combination.php b/src/Classes/DependencyManager/Combination.php
new file mode 100644
index 00000000..0621d779
--- /dev/null
+++ b/src/Classes/DependencyManager/Combination.php
@@ -0,0 +1,104 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class Combination
+{
+ /** @var array */
+ private $combinations = [];
+
+ /**
+ * @return array
+ */
+ public function getAll(): array
+ {
+ return $this->combinations;
+ }
+
+ public function add(string $archiveName, string $version): void
+ {
+ if (array_key_exists($archiveName, $this->combinations)) {
+ throw new DependencyException($archiveName . ' is already set.');
+ }
+
+ $this->combinations[$archiveName] = $version;
+ }
+
+ public function overwrite(string $archiveName, string $version): void
+ {
+ $this->combinations[$archiveName] = $version;
+ }
+
+ public function getVersion(string $archiveName): string
+ {
+ if (!array_key_exists($archiveName, $this->combinations)) {
+ throw new DependencyException('Version of ' . $archiveName . ' not found.');
+ }
+
+ return $this->combinations[$archiveName];
+ }
+
+ public function clone(): Combination
+ {
+ $combinations = $this->combinations; // clones an array
+ $newCombination = new Combination();
+ $newCombination->combinations = $combinations;
+ return $newCombination;
+ }
+
+ /**
+ * Liefert eine neues Combinations Obj zurück, in der nur echte Module enthalten sind
+ *
+ * Einträge, wie modified, php, mmlc werden entfernt.
+ *
+ * @return Combination Liefert ein Combination Obj nur mit echten Modulen
+ */
+ public function strip(): Combination
+ {
+ $combination = new Combination();
+ foreach ($this->combinations as $archiveName => $version) {
+ if (!$this->isArchiveName($archiveName)) {
+ continue;
+ }
+
+ $combination->add($archiveName, $version);
+ }
+ return $combination;
+ }
+
+ /**
+ * Überprüft, ob es sich um einen gültigen ArchvieName handelt
+ *
+ * @param string $archiveName Ein ArchiveName
+ * @return bool Liefert true, wenn $archiveName ein gültiger ArchvieName ist
+ */
+ private function isArchiveName(string $archiveName): bool
+ {
+ if (strpos($archiveName, '/') === false) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ public function __toString(): string
+ {
+ $strings = [];
+ foreach ($this->combinations as $archiveName => $version) {
+ $strings[] = "$archiveName v$version";
+ }
+ return implode(', ', $strings);
+ }
+}
diff --git a/src/Classes/DependencyManager/CombinationBuilder.php b/src/Classes/DependencyManager/CombinationBuilder.php
new file mode 100644
index 00000000..ee4eedbb
--- /dev/null
+++ b/src/Classes/DependencyManager/CombinationBuilder.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class CombinationBuilder
+{
+ /**
+ * @param FlatEntry[] $flatEntries
+ *
+ * @return Combination[]
+ */
+ public function buildAllFromModuleFlatEntries($flatEntries): array
+ {
+ $combinations = [];
+ $flatEntries = array_values($flatEntries);
+ $this->buildAllCombinationsFromModuleFlatEntries($flatEntries, $combinations, new Combination(), 0);
+ return $combinations;
+ }
+
+ /**
+ * @param FlatEntry[] $flatEntries
+ * @param Combination[] $combinations
+ * @param Combination $combination
+ * @param int $index
+ */
+ private function buildAllCombinationsFromModuleFlatEntries(
+ array &$flatEntries,
+ array &$combinations,
+ Combination $combination,
+ int $index
+ ): void {
+ /** @var FlatEntry*/
+ $flatEntry = $flatEntries[$index] ?? [];
+
+ if (!$flatEntry) {
+ $combinations[] = $combination;
+ return;
+ }
+
+ foreach ($flatEntry->versions as $versionStr) {
+ $newCombination = $combination->clone();
+ $newCombination->add($flatEntry->archiveName, $versionStr);
+ $this->buildAllCombinationsFromModuleFlatEntries($flatEntries, $combinations, $newCombination, $index + 1);
+ }
+ }
+}
diff --git a/src/Classes/DependencyManager/CombinationIterator.php b/src/Classes/DependencyManager/CombinationIterator.php
new file mode 100644
index 00000000..84683748
--- /dev/null
+++ b/src/Classes/DependencyManager/CombinationIterator.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class CombinationIterator
+{
+ /** @var FlatEntry[] */
+ private $flatEntries;
+
+ /** @var int[] */
+
+ /** @var Counter $counter */
+ private $counter;
+
+ /**
+ * @param FlatEntry[] $flatEntries
+ */
+ public function __construct(array $flatEntries)
+ {
+ $this->flatEntries = $flatEntries;
+
+ $counterMaxValues = [];
+ foreach ($flatEntries as $flatEntry) {
+ if (!$flatEntry->versions) {
+ continue;
+ }
+ $counterMaxValues[] = count($flatEntry->versions) - 1;
+ }
+
+ $this->counter = new Counter($counterMaxValues);
+ }
+
+ public function current(): Combination
+ {
+ return $this->combinationFromCounter($this->flatEntries, $this->counter);
+ }
+
+ public function next(): Combination
+ {
+ $this->counter->next();
+ return $this->combinationFromCounter($this->flatEntries, $this->counter);
+ }
+
+ public function isStart(): bool
+ {
+ return $this->counter->isStart();
+ }
+
+ /**
+ * @param FlatEntry[] $flatEntries
+ * @param Counter Counter
+ */
+ private function combinationFromCounter(array $flatEntries, Counter $counter): Combination
+ {
+ $counter = $counter->current();
+
+ $counterIndex = 0;
+ $combination = new Combination();
+ foreach ($flatEntries as $flatEntry) {
+ if (!$flatEntry->versions) {
+ continue;
+ }
+ $versionIndex = $counter[$counterIndex];
+ $versionStr = $flatEntry->versions[$versionIndex];
+ $combination->add($flatEntry->archiveName, $versionStr);
+ $counterIndex++;
+ }
+
+ return $combination;
+ }
+}
diff --git a/src/Classes/DependencyManager/CombinationSatisfyer.php b/src/Classes/DependencyManager/CombinationSatisfyer.php
new file mode 100644
index 00000000..b3a417f7
--- /dev/null
+++ b/src/Classes/DependencyManager/CombinationSatisfyer.php
@@ -0,0 +1,247 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+use RobinTheHood\ModifiedModuleLoaderClient\Semver\Comparator;
+use RobinTheHood\ModifiedModuleLoaderClient\Semver\Parser;
+
+class CombinationSatisfyer
+{
+ /** @var Comparator */
+ private $comparator;
+
+ public function __construct()
+ {
+ $this->comparator = new Comparator(new Parser());
+ }
+
+ /**
+ * @param ModuleTree[] $moduleTrees
+ * @param Combination[] $combinations
+ *
+ * @return CombinationSatisfyerResult
+ */
+ public function satisfiesCominationsFromModuleTrees(
+ array $moduleTrees,
+ array $combinations
+ ): CombinationSatisfyerResult {
+ $testCombination = null;
+ $foundCombination = new Combination();
+ $failLog = new FailLog();
+
+ foreach ($combinations as $testCombination) {
+ $foundCombination = new Combination();
+ $result = $this->satisfiesCominationFromModuleTrees(
+ $moduleTrees,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+
+ if ($result) {
+ $combinationSatisfyerResult = new CombinationSatisfyerResult(
+ CombinationSatisfyerResult::RESULT_COMBINATION_FOUND,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+ return $combinationSatisfyerResult;
+ }
+ }
+
+ $combinationSatisfyerResult = new CombinationSatisfyerResult(
+ CombinationSatisfyerResult::RESULT_COMBINATION_NOT_FOUND,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+
+ return $combinationSatisfyerResult;
+ }
+
+ /**
+ * @param ModuleTree $moduleTree
+ * @param Combination[] $combinations
+ *
+ * @return CombinationSatisfyerResult
+ */
+ public function satisfiesCominationsFromModuleTree(
+ ModuleTree $moduleTree,
+ array $combinations
+ ): CombinationSatisfyerResult {
+ $testCombination = null;
+ $foundCombination = new Combination();
+ $failLog = new FailLog();
+
+ foreach ($combinations as $testCombination) {
+ $result = $this->satisfiesCominationFromModuleTree(
+ $moduleTree,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+
+ if ($result) {
+ $combinationSatisfyerResult = new CombinationSatisfyerResult(
+ CombinationSatisfyerResult::RESULT_COMBINATION_FOUND,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+ return $combinationSatisfyerResult;
+ }
+ }
+
+ $combinationSatisfyerResult = new CombinationSatisfyerResult(
+ CombinationSatisfyerResult::RESULT_COMBINATION_NOT_FOUND,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+
+ return $combinationSatisfyerResult;
+ }
+
+ public function satisfiesCominationsFromModuleWithIterator(
+ ModuleTree $moduleTree,
+ CombinationIterator $combinationIterator
+ ): CombinationSatisfyerResult {
+ $foundCombination = new Combination();
+
+ while (true) {
+ $failLog = new FailLog();
+ $testCombination = $combinationIterator->current();
+ $result = $this->satisfiesCominationFromModuleTree(
+ $moduleTree,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+
+ if ($result) {
+ $combinationSatisfyerResult = new CombinationSatisfyerResult(
+ CombinationSatisfyerResult::RESULT_COMBINATION_FOUND,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+ return $combinationSatisfyerResult;
+ }
+
+ $combinationIterator->next();
+ if ($combinationIterator->isStart()) {
+ $combinationSatisfyerResult = new CombinationSatisfyerResult(
+ CombinationSatisfyerResult::RESULT_COMBINATION_NOT_FOUND,
+ $testCombination,
+ $foundCombination,
+ $failLog
+ );
+ return $combinationSatisfyerResult;
+ }
+ }
+ }
+
+ /**
+ * @param ModuleTree $moduleTree
+ * @param Combination $combination
+ * @param Combination $foundCombination
+ * @param FailLog $failLog
+ * @param ModuleTree[] $moduleTreeChain
+ *
+ * @return bool
+ */
+ public function satisfiesCominationFromModuleTree(
+ ModuleTree $moduleTree,
+ Combination $combination,
+ Combination &$foundCombination,
+ FailLog &$failLog,
+ array $moduleTreeChain = []
+ ): bool {
+ // Context: Module
+ $archiveName = $moduleTree->archiveName;
+ try {
+ $selectedVersion = $combination->getVersion($archiveName);
+ } catch (DependencyException $e) {
+ return false;
+ }
+
+ // Es gibt keine weiteren Untermodule
+ if (!$moduleTree->moduleVersions) {
+ $result = $this->comparator->satisfies($selectedVersion, $moduleTree->versionConstraint);
+ if (!$result) {
+ $failLog->fail($moduleTreeChain, $archiveName, $selectedVersion, $moduleTree->versionConstraint);
+ } else {
+ $failLog->unfail($moduleTreeChain, $archiveName, $selectedVersion, $moduleTree->versionConstraint);
+ }
+ return $result;
+ }
+
+ foreach ($moduleTree->moduleVersions as $moduleVersion) {
+ // Context: Version
+ if ($moduleVersion->version === $selectedVersion) {
+ $foundCombination->overwrite($archiveName, $moduleVersion->version);
+
+ $failLog->unfail(
+ $moduleTreeChain,
+ $archiveName,
+ $moduleVersion->version,
+ $moduleTree->versionConstraint
+ );
+
+ return $this->satisfiesCominationFromModuleTrees(
+ $moduleVersion->require,
+ $combination,
+ $foundCombination,
+ $failLog,
+ array_merge($moduleTreeChain, [$moduleTree])
+ );
+ }
+
+ $failLog->fail($moduleTreeChain, $archiveName, $moduleVersion->version, $moduleTree->versionConstraint);
+ }
+
+ return false;
+ }
+
+ /**
+ * @param ModuleTree[] $moduleTrees
+ * @param Combination $combination
+ * @param Combination $foundCombination
+ * @param FailLog $failLog
+ * @param ModuleTree[] $moduleTreeChain
+ *
+ * @return bool
+ */
+ public function satisfiesCominationFromModuleTrees(
+ array $moduleTrees,
+ Combination $combination,
+ Combination &$foundCombination,
+ FailLog &$failLog,
+ array $moduleTreeChain = []
+ ): bool {
+ // Context: Expanded
+ $moduleResult = true;
+ foreach ($moduleTrees as $moduleTree) {
+ $result = $this->satisfiesCominationFromModuleTree(
+ $moduleTree,
+ $combination,
+ $foundCombination,
+ $failLog,
+ $moduleTreeChain
+ );
+ $moduleResult = $moduleResult && $result;
+ }
+ return $moduleResult;
+ }
+}
diff --git a/src/Classes/DependencyManager/CombinationSatisfyerResult.php b/src/Classes/DependencyManager/CombinationSatisfyerResult.php
new file mode 100644
index 00000000..df6cad59
--- /dev/null
+++ b/src/Classes/DependencyManager/CombinationSatisfyerResult.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class CombinationSatisfyerResult
+{
+ public const RESULT_COMBINATION_NOT_FOUND = 0;
+ public const RESULT_COMBINATION_FOUND = 1;
+
+ /** @var int */
+ public $result = -1;
+
+ /** @var ?Combination */
+ public $testCombination = null;
+
+ /** @var ?Combination */
+ public $foundCombination = null;
+
+ /** @var ?FailLog */
+ public $failLog = null;
+
+ public function __construct(
+ int $result,
+ ?Combination $testCombination,
+ ?Combination $foundCombination,
+ ?FailLog $failLog
+ ) {
+ $this->result = $result;
+ $this->testCombination = $testCombination;
+ $this->foundCombination = $foundCombination;
+ $this->failLog = $failLog;
+ }
+}
diff --git a/src/Classes/DependencyManager/Counter.php b/src/Classes/DependencyManager/Counter.php
new file mode 100644
index 00000000..1821451a
--- /dev/null
+++ b/src/Classes/DependencyManager/Counter.php
@@ -0,0 +1,67 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class Counter
+{
+ private $counterMaxValues = [];
+ private $counterValues = [];
+
+ public function __construct(array $maxValues)
+ {
+ $this->counterMaxValues = $maxValues;
+ foreach ($this->counterMaxValues as $counterMaxValue) {
+ $this->counterValues[] = 0;
+ }
+ }
+
+ public function current(): array
+ {
+ return $this->counterValues;
+ }
+
+ public function next(): array
+ {
+ $this->incrementCounterAtIndex(0);
+ return $this->counterValues;
+ }
+
+ public function isStart(): bool
+ {
+ foreach ($this->counterValues as $value) {
+ if ($value !== 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private function incrementCounterAtIndex(int $counterIndex)
+ {
+ if ($counterIndex > count($this->counterMaxValues) - 1) {
+ return true;
+ }
+
+ if ($this->incrementCounterAtIndex($counterIndex + 1)) {
+ $this->counterValues[$counterIndex]++;
+ }
+
+ if ($this->counterValues[$counterIndex] > $this->counterMaxValues[$counterIndex]) {
+ $this->counterValues[$counterIndex] = 0;
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/Classes/DependencyManager/DependencyBuilder.php b/src/Classes/DependencyManager/DependencyBuilder.php
new file mode 100644
index 00000000..934cffc4
--- /dev/null
+++ b/src/Classes/DependencyManager/DependencyBuilder.php
@@ -0,0 +1,186 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+use RobinTheHood\ModifiedModuleLoaderClient\Loader\ModuleLoader;
+use RobinTheHood\ModifiedModuleLoaderClient\Module;
+use RobinTheHood\ModifiedModuleLoaderClient\Semver\Constraint;
+
+class DependencyBuilder
+{
+ private function logFile($value, $file)
+ {
+ @mkdir(__DIR__ . '/logs/');
+ $path = __DIR__ . '/logs/' . $file;
+ file_put_contents($path, json_encode($value, JSON_PRETTY_PRINT));
+ }
+
+ public function log($var)
+ {
+ print_r($var);
+ }
+
+ public function test()
+ {
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $module = $moduleLoader->loadLatestVersionByArchiveName('firstweb/multi-order');
+
+ if (!$module) {
+ die('Can not find base module');
+ }
+
+ $systemSet = new SystemSet();
+ $systemSet->set([
+ "modified" => '2.0.4.2',
+ "php" => '7.4.0',
+ "mmlc" => '1.19.0',
+ "composer/autoload" => '1.3.0',
+ "robinthehood/modified-std-module" => '0.9.0',
+ "robinthehood/modified-orm" => '1.8.1',
+ "robinthehood/pdf-bill" => '0.17.0'
+ ]);
+
+ $this->log('TEST: satisfiesContraints1');
+ $combinationSatisfyerResult = $this->satisfiesContraints1($module, $systemSet);
+ $this->log($combinationSatisfyerResult);
+
+ $this->log('TEST: satisfiesContraints2');
+ $combinationSatisfyerResult = $this->satisfiesContraints2('firstweb/multi-order', '^1.0.0', $systemSet);
+ $this->log($combinationSatisfyerResult);
+
+ // var_dump('TEST: satisfiesContraints3');
+ $combinationSatisfyerResult = $this->satisfies('firstweb/multi-order', '^1.0.0', $systemSet);
+ $this->log($combinationSatisfyerResult);
+ }
+
+ public function satisfiesContraints1(Module $module, SystemSet $systemSet): CombinationSatisfyerResult
+ {
+ $moduleTreeBuilder = new ModuleTreeBuilder();
+ $moduleTrees = $moduleTreeBuilder->buildListByConstraints($module);
+ $this->logFile($moduleTrees, '1-moduleTrees.json');
+
+ $flatEntryBuilder = new FlatEntryBuilder();
+ $flatEntries = $flatEntryBuilder->buildListFromModuleTrees($moduleTrees);
+ $this->logFile($flatEntries, '1-flatEntries.json');
+
+ $flatEntries = $flatEntryBuilder->fitSystemSet($flatEntries, $systemSet);
+ $this->logFile($flatEntries, '1-flatEntries-fit.json');
+
+ $combinationBuilder = new CombinationBuilder();
+ $combinations = $combinationBuilder->buildAllFromModuleFlatEntries($flatEntries);
+ $this->logFile($combinations, '1-combinations.json');
+
+ $combinationSatisfyer = new CombinationSatisfyer();
+ $combinationSatisfyerResult = $combinationSatisfyer->satisfiesCominationsFromModuleTrees(
+ $moduleTrees,
+ $combinations
+ );
+
+ return $combinationSatisfyerResult;
+ }
+
+
+ public function satisfiesContraints2(
+ string $archiveName,
+ string $constraint,
+ SystemSet $systemSet
+ ): CombinationSatisfyerResult {
+ $moduleTreeBuilder = new ModuleTreeBuilder();
+ $moduleTree = $moduleTreeBuilder->buildByConstraints($archiveName, $constraint);
+ $this->logFile($moduleTree, '2-moduleTrees.json');
+
+ $flatEntryBuilder = new FlatEntryBuilder();
+ $flatEntries = $flatEntryBuilder->buildListFromModuleTree($moduleTree);
+ $this->logFile($flatEntries, '2-flatEntries.json');
+
+ $flatEntries = $flatEntryBuilder->fitSystemSet($flatEntries, $systemSet);
+ $this->logFile($flatEntries, '2-flatEntries-fit.json');
+
+ $combinationBuilder = new CombinationBuilder();
+ $combinations = $combinationBuilder->buildAllFromModuleFlatEntries($flatEntries);
+ $this->logFile($combinations, '2-combinations.json');
+
+ $combinationSatisfyer = new CombinationSatisfyer();
+ $combinationSatisfyerResult = $combinationSatisfyer->satisfiesCominationsFromModuleTree(
+ $moduleTree,
+ $combinations
+ );
+
+ return $combinationSatisfyerResult;
+ }
+
+ public function satisfies(string $archiveName, string $constraint, SystemSet $systemSet): CombinationSatisfyerResult
+ {
+ $systemSet->remove($archiveName);
+ $constraint = $this->createConstraint($archiveName, $constraint, $systemSet);
+
+ $moduleTreeBuilder = new ModuleTreeBuilder();
+ $moduleTree = $moduleTreeBuilder->buildByConstraints($archiveName, $constraint);
+ $this->logFile($moduleTree, '3-moduleTrees.json');
+
+ $flatEntryBuilder = new FlatEntryBuilder();
+ $flatEntries = $flatEntryBuilder->buildListFromModuleTree($moduleTree);
+ $this->logFile($flatEntries, '3-flatEntries.json');
+
+ $flatEntries = $flatEntryBuilder->fitSystemSet($flatEntries, $systemSet);
+ $this->logFile($flatEntries, '3-flatEntries-fit.json');
+
+ $combinationIterator = new CombinationIterator($flatEntries);
+ $combinationSatisfyer = new CombinationSatisfyer();
+ $combinationSatisfyerResult = $combinationSatisfyer->satisfiesCominationsFromModuleWithIterator(
+ $moduleTree,
+ $combinationIterator
+ );
+ return $combinationSatisfyerResult;
+ }
+
+ /**
+ * Gehe alle Module durch, die in $systemSet sind und das gleichzeitig Modul $archiveName benötigen.
+ * Gibt ein $constraint zurück, sodass die Anforderungenden der Module in $systemSet erhaltenbleiben.
+ */
+ private function createConstraint(string $archiveName, string $constraint, SystemSet $systemSet): string
+ {
+ /** @var string[] */
+ $requiredConstraints = [$constraint];
+
+ $archives = $systemSet->getArchives();
+ foreach ($archives as $archiveNameB => $version) {
+ $installedModule = $this->getModuleByArchiveNameAndVersion($archiveNameB, $version);
+ if (!$installedModule) {
+ continue;
+ }
+
+ $requiredConstraint = $this->getRequiredConstraint($installedModule, $archiveName);
+ if (!$requiredConstraint) {
+ continue;
+ }
+
+ $requiredConstraints[] = $requiredConstraint;
+ }
+
+ $constraint = Constraint::createConstraintFromConstraints($requiredConstraints);
+
+ return $constraint;
+ }
+
+ private function getModuleByArchiveNameAndVersion(string $archiveName, string $version): ?Module
+ {
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ return $moduleLoader->loadByArchiveNameAndVersion($archiveName, $version);
+ }
+
+ private function getRequiredConstraint(Module $installedModule, string $archiveName): string
+ {
+ $required = $installedModule->getRequire();
+ return $required[$archiveName] ?? '';
+ }
+}
diff --git a/src/Classes/DependencyManager/DependencyBuilderOld.php b/src/Classes/DependencyManager/DependencyBuilderOld.php
new file mode 100644
index 00000000..dd228936
--- /dev/null
+++ b/src/Classes/DependencyManager/DependencyBuilderOld.php
@@ -0,0 +1,303 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+use RobinTheHood\ModifiedModuleLoaderClient\Loader\ModuleLoader;
+use RobinTheHood\ModifiedModuleLoaderClient\Module;
+
+class DependencyBuilderOld
+{
+ private function logFile($value, $file)
+ {
+ @mkdir(__DIR__ . '/logs/');
+ $path = __DIR__ . '/logs/' . $file;
+ file_put_contents($path, json_encode($value, JSON_PRETTY_PRINT));
+ }
+
+ public function log($var)
+ {
+ print_r($var);
+ }
+
+ public function test()
+ {
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $module = $moduleLoader->loadLatestVersionByArchiveName('firstweb/multi-order');
+
+ if (!$module) {
+ die('Can not find base module');
+ }
+
+ $modules = [];
+ $this->getModuleList($modules, $module);
+ $this->logFile($modules, 'debug-log-modules.json');
+
+ $tree = $this->buildTreeByModuleRecursive($module);
+ $this->logFile($tree, 'debug-log-tree.json');
+
+ $tree = $this->buildTreeByModuleRecursiveConstraint($module);
+ $this->logFile($tree, 'debug-log-tree-constraint.json');
+
+ $flat = [];
+ $this->flattenTreeNew($tree, $flat);
+ $this->logFile($flat, 'debug-log-flat.json');
+
+ $combinations = [];
+ $this->allCombinations($flat, $combinations, 0);
+ $this->logFile($combinations, 'debug-log-combinations.json');
+
+ $combinations = array_reverse($combinations);
+ foreach ($combinations as $combination) {
+ $combination['composer/autoload'] = '1.0.0';
+ $result = $this->satisfiesComination($tree, $combination);
+ if ($result) {
+ $this->log($combination);
+ $this->log($result);
+ break;
+ }
+ }
+ }
+
+
+ private function allCombinations(&$flat, &$combinations, int $index, $combination = [])
+ {
+ $entry = array_values($flat)[$index] ?? [];
+
+ if (!$entry) {
+ $combinations[] = $combination;
+ return;
+ }
+
+ foreach ($entry['versions'] as $version) {
+ $newCombination = array_merge($combination, [$entry['archiveName'] => $version]);
+ $this->allCombinations($flat, $combinations, $index + 1, $newCombination);
+ }
+ }
+
+ private function satisfiesComination(&$tree, $combination): bool
+ {
+ // Expanded
+ $moduleResult = true;
+ foreach ($tree as &$module) {
+ // Module
+ $archiveName = $module['archiveName'];
+ $moduleVersions = &$module['versions'];
+ $selectedVersion = $combination[$archiveName];
+ $versionResult = false;
+ foreach ($moduleVersions as $version => &$moduleVersion) {
+ // Version
+ if ($version === $selectedVersion) {
+ $subTree = &$moduleVersion['requireExpanded'];
+ $versionResult = $this->satisfiesComination($subTree, $combination);
+ break;
+ }
+ }
+ $moduleResult = $moduleResult && $versionResult;
+ }
+ return $moduleResult;
+ }
+
+ private function flattenTreeNew($tree, &$flat)
+ {
+ if (!$tree) {
+ return;
+ }
+
+ foreach ($tree as $entry) {
+ $moduleEntry = [];
+ $moduleEntry["archiveName"] = $entry['archiveName'];
+ foreach ($entry['versions'] as $version => $entrys) {
+ $moduleEntry["versions"][] = $version;
+ $this->flattenTreeNew($entrys['requireExpanded'], $flat);
+ }
+ $flat[$entry['archiveName']] = $moduleEntry;
+ }
+ }
+
+
+ private function getModuleList(array &$modules, Module $module, int $depth = 0)
+ {
+ if ($depth >= 10) {
+ return;
+ }
+
+ $requireExpanded = [];
+ $require = $module->getRequire();
+ foreach ($require as $archiveName => $versionConstraint) {
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $moduleVersions = $moduleLoader->loadAllByArchiveNameAndConstraint($archiveName, $versionConstraint);
+ $versions = [];
+ foreach ($moduleVersions as $moduleA) {
+ $versions[] = $archiveName . ' : ' . $moduleA->getVersion();
+ }
+ $requireExpanded[$archiveName] = $versions;
+ }
+
+ $moduleAsArray = [
+ 'archiveName' => $module->getArchiveName(),
+ 'version' => $module->getVersion(),
+ 'require' => $require,
+ 'requireExpanded' => $requireExpanded
+ ];
+
+ if (!$this->containsModule($moduleAsArray, $modules)) {
+ $modules[] = $moduleAsArray;
+ }
+
+ $require = $module->getRequire();
+ foreach ($require as $archiveName => $versionConstraint) {
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $moduleVersions = $moduleLoader->loadAllByArchiveNameAndConstraint($archiveName, $versionConstraint);
+ // var_dump($archiveName);
+ // if ($archiveName == 'robinthehood/modified-std-module') {
+ // echo 'aaa';
+ // var_dump($moduleVersions);
+ // die();
+ // }
+ foreach ($moduleVersions as $moduleA) {
+ $this->getModuleList($modules, $moduleA, $depth + 1);
+ }
+ }
+ }
+
+ private function containsModule($moduleA, $modules)
+ {
+ foreach ($modules as $moduleB) {
+ if ($moduleA['archiveName'] !== $moduleB['archiveName']) {
+ continue;
+ }
+
+ if ($moduleA['version'] !== $moduleB['version']) {
+ continue;
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+
+ private function buildTreeByModuleRecursiveConstraint(Module $module, int $depth = 0): array
+ {
+ if ($depth >= 10) {
+ return [];
+ }
+
+ $require = $module->getRequire();
+
+ $requireModulesTree = [];
+ foreach ($require as $archiveName => $versionConstraint) {
+ // Modules to Entry
+ $entry = [];
+ $entry['archiveName'] = $archiveName;
+ $entry['versionConstraint'] = $versionConstraint;
+
+ // Versions
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $modules = $moduleLoader->loadAllByArchiveNameAndConstraint($archiveName, $versionConstraint);
+ foreach ($modules as $module) {
+ $entry['versions'][$module->getVersion()] = [
+ 'value' => false,
+ 'requireExpanded' => $this->buildTreeByModuleRecursiveConstraint($module, $depth + 1)
+ ];
+ }
+
+ $requireModulesTree[] = $entry;
+ }
+
+ return $requireModulesTree;
+ }
+
+ private function buildTreeByModuleRecursive(Module $module, int $depth = 0): array
+ {
+ if ($depth >= 10) {
+ return [];
+ }
+
+ $require = $module->getRequire();
+
+ $requireModulesTree = [];
+ foreach ($require as $archiveName => $versionConstraint) {
+ // Modules to Entry
+ $entry = [];
+ $entry['archiveName'] = $archiveName;
+ $entry['versionConstraint'] = $versionConstraint;
+
+ // Versions
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $modules = $moduleLoader->loadAllVersionsByArchiveName($archiveName);
+ foreach ($modules as $module) {
+ $entry['versions'][$module->getVersion()] = $this->buildTreeByModuleRecursive($module, $depth + 1);
+ }
+
+
+ $requireModulesTree[] = $entry;
+ }
+
+ return $requireModulesTree;
+ }
+
+
+ private function buildTreeByModuleRecursiveOld(Module $module, int $depth = 0): array
+ {
+ if ($depth >= 5) {
+ return [];
+ }
+
+ $require = $module->getRequire();
+
+ $requireModulesTree = [];
+ foreach ($require as $archiveName => $versionConstraint) {
+ $moduleLoader = ModuleLoader::getModuleLoader();
+
+ // An dieser Stelle wird zurzeit immer die neuste Variante ausgewählt.
+ // Eigentlich müssen hier alle Varianten die zu $versionConstraint passen
+ // ausgewählt und weiter verarbeitet werden. Zurzeit wird $versionConstraint
+ // aber nicht beachtet. Das muss bei einer späteren Version verbessert werden.
+ $selectedModule = $moduleLoader->loadLatestVersionByArchiveName($archiveName);
+
+ $entry = [];
+ if ($selectedModule) {
+ // $entry['module'] = $selectedModule;
+ $entry['archiveName'] = $selectedModule->getArchiveName();
+ $entry['requestedVersion'] = $versionConstraint;
+ $entry['selectedVersion'] = $selectedModule->getVersion();
+ $entry['require'] = [];
+ $requireModules = $this->buildTreeByModuleRecursive($selectedModule, ++$depth);
+
+ if ($requireModules) {
+ $entry['require'] = $requireModules;
+ }
+
+ $requireModulesTree[] = $entry;
+ }
+ }
+
+ return $requireModulesTree;
+ }
+
+ public function flattenTree($moduleTree, &$modules = null)
+ {
+ if (!$moduleTree) {
+ return;
+ }
+
+ foreach ($moduleTree as $entry) {
+ $modules[] = [
+ 'module' => $entry['module'],
+ 'requestedVersion' => $entry['requestedVersion'],
+ 'selectedVersion' => $entry['selectedVersion']
+ ];
+ $this->flattenTree($entry['require'], $modules);
+ }
+ }
+}
diff --git a/src/Classes/DependencyException.php b/src/Classes/DependencyManager/DependencyException.php
similarity index 83%
rename from src/Classes/DependencyException.php
rename to src/Classes/DependencyManager/DependencyException.php
index 62305926..e2f25960 100644
--- a/src/Classes/DependencyException.php
+++ b/src/Classes/DependencyManager/DependencyException.php
@@ -11,7 +11,7 @@
* file that was distributed with this source code.
*/
-namespace RobinTheHood\ModifiedModuleLoaderClient;
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
class DependencyException extends \LogicException
{
diff --git a/src/Classes/DependencyManager/DependencyManager.php b/src/Classes/DependencyManager/DependencyManager.php
new file mode 100644
index 00000000..f5ef6721
--- /dev/null
+++ b/src/Classes/DependencyManager/DependencyManager.php
@@ -0,0 +1,136 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\Combination;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\CombinationSatisfyerResult;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\DependencyBuilder;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\SystemSetFactory;
+use RobinTheHood\ModifiedModuleLoaderClient\Module;
+use RobinTheHood\ModifiedModuleLoaderClient\Loader\ModuleLoader;
+use RobinTheHood\ModifiedModuleLoaderClient\Semver\Comparator;
+use RobinTheHood\ModifiedModuleLoaderClient\Semver\Parser;
+
+class DependencyManager
+{
+ protected $comparator;
+
+ public function __construct()
+ {
+ $this->comparator = new Comparator(new Parser());
+ }
+
+ /**
+ * Liefert eine Liste mit allen Modulen aus $selectedModules, die das Modul
+ * $module verwenden.
+ *
+ * @param Module[] $selectedModules
+ */
+ public function getUsedByEntrys(Module $module, array $selectedModules): array
+ {
+ $usedByEntrys = [];
+ foreach ($selectedModules as $selectedModule) {
+ foreach ($selectedModule->getRequire() as $archiveName => $version) {
+ if ($archiveName == $module->getArchiveName()) {
+ $usedByEntrys[] = [
+ 'module' => $selectedModule,
+ 'requiredVersion' => $version
+ ];
+ }
+ }
+ }
+ return $usedByEntrys;
+ }
+
+ /**
+ * @param Combination $combination
+ *
+ * @return Module[]
+ */
+ public function getAllModulesFromCombination(Combination $combination): array
+ {
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $moduleLoader->resetCache();
+
+ $modules = [];
+ foreach ($combination->strip()->getAll() as $archiveName => $version) {
+ $module = $moduleLoader->loadByArchiveNameAndVersion($archiveName, $version);
+ if (!$module) {
+ throw new DependencyException('Can not find Module ' . $archiveName . ' in version ' . $version);
+ }
+ $modules[] = $module;
+ }
+ return $modules;
+ }
+
+ /**
+ * @param Module $module
+ * @param string[] $doNotCheck
+ *
+ * @return CombinationSatisfyerResult
+ */
+ public function canBeInstalled(Module $module, array $doNotCheck = []): CombinationSatisfyerResult
+ {
+ $systemSetFactory = new SystemSetFactory();
+ $systemSet = $systemSetFactory->getSystemSet();
+
+ foreach ($doNotCheck as $name) {
+ $systemSet->remove($name);
+ }
+
+ $dependencyBuilder = new DependencyBuilder();
+ $combinationSatisfyerResult = $dependencyBuilder->satisfies(
+ $module->getArchiveName(),
+ $module->getVersion(),
+ $systemSet
+ );
+
+ if ($combinationSatisfyerResult->result === CombinationSatisfyerResult::RESULT_COMBINATION_NOT_FOUND) {
+ throw new DependencyException(
+ "Can not install module {$module->getArchiveName()} in version {$module->getVersion()} "
+ . "because there are conflicting version contraints. "
+ . "Perhaps you have installed a module that requires a different version, "
+ . "or there is no compatible combination of dependencies. "
+ . " The following combination is required: {$combinationSatisfyerResult->failLog}"
+ );
+ }
+
+ // $modules = $this->getAllModulesFromCombination($combinationSatisfyerResult->foundCombination);
+ // $this->canBeInstalledTestChanged($module, $modules);
+
+ return $combinationSatisfyerResult;
+ }
+
+ /**
+ * Testet, ob das Modul in $module installiert werden kann, ob das Modul $module
+ * selbst oder eine Abhängigkeit in $modules im Status 'changed' ist.
+ *
+ * @param Module[] $modules
+ */
+ private function canBeInstalledTestChanged(Module $module, array $modules): void
+ {
+ $module = $module->getInstalledVersion();
+ if ($module && $module->isInstalled() && $module->isChanged()) {
+ $a = $module->getArchiveName();
+ throw new DependencyException("Module $a can not be installed because the Module has changes");
+ }
+
+ foreach ($modules as $module) {
+ if ($module && $module->isInstalled() && $module->isChanged()) {
+ $a = $module->getArchiveName();
+ throw new DependencyException("Required Module $a can not be installed because the Module has changes");
+ }
+ }
+ }
+}
diff --git a/src/Classes/DependencyManager.php b/src/Classes/DependencyManager/DependencyManagerOld.php
similarity index 77%
rename from src/Classes/DependencyManager.php
rename to src/Classes/DependencyManager/DependencyManagerOld.php
index 8ccde429..bb391a42 100644
--- a/src/Classes/DependencyManager.php
+++ b/src/Classes/DependencyManager/DependencyManagerOld.php
@@ -11,15 +11,19 @@
* file that was distributed with this source code.
*/
-namespace RobinTheHood\ModifiedModuleLoaderClient;
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\Combination;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\CombinationSatisfyerResult;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\DependencyBuilder;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\SystemSetFactory;
use RobinTheHood\ModifiedModuleLoaderClient\Module;
use RobinTheHood\ModifiedModuleLoaderClient\Loader\LocalModuleLoader;
use RobinTheHood\ModifiedModuleLoaderClient\Loader\ModuleLoader;
use RobinTheHood\ModifiedModuleLoaderClient\Semver\Comparator;
use RobinTheHood\ModifiedModuleLoaderClient\Semver\Parser;
-class DependencyManager
+class DependencyManagerOld
{
protected $comparator;
@@ -53,20 +57,60 @@ public function getAllModules($module): array
return $modules;
}
- public function canBeInstalled(Module $module): void
+ /**
+ * @param Combination $combination
+ *
+ * @return Module[]
+ */
+ public function getAllModulesFromCombination(Combination $combination): array
{
- $modules = $this->getAllModules($module);
- $modules[] = $module;
- foreach ($modules as $module) {
- $this->canBeInstalledTestRequiers($module, $modules);
- $this->canBeInstalledTestSelected($module, $modules);
- $this->canBeInstalledTestInstalled($module);
- $this->canBeInstalledTestChanged($module, $modules);
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $moduleLoader->resetCache();
+
+ $modules = [];
+ foreach ($combination->strip()->getAll() as $archiveName => $version) {
+ $module = $moduleLoader->loadByArchiveNameAndVersion($archiveName, $version);
+ if (!$module) {
+ throw new DependencyException('Can not find Module ' . $archiveName . ' in version ' . $version);
+ }
+ $modules[] = $module;
+ }
+ return $modules;
+ }
+
+ /**
+ * @param Module $module
+ *
+ * @return combinationSatisfyerResult
+ */
+ public function canBeInstalled(Module $module): CombinationSatisfyerResult
+ {
+ // $modules = $this->getAllModules($module);
+ // $modules[] = $module;
+ // foreach ($modules as $subModule) {
+ // // $this->canBeInstalledTestRequiers($module, $modules);
+ // // $this->canBeInstalledTestSelected($module, $modules);
+ // // $this->canBeInstalledTestInstalled($module);
+ // $this->canBeInstalledTestChanged($subModule, $modules);
+ // }
+
+ $systemSetFactory = new SystemSetFactory();
+ $systemSet = $systemSetFactory->getSystemSet();
+
+ $dependencyBuilder = new DependencyBuilder();
+ $combinationSatisfyerResult = $dependencyBuilder->satisfies($module->getArchiveName(), $module->getVersion(), $systemSet);
+ if (!$combinationSatisfyerResult->foundCombination) {
+ throw new DependencyException("Can not install modul");
}
+
+ $modules = $this->getAllModulesFromCombination($combinationSatisfyerResult->foundCombination);
+ $this->canBeInstalledTestChanged($module, $modules);
+
+ return $combinationSatisfyerResult;
}
/**
- * Test ob das Modul in $module installiert werden kann, ob das Modul $module
+ * Testet, ob das Modul in $module installiert werden kann, ob das Modul $module
* selbst oder eine Abhängigkeit in $modules im Status 'changed' ist.
*
* @param Module[] $modules
@@ -155,8 +199,6 @@ private function canBeInstalledTestRequiers(Module $module, array $selectedModul
}
}
-
-
/**
* Liefert eine Liste mit allen Modulen aus $selectedModules, die das Modul
* $module verwenden.
diff --git a/src/Classes/DependencyManager/FailLog.php b/src/Classes/DependencyManager/FailLog.php
new file mode 100644
index 00000000..79077e81
--- /dev/null
+++ b/src/Classes/DependencyManager/FailLog.php
@@ -0,0 +1,98 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class FailLog
+{
+ public const TYPE_UNFAIL = 0;
+ public const TYPE_FAIL = 1;
+
+ /** @var array */
+ private $entries = [];
+
+ /**
+ * @param ModuleTree[] $moduleTreeChain
+ */
+ public function unfail(
+ array $moduleTreeChain,
+ string $archiveName,
+ string $version = '',
+ string $constraint = ''
+ ): void {
+ $key = $this->createKey($moduleTreeChain, $archiveName);
+
+ $this->entries[$key] = [
+ 'type' => self::TYPE_UNFAIL,
+ 'moduleTreeChain' => $moduleTreeChain,
+ 'archiveName' => $version,
+ 'version' => $version,
+ 'constraint' => $constraint
+ ];
+ }
+
+ /**
+ * @param ModuleTree[] $moduleTreeChain
+ */
+ public function fail(
+ array $moduleTreeChain,
+ string $archiveName,
+ string $version = '',
+ string $constraint = ''
+ ): void {
+ $key = $this->createKey($moduleTreeChain, $archiveName);
+
+ if (!array_key_exists($key, $this->entries)) {
+ $this->entries[$key] = [
+ 'moduleTreeChain' => $moduleTreeChain,
+ 'type' => self::TYPE_FAIL,
+ 'archiveName' => $version,
+ 'version' => $version,
+ 'constraint' => $constraint
+ ];
+ }
+ }
+
+ /**
+ * @param ModuleTree[] $moduleTreeChain
+ */
+ private function createKey(array $moduleTreeChain, string $archiveName): string
+ {
+ $key = '';
+ foreach ($moduleTreeChain as $moduleTree) {
+ if ($key) {
+ $key .= ' > ';
+ }
+ $key .= $moduleTree->archiveName;
+ }
+ return $key . ' > ' . $archiveName;
+ }
+
+ public function __toString(): string
+ {
+ $strings = [];
+ foreach ($this->entries as $key => $entry) {
+ if ($entry['type'] !== self::TYPE_FAIL) {
+ continue;
+ }
+ $constraint = $entry['constraint'];
+ $version = $entry['version'];
+ if ($constraint) {
+ $strings[] = "$key $constraint";
+ } else {
+ $strings[] = "$key $version";
+ }
+ }
+ return implode('
', $strings);
+ }
+}
diff --git a/src/Classes/DependencyManager/FlatEntry.php b/src/Classes/DependencyManager/FlatEntry.php
new file mode 100644
index 00000000..9aa3aa75
--- /dev/null
+++ b/src/Classes/DependencyManager/FlatEntry.php
@@ -0,0 +1,34 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class FlatEntry
+{
+ /** @var string */
+ public $archiveName;
+
+ /** @var array version strings */
+ public $versions = [];
+
+ public function combine(FlatEntry $flatEntry): void
+ {
+ if ($this->archiveName !== $flatEntry->archiveName) {
+ throw new DependencyException("Cant combine FlatEntry {$this->archiveName} and {$flatEntry->archiveName}");
+ }
+
+ $this->versions = array_merge($this->versions, $flatEntry->versions);
+ $this->versions = array_unique($this->versions);
+ $this->versions = array_values($this->versions);
+ }
+}
diff --git a/src/Classes/DependencyManager/FlatEntryBuilder.php b/src/Classes/DependencyManager/FlatEntryBuilder.php
new file mode 100644
index 00000000..5aa31c83
--- /dev/null
+++ b/src/Classes/DependencyManager/FlatEntryBuilder.php
@@ -0,0 +1,203 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class FlatEntryBuilder
+{
+ /**
+ * @param ModuleTree[] $moduleTrees
+ *
+ * @return FlatEntry[]
+ */
+ public function buildListFromModuleTrees(array $moduleTrees): array
+ {
+ $flatEntries = [];
+ $this->buildModuleFlatEntriesByModuleTrees($moduleTrees, $flatEntries);
+ return $flatEntries;
+ }
+
+ /**
+ * @param ModuleTree $moduleTree
+ *
+ * @return FlatEntry[]
+ */
+ public function buildListFromModuleTree(ModuleTree $moduleTree): array
+ {
+ $flatEntries = [];
+ $this->buildModuleFlatEntriesByModuleTree($moduleTree, $flatEntries);
+ return $flatEntries;
+ }
+
+ /**
+ * @param FlatEntry[] $flatEntries
+ * @param string $archiveName
+ * @param FlatEntry $flatEntry
+ */
+ private function addFlatEntry(array &$flatEntries, string $archiveName, FlatEntry $flatEntry): void
+ {
+ if (array_key_exists($archiveName, $flatEntries)) {
+ $flatEntries[$archiveName]->combine($flatEntry);
+ } else {
+ $flatEntries[$archiveName] = $flatEntry;
+ }
+ }
+
+ /**
+ * @param ModuleTree[] $moduleTrees
+ * @param FlatEntry[] $flatEntries
+ */
+ private function buildModuleFlatEntriesByModuleTrees(array $moduleTrees, array &$flatEntries): void
+ {
+ if (!$moduleTrees) {
+ return;
+ }
+
+ foreach ($moduleTrees as $moduleTree) {
+ $flatEntry = new FlatEntry();
+ $flatEntry->archiveName = $moduleTree->archiveName;
+ foreach ($moduleTree->moduleVersions as $moduleVersion) {
+ $flatEntry->versions[] = $moduleVersion->version;
+ $this->buildModuleFlatEntriesByModuleTrees($moduleVersion->require, $flatEntries);
+ }
+ $this->addFlatEntry($flatEntries, $moduleTree->archiveName, $flatEntry);
+ }
+ }
+
+ /**
+ * @param ModuleTree $moduleTree
+ * @param FlatEntry[] $flatEntries
+ */
+ private function buildModuleFlatEntriesByModuleTree(ModuleTree $moduleTree, array &$flatEntries): void
+ {
+ $flatEntry = new FlatEntry();
+ $flatEntry->archiveName = $moduleTree->archiveName;
+ foreach ($moduleTree->moduleVersions as $moduleVersion) {
+ $flatEntry->versions[] = $moduleVersion->version;
+ foreach ($moduleVersion->require as $moduleTree) {
+ $this->buildModuleFlatEntriesByModuleTree($moduleTree, $flatEntries);
+ }
+ }
+ $this->addFlatEntry($flatEntries, $flatEntry->archiveName, $flatEntry);
+ }
+
+
+
+ /**
+ * @param FlatEntry[] $flatEntries
+ * @param SystemSet $systemSet
+ * @return FlatEntry[]
+ */
+ public function fitSystemSet(array $flatEntries, SystemSet $systemSet): array
+ {
+ $flatEntries = $this->removeFlatEntriesBySystemSet($flatEntries, $systemSet);
+ $flatEntries = $this->removeFlatEntriesWithNoVersion($flatEntries);
+ $flatEntries = $this->addFlatEntriesBySystemSet($flatEntries, $systemSet);
+ return $flatEntries;
+ }
+
+ /**
+ * @param FlatEntry[] $flatEntries
+ * @param SystemSet $systemSet
+ * @return FlatEntry[]
+ */
+ public function addFlatEntriesBySystemSet(array $flatEntries, SystemSet $systemSet): array
+ {
+ foreach ($systemSet->getAll() as $archiveName => $version) {
+ if ($this->findFatEntryByArchiveName($archiveName, $flatEntries)) {
+ continue;
+ }
+ $flatEntry = new FlatEntry();
+ $flatEntry->archiveName = $archiveName;
+ $flatEntry->versions = [$version];
+ $flatEntries[] = $flatEntry;
+ }
+ return $flatEntries;
+ }
+
+ /**
+ * @param string $archiveName
+ * @param FlatEntry[] $flatEntries
+ *
+ * @return ?FlatEntry
+ */
+ private function findFatEntryByArchiveName(string $archiveName, array $flatEntries): ?FlatEntry
+ {
+ foreach ($flatEntries as $flatEntry) {
+ if ($flatEntry->archiveName === $archiveName) {
+ return $flatEntry;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param FlatEntry[] $flatEntries
+ *
+ * @return FlatEntry[]
+ */
+ public function removeFlatEntriesWithNoVersion(array $flatEntries): array
+ {
+ $filteredFlatEntires = [];
+ foreach ($flatEntries as $flatEntry) {
+ if (!$flatEntry->versions) {
+ continue;
+ }
+ $filteredFlatEntires[] = $flatEntry;
+ }
+ return $filteredFlatEntires;
+ }
+
+ /**
+ * @param FlatEntry[] $moduleFlatTreeEntries
+ * @param SystemSet $systemSet
+ * @return FlatEntry[]
+ */
+ public function removeFlatEntriesBySystemSet(array $moduleFlatTreeEntries, SystemSet $systemSet): array
+ {
+ foreach ($systemSet->getAll() as $archiveName => $version) {
+ $versions = [$version];
+ $moduleFlatTreeEntries = $this->removeModuleFlatEnty($moduleFlatTreeEntries, $archiveName, $versions);
+ }
+ return $moduleFlatTreeEntries;
+ }
+
+ /**
+ * @param FlatEntry[] $moduleFlatTreeEntries
+ * @param string $archiveName
+ * @param string[] $versions
+ *
+ * @return FlatEntry[]
+ */
+ private function removeModuleFlatEnty(array $moduleFlatTreeEntries, string $archiveName, array $versions): array
+ {
+ $filteredModuleFlatTreeEntries = [];
+ foreach ($moduleFlatTreeEntries as $moduleFlatTreeEntry) {
+ if ($moduleFlatTreeEntry->archiveName !== $archiveName) {
+ $filteredModuleFlatTreeEntries[$moduleFlatTreeEntry->archiveName] = $moduleFlatTreeEntry;
+ continue;
+ }
+
+ $fileredVersions = [];
+ foreach ($moduleFlatTreeEntry->versions as $versionStr) {
+ if (!in_array($versionStr, $versions)) {
+ continue;
+ }
+ $fileredVersions[] = $versionStr;
+ }
+ $newModuleFlatTreeEntry = new FlatEntry();
+ $newModuleFlatTreeEntry->archiveName = $moduleFlatTreeEntry->archiveName;
+ $newModuleFlatTreeEntry->versions = $fileredVersions;
+ $filteredModuleFlatTreeEntries[$moduleFlatTreeEntry->archiveName] = $newModuleFlatTreeEntry;
+ }
+ return $filteredModuleFlatTreeEntries;
+ }
+}
diff --git a/src/Classes/DependencyManager/ModuleTree.php b/src/Classes/DependencyManager/ModuleTree.php
new file mode 100644
index 00000000..03950c3c
--- /dev/null
+++ b/src/Classes/DependencyManager/ModuleTree.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class ModuleTree
+{
+ /** @var string */
+ public $archiveName = '';
+
+ /** @var string version constraint string */
+ public $versionConstraint = '';
+
+ /** @var ModuleVersion[] */
+ public $moduleVersions = [];
+}
diff --git a/src/Classes/DependencyManager/ModuleTreeBuilder.php b/src/Classes/DependencyManager/ModuleTreeBuilder.php
new file mode 100644
index 00000000..d13a771c
--- /dev/null
+++ b/src/Classes/DependencyManager/ModuleTreeBuilder.php
@@ -0,0 +1,191 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+use RobinTheHood\ModifiedModuleLoaderClient\Loader\ModuleLoader;
+use RobinTheHood\ModifiedModuleLoaderClient\Module;
+use RobinTheHood\ModifiedModuleLoaderClient\ModuleFilter;
+use RobinTheHood\ModifiedModuleLoaderClient\ModuleSorter;
+
+class ModuleTreeBuilder
+{
+ /** @var array */
+ private $moduleCache = [];
+
+ /**
+ * @param string $archiveName
+ * @param string $versionConstraint
+ * @return Module[]
+ */
+ private function loadAllByArchiveNameAndConstraint(string $archiveName, string $versionConstraint): array
+ {
+ $modules = $this->moduleCache[$archiveName] ?? [];
+ if (!$modules) {
+ $moduleLoader = ModuleLoader::getModuleLoader();
+ $modules = $moduleLoader->loadAllVersionsByArchiveName($archiveName);
+ $modules = ModuleSorter::sortByVersion($modules);
+ $this->moduleCache[$archiveName] = $modules;
+ }
+
+ return ModuleFilter::filterByVersionConstrain($modules, $versionConstraint);
+ }
+
+ /**
+ * @param Module $Module
+ * @param int $depth
+ * @return ModuleTree[]
+ */
+ public function buildListByConstraints(Module $module, int $depth = 0): array
+ {
+ if ($depth >= 10) {
+ return [];
+ }
+
+ $require = $module->getRequire();
+
+ $moduleTrees = [];
+
+ $this->addExtraDependencies($module, $moduleTrees);
+
+ foreach ($require as $archiveName => $versionConstraint) {
+ // Modules to Entry
+ $moduleTree = new ModuleTree();
+ $moduleTree->archiveName = $archiveName;
+ $moduleTree->versionConstraint = $versionConstraint;
+
+ // Fetch Versions
+ $modules = $this->loadAllByArchiveNameAndConstraint($archiveName, $versionConstraint);
+
+ // VersionList
+ foreach ($modules as $module) {
+ $moduleVersion = new ModuleVersion();
+ $moduleVersion->version = $module->getVersion();
+ $moduleVersion->require = $this->buildListByConstraints($module, $depth + 1);
+ $moduleTree->moduleVersions[$moduleVersion->version] = $moduleVersion;
+ }
+
+ $moduleTrees[] = $moduleTree;
+ }
+
+ return $moduleTrees;
+ }
+
+ /**
+ * @param string $archiveName
+ * @param string $versionConstraint
+ * @param int $depth
+ */
+ public function buildByConstraints(string $archiveName, string $versionConstraint, int $depth = 0): ModuleTree
+ {
+ $moduleTree = new ModuleTree();
+ $moduleTree->archiveName = $archiveName;
+ $moduleTree->versionConstraint = $versionConstraint;
+
+ $modules = $this->loadAllByArchiveNameAndConstraint($archiveName, $versionConstraint);
+
+ $moduleVersions = [];
+ foreach ($modules as $module) {
+ // Context: Module
+ $moduleVersion = new ModuleVersion();
+ $moduleVersion->version = $module->getVersion();
+
+ if ($depth < 10) {
+ $this->addExtraDependencies($module, $moduleVersion->require);
+
+ $require = $module->getRequire();
+ foreach ($require as $archiveName => $versionConstraint) {
+ // Context: require
+ $moduleVersion->require[] = $this->buildByConstraints($archiveName, $versionConstraint, $depth + 1);
+ }
+ }
+ $moduleVersions[] = $moduleVersion;
+ }
+ $moduleTree->moduleVersions = $moduleVersions;
+
+ return $moduleTree;
+ }
+
+ /**
+ * @param Module $module
+ * @param ModuleTree[] $moduleTrees
+ */
+ private function addExtraDependencies(Module $module, array &$moduleTrees): void
+ {
+ $this->addTreeModified($module, $moduleTrees);
+ $this->addTreePhp($module, $moduleTrees);
+ $this->addTreeMmlc($module, $moduleTrees);
+ }
+
+ /**
+ * @param Module $module
+ * @param ModuleTree[] $moduleTrees
+ */
+ private function addTreeModified(Module $module, array &$moduleTrees): void
+ {
+ if (!$module->getModifiedCompatibility()) {
+ return;
+ }
+
+ $moduleVersions = [];
+ foreach ($module->getModifiedCompatibility() as $modifiedVersion) {
+ $moduleVersion = new ModuleVersion();
+ $moduleVersion->version = $modifiedVersion;
+ $moduleVersion->require = [];
+ $moduleVersions[] = $moduleVersion;
+ }
+
+ $moduleTree = new ModuleTree();
+ $moduleTree->archiveName = 'modified';
+ $moduleTree->versionConstraint = '';
+ $moduleTree->moduleVersions = array_reverse($moduleVersions);
+
+ $moduleTrees[] = $moduleTree;
+ }
+
+ /**
+ * @param Module $module
+ * @param ModuleTree[] $moduleTrees
+ */
+ private function addTreePhp(Module $module, array &$moduleTrees): void
+ {
+ if (!$module->getPhp()) {
+ return;
+ }
+
+ $moduleTree = new ModuleTree();
+ $moduleTree->archiveName = 'php';
+ $moduleTree->versionConstraint = $module->getPhp()['version'] ?? '^7.4.0 || ^8.0.0';
+ $moduleTree->moduleVersions = [];
+
+ $moduleTrees[] = $moduleTree;
+ }
+
+ /**
+ * @param Module $module
+ * @param ModuleTree[] $moduleTrees
+ */
+ private function addTreeMmlc(Module $module, array &$moduleTrees): void
+ {
+ if (!$module->getMmlc()) {
+ return;
+ }
+
+ $moduleTree = new ModuleTree();
+ $moduleTree->archiveName = 'mmlc';
+ $moduleTree->versionConstraint = $module->getMmlc()['version'] ?? '^1.19.0';
+ $moduleTree->moduleVersions = [];
+
+ $moduleTrees[] = $moduleTree;
+ }
+}
diff --git a/src/Classes/DependencyManager/ModuleVersion.php b/src/Classes/DependencyManager/ModuleVersion.php
new file mode 100644
index 00000000..619bdfcb
--- /dev/null
+++ b/src/Classes/DependencyManager/ModuleVersion.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class ModuleVersion
+{
+ /** @var string */
+ public $version = '';
+
+ /** @var ModuleTree[] **/
+ public $require = [];
+}
diff --git a/src/Classes/DependencyManager/SystemSet.php b/src/Classes/DependencyManager/SystemSet.php
new file mode 100644
index 00000000..b41941c6
--- /dev/null
+++ b/src/Classes/DependencyManager/SystemSet.php
@@ -0,0 +1,85 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+class SystemSet
+{
+ /**
+ * SystemNames: mmlc, modified, php
+ *
+ * An array entry looks like
+ * array
+ * or
+ * array
+ *
+ * @var array
+ **/
+ private $systems = [];
+
+ /**
+ * @param array $sytems
+ */
+ public function set(array $sytems): void
+ {
+ $this->systems = $sytems;
+ }
+
+ public function add(string $name, string $versionString): void
+ {
+ $this->systems[$name] = $versionString;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getArchiveNames(): array
+ {
+ $archiveNames = array_values(array_filter(array_keys($this->systems), function ($name) {
+ return strpos($name, '/') !== false;
+ }));
+ return $archiveNames;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAll(): array
+ {
+ return $this->systems;
+ }
+
+ /**
+ * @return array
+ */
+ public function getArchives(): array
+ {
+ $archives = [];
+ foreach ($this->systems as $name => $version) {
+ if (!strpos($name, '/')) {
+ continue;
+ }
+ $archives[$name] = $version;
+ }
+ return $archives;
+ }
+
+ public function remove(string $name): void
+ {
+ if (!array_key_exists($name, $this->systems)) {
+ return;
+ }
+
+ unset($this->systems[$name]);
+ }
+}
diff --git a/src/Classes/DependencyManager/SystemSetFactory.php b/src/Classes/DependencyManager/SystemSetFactory.php
new file mode 100644
index 00000000..14e7b34f
--- /dev/null
+++ b/src/Classes/DependencyManager/SystemSetFactory.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
+
+use RobinTheHood\ModifiedModuleLoaderClient\App;
+use RobinTheHood\ModifiedModuleLoaderClient\Loader\LocalModuleLoader;
+use RobinTheHood\ModifiedModuleLoaderClient\ShopInfo as ModifiedModuleLoaderClientShopInfo;
+
+class SystemSetFactory
+{
+ public function getSystemSet(): SystemSet
+ {
+ $systemSet = new SystemSet();
+ $systemSet->add('modified', ModifiedModuleLoaderClientShopInfo::getModifiedVersion());
+ $systemSet->add('php', phpversion());
+ $systemSet->add('mmlc', App::getMmlcVersion());
+
+ $moduleLoader = LocalModuleLoader::getModuleLoader();
+ $modules = $moduleLoader->loadAllInstalledVersions();
+ foreach ($modules as $module) {
+ $systemSet->add($module->getArchiveName(), $module->getVersion());
+ }
+ return $systemSet;
+ }
+}
diff --git a/src/Classes/IndexController.php b/src/Classes/IndexController.php
index 0721258a..4c99f36e 100644
--- a/src/Classes/IndexController.php
+++ b/src/Classes/IndexController.php
@@ -21,6 +21,8 @@
use RobinTheHood\ModifiedModuleLoaderClient\Category;
use RobinTheHood\ModifiedModuleLoaderClient\SendMail;
use RobinTheHood\ModifiedModuleLoaderClient\Config;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\DependencyException;
+use RuntimeException;
class IndexController extends Controller
{
@@ -317,14 +319,17 @@ public function invokeInstall()
try {
$moduleInstaller = new ModuleInstaller();
- //$moduleInstaller->install($module);
- //$moduleInstaller->installDependencies($module);
$moduleInstaller->installWithDependencies($module);
} catch (DependencyException $e) {
Notification::pushFlashMessage([
'text' => $e->getMessage(),
'type' => 'error'
]);
+ } catch (RuntimeException $e) {
+ Notification::pushFlashMessage([
+ 'text' => $e->getMessage(),
+ 'type' => 'error'
+ ]);
}
return $this->redirectRef($archiveName, $module->getVersion());
@@ -356,7 +361,7 @@ private function invokeRevertChanges()
'text' => $e->getMessage(),
'type' => 'error'
]);
- } catch (\RuntimeException $e) {
+ } catch (RuntimeException $e) {
Notification::pushFlashMessage([
'text' => $e->getMessage(),
'type' => 'error'
@@ -392,6 +397,11 @@ public function invokeUninstall()
'text' => $e->getMessage(),
'type' => 'error'
]);
+ } catch (RuntimeException $e) {
+ Notification::pushFlashMessage([
+ 'text' => $e->getMessage(),
+ 'type' => 'error'
+ ]);
}
return $this->redirectRef($archiveName, $module->getVersion());
@@ -425,6 +435,11 @@ public function invokeUpdate()
'text' => $e->getMessage(),
'type' => 'error'
]);
+ } catch (RuntimeException $e) {
+ Notification::pushFlashMessage([
+ 'text' => $e->getMessage(),
+ 'type' => 'error'
+ ]);
}
if (!$newModule) {
@@ -502,13 +517,17 @@ public function invokeLoadAndInstall()
try {
$moduleInstaller = new ModuleInstaller();
- $moduleInstaller->install($module);
- $moduleInstaller->installDependencies($module);
+ $moduleInstaller->installWithDependencies($module);
} catch (DependencyException $e) {
Notification::pushFlashMessage([
'text' => $e->getMessage(),
'type' => 'error'
]);
+ } catch (RuntimeException $e) {
+ Notification::pushFlashMessage([
+ 'text' => $e->getMessage(),
+ 'type' => 'error'
+ ]);
}
return $this->redirectRef($archiveName, $module->getVersion());
@@ -540,6 +559,11 @@ public function invokeUnloadLocalModule()
'text' => $e->getMessage(),
'type' => 'error'
]);
+ } catch (RuntimeException $e) {
+ Notification::pushFlashMessage([
+ 'text' => $e->getMessage(),
+ 'type' => 'error'
+ ]);
}
return $this->redirect('/');
diff --git a/src/Classes/Loader/ModuleLoader.php b/src/Classes/Loader/ModuleLoader.php
index 35e5a802..27ff4814 100644
--- a/src/Classes/Loader/ModuleLoader.php
+++ b/src/Classes/Loader/ModuleLoader.php
@@ -29,6 +29,23 @@ public static function getModuleLoader(): ModuleLoader
return self::$moduleLoader;
}
+ /**
+ * Resets / deletes allready loaded modules data. For examplae because
+ * during the script runtime the amount of modules or data of modules
+ * changed and the LocalModuleLoader does not give the latest module
+ * informations.
+ */
+ public function resetCache()
+ {
+ $this->cachedModules = null;
+
+ $localModuleLoader = LocalModuleLoader::getModuleLoader();
+ $localModuleLoader->resetCache();
+
+ $remoteModuleLoader = RemoteModuleLoader::getModuleLoader();
+ $remoteModuleLoader->resetCache();
+ }
+
/**
* Loads all local module version plus all latest remote module version.
*
diff --git a/src/Classes/Loader/RemoteModuleLoader.php b/src/Classes/Loader/RemoteModuleLoader.php
index 60f4e132..d3e57e44 100644
--- a/src/Classes/Loader/RemoteModuleLoader.php
+++ b/src/Classes/Loader/RemoteModuleLoader.php
@@ -45,6 +45,17 @@ public static function getModuleLoader(): RemoteModuleLoader
return self::$moduleLoader;
}
+ /**
+ * Resets / deletes allready loaded modules data. For examplae because
+ * during the script runtime the amount of modules or data of modules
+ * changed and the LocalModuleLoader does not give the latest module
+ * informations.
+ */
+ public function resetCache()
+ {
+ $this->cachedModules = null;
+ }
+
/**
* Loads all remote module versions.
*
diff --git a/src/Classes/Module.php b/src/Classes/Module.php
index 5b803f94..bf9a9a02 100644
--- a/src/Classes/Module.php
+++ b/src/Classes/Module.php
@@ -14,11 +14,11 @@
namespace RobinTheHood\ModifiedModuleLoaderClient;
use RobinTheHood\ModifiedModuleLoaderClient\App;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\DependencyManager;
use RobinTheHood\ModifiedModuleLoaderClient\ShopInfo;
use RobinTheHood\ModifiedModuleLoaderClient\FileInfo;
use RobinTheHood\ModifiedModuleLoaderClient\ModuleFilter;
use RobinTheHood\ModifiedModuleLoaderClient\ModuleInfo;
-use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
use RobinTheHood\ModifiedModuleLoaderClient\FileHasher\ChangedEntryCollection;
use RobinTheHood\ModifiedModuleLoaderClient\Loader\ModuleLoader;
use RobinTheHood\ModifiedModuleLoaderClient\Loader\LocalModuleLoader;
@@ -516,12 +516,9 @@ public function isCompatibleWithPhp(): bool
$phpVersionInstalled = phpversion();
$comparator = new Comparator(new Parser());
- return $comparator->satisfiesOr($phpVersionInstalled, $phpVersionContraint);
+ return $comparator->satisfies($phpVersionInstalled, $phpVersionContraint);
}
- /**
- * Version 1.20.0 is the last version without isCompatibleWithMmlc()
- */
public function isCompatibleWithMmlc(): bool
{
$mmlcVersionInstalled = App::getMmlcVersion();
@@ -530,13 +527,13 @@ public function isCompatibleWithMmlc(): bool
}
$mmlc = $this->getMmlc();
- $mmlcVersionContraint = $mmlc['version'] ?? '^1.20.0';
+ $mmlcVersionContraint = $mmlc['version'] ?? '';
if (!$mmlcVersionContraint) {
return true;
}
$comparator = new Comparator(new Parser());
- return $comparator->satisfiesOr($mmlcVersionInstalled, $mmlcVersionContraint);
+ return $comparator->satisfies($mmlcVersionInstalled, $mmlcVersionContraint);
}
public function getTemplateFiles($file): array
diff --git a/src/Classes/ModuleFactory.php b/src/Classes/ModuleFactory.php
index f8ad1b29..20691b0a 100644
--- a/src/Classes/ModuleFactory.php
+++ b/src/Classes/ModuleFactory.php
@@ -94,7 +94,7 @@ public static function createFromArray(array $array): Module
$module->setAutoload($autoload);
$module->setTags($array['tags'] ?? '');
$module->setPhp($array['php'] ?? []);
- $module->setMmlc($array['mmlc'] ?? []);
+ $module->setMmlc($array['mmlc'] ?? ['version' => App::getMmlcVersion()]);
// Module
$module->setLocalRootPath($array['localRootPath'] ?? '');
diff --git a/src/Classes/ModuleInstaller.php b/src/Classes/ModuleInstaller.php
index d8724dd6..c4e527fd 100644
--- a/src/Classes/ModuleInstaller.php
+++ b/src/Classes/ModuleInstaller.php
@@ -17,11 +17,13 @@
use RobinTheHood\ModifiedModuleLoaderClient\Archive;
use RobinTheHood\ModifiedModuleLoaderClient\Config;
use RobinTheHood\ModifiedModuleLoaderClient\FileInfo;
-use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager;
use RobinTheHood\ModifiedModuleLoaderClient\Api\V1\ApiRequest;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\Combination;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\DependencyManager;
use RobinTheHood\ModifiedModuleLoaderClient\Loader\LocalModuleLoader;
use RobinTheHood\ModifiedModuleLoaderClient\Helpers\FileHelper;
use RobinTheHood\ModifiedModuleLoaderClient\ModuleHasher\ModuleHashFileCreator;
+use RuntimeException;
class ModuleInstaller
{
@@ -49,7 +51,7 @@ public function pull(Module $module): bool
$archive = Archive::pullArchive($archiveUrl, $module->getArchiveName(), $module->getVersion());
$archive->untarArchive();
return true;
- } catch (\RuntimeException $e) {
+ } catch (RuntimeException $e) {
//Can not pull Archive
return false;
}
@@ -81,13 +83,37 @@ public function delete(Module $module)
}
}
- public function install(Module $module, $force = false): void
+ public function install(Module $module, bool $force = false): void
{
if (!$force) {
$dependencyManager = new DependencyManager();
$dependencyManager->canBeInstalled($module);
}
+ $this->internalInstall($module);
+ $this->createAutoloadFile();
+ }
+
+ public function installWithDependencies(Module $module): void
+ {
+ $dependencyManager = new DependencyManager();
+ $combinationSatisfyerResult = $dependencyManager->canBeInstalled($module, ['']);
+
+ if (!$combinationSatisfyerResult->foundCombination) {
+ throw new RuntimeException(
+ "Can not install module {$module->getArchiveName()} {$module->getVersion()} with dependencies. "
+ . "No possible combination of versions found"
+ );
+ }
+
+ $this->uninstall($module);
+ $this->internalInstall($module);
+ $this->internalInstallDependencies($module, $combinationSatisfyerResult->foundCombination);
+ $this->createAutoloadFile();
+ }
+
+ private function internalInstall(Module $module): void
+ {
// Install Source Files to Shop Root
$files = $module->getSrcFilePaths();
@@ -120,51 +146,129 @@ public function install(Module $module, $force = false): void
$moduleHashFileCreator = new ModuleHashFileCreator();
$moduleHashFile = $moduleHashFileCreator->createHashFile($module);
$moduleHashFile->writeTo($module->getHashPath());
-
- $this->createAutoloadFile();
}
- public function revertChanges(Module $module): void
+ private function internalPullAndInstall(Module $module): void
{
- if (!$module->isInstalled()) {
- throw new \RuntimeException('Can not revert changes because ' . $module->getArchiveName() . ' is not installed.');
+ if (!$module->isLoaded()) {
+ $this->pull($module);
+ }
+
+ $reloaded = $this->reload($module);
+
+ if (!$reloaded->isLoaded()) {
+ throw new RuntimeException(
+ "Can not pull and install module {$module->getArchiveName()} {$module->getVersion()}. "
+ . "Module is not loaded."
+ );
+ }
+
+ if ($reloaded->isInstalled()) {
+ return;
}
- $this->install($module, true);
+ $this->uninstall($module);
+ $this->internalInstall($module);
}
- public function installDependencies(Module $module): void
+ private function internalInstallDependencies(Module $parentModule, Combination $combination): void
{
$dependencyManager = new DependencyManager();
- $modules = $dependencyManager->getAllModules($module);
+ $modules = $dependencyManager->getAllModulesFromCombination($combination);
- foreach ($modules as $depModule) {
- if (!$depModule->isLoaded()) {
- $this->pull($depModule);
+ foreach ($modules as $module) {
+ if ($parentModule->getArchiveName() === $module->getArchiveName()) {
+ continue;
}
+ $this->internalPullAndInstall($module);
}
+ }
- $modules = $dependencyManager->getAllModules($module);
- foreach ($modules as $depModule) {
- $this->uninstall($depModule);
- if ($depModule->isLoaded() && !$depModule->isInstalled()) {
- $this->install($depModule);
- }
+ public function update(Module $module): ?Module
+ {
+ $installedModule = $module->getInstalledVersion();
+ $newModule = $module->getNewestVersion();
+
+ $dependencyManager = new DependencyManager();
+ $combinationSatisfyerResult = $dependencyManager->canBeInstalled($module);
+
+ if (!$combinationSatisfyerResult->foundCombination) {
+ throw new RuntimeException(
+ "Can not update module {$module->getArchiveName()} {$module->getVersion()}. "
+ . "No possible combination of versions found"
+ );
}
+ if ($installedModule) {
+ $this->uninstall($installedModule);
+ }
+
+ $this->pull($newModule);
+ $newModule = $this->reload($newModule);
+
+ $this->internalInstall($newModule);
$this->createAutoloadFile();
+
+ return $newModule;
}
- public function installWithDependencies(Module $module): void
+ public function updateWithDependencies(Module $module): ?Module
{
+ $installedModule = $module->getInstalledVersion();
+ $newModule = $module->getNewestVersion();
+
$dependencyManager = new DependencyManager();
- $dependencyManager->canBeInstalled($module);
+ $combinationSatisfyerResult = $dependencyManager->canBeInstalled($module);
+
+ if (!$combinationSatisfyerResult->foundCombination) {
+ throw new RuntimeException(
+ "Can not update module {$module->getArchiveName()} {$module->getVersion()} with dependencies. "
+ . "No possible combination of versions found"
+ );
+ }
+
+ if ($installedModule) {
+ $this->uninstall($installedModule);
+ }
+
+ $this->pull($newModule);
+ $newModule = $this->reload($newModule);
+
+ $this->internalInstall($newModule);
+ $this->internalInstallDependencies($newModule, $combinationSatisfyerResult->foundCombination);
+ $this->createAutoloadFile();
- $this->install($module);
- $this->installDependencies($module);
+ return $newModule;
+ }
+
+ private function reload(Module $module): Module
+ {
+ $moduleLoader = new LocalModuleLoader();
+ $moduleLoader->resetCache();
+ $reloadedModule = $moduleLoader->loadByArchiveNameAndVersion(
+ $module->getArchiveName(),
+ $module->getVersion()
+ );
+
+ if (!$reloadedModule) {
+ throw new RuntimeException("Can not reload module {$module->getArchiveName()} {$module->getVersion()}");
+ }
+
+ return $reloadedModule;
+ }
+
+ public function revertChanges(Module $module): void
+ {
+ if (!$module->isInstalled()) {
+ throw new RuntimeException(
+ "Can not revert changes because {$module->getArchiveName()} {$module->getVersion()} is not installed."
+ );
+ }
+
+ $this->internalInstall($module);
}
- public function createAutoloadFile(): void
+ private function createAutoloadFile(): void
{
$localModuleLoader = LocalModuleLoader::getModuleLoader();
$localModuleLoader->resetCache();
@@ -207,18 +311,28 @@ public function createAutoloadFile(): void
\file_put_contents(App::getShopRoot() . '/vendor-mmlc/autoload.php', $template);
}
- //TODO: Better return void type an thorw exception at error
- public function uninstall(?Module $module): bool
+ public function uninstall(Module $module): bool
{
- if (!$module) {
+ $installedModule = $module->getInstalledVersion();
+ if (!$installedModule) {
return false;
}
- $module = $module->getInstalledVersion();
- if (!$module) {
- return false;
+ if ($installedModule->isChanged()) {
+ throw new RuntimeException(
+ "Can not uninstall module {$installedModule->getArchiveName()} {$installedModule->getVersion()} "
+ . "because the module has changes."
+ );
}
+ $this->internalUninstall($installedModule);
+ $this->createAutoloadFile();
+
+ return true;
+ }
+
+ private function internalUninstall(Module $module): void
+ {
// Uninstall from shop-root
$files = $module->getSrcFilePaths();
foreach ($files as $file) {
@@ -239,49 +353,9 @@ public function uninstall(?Module $module): bool
if (file_exists($module->getHashPath())) {
unlink($module->getHashPath());
}
-
- $this->createAutoloadFile();
-
- return true;
- }
-
- public function update(Module $module): ?Module
- {
- $oldModule = $module->getInstalledVersion();
- $newModule = $module->getNewestVersion();
-
- $dependencyManager = new DependencyManager();
- $dependencyManager->canBeInstalled($newModule);
-
- $this->uninstall($oldModule);
- $this->pull($newModule);
-
- // Da nach $newModule->pull() das Modul noch nicht lokal inistailisiert
- // sein kann, wird es noch einmal geladen.
- $moduleLoader = new LocalModuleLoader();
- $newModule = $moduleLoader->loadByArchiveNameAndVersion($newModule->getArchiveName(), $newModule->getVersion());
-
- if (!$newModule) {
- return null; //TODO: Better return not nullable type Module and thorw an exception
- }
-
- $this->install($newModule);
-
- return $newModule;
- }
-
- public function updateWithDependencies(Module $module): ?Module
- {
- $newModule = $this->update($module);
- if (!$newModule) {
- return null; //TODO: Better return not nullable type Module and thorw an exception
- }
-
- $this->installDependencies($newModule);
- return $newModule;
}
- public function installFile(string $src, string $dest, bool $overwrite = false): bool
+ private function installFile(string $src, string $dest, bool $overwrite = false): bool
{
if (!file_exists($src)) {
return false;
@@ -308,7 +382,7 @@ public function installFile(string $src, string $dest, bool $overwrite = false):
return true;
}
- public function uninstallFile(string $dest): void
+ private function uninstallFile(string $dest): void
{
if (file_exists($dest)) {
unlink($dest);
diff --git a/src/Classes/Semver/Comparator.php b/src/Classes/Semver/Comparator.php
index 86b1598e..4d4d5920 100644
--- a/src/Classes/Semver/Comparator.php
+++ b/src/Classes/Semver/Comparator.php
@@ -163,22 +163,39 @@ public function isCompatible(string $versionString1, string $versionString2): bo
return $this->greaterThanOrEqualTo($versionString1, $versionString2);
}
- public function satisfies(string $versionString1, string $constrain): bool
+ public function satisfies(string $versionString1, string $constraint): bool
{
- if ($constrain[0] == '^') { // Ist Buchstabe an Index 0 = ^
- $versionString2 = str_replace('^', '', $constrain);
- return $this->isCompatible($versionString1, $versionString2);
- } elseif ($constrain[0] == '<' && $constrain[1] == '=') {
- $versionString2 = str_replace('<=', '', $constrain);
+ if (strpos($constraint, '||')) {
+ return $this->satisfiesOr($versionString1, $constraint);
+ }
+
+ if (strpos($constraint, ',')) {
+ return $this->satisfiesAnd($versionString1, $constraint);
+ }
+
+ if (strpos($constraint, '<=') === 0) {
+ $versionString2 = str_replace('<=', '', $constraint);
return $this->lessThanOrEqualTo($versionString1, $versionString2);
+ } elseif (strpos($constraint, '<') === 0) {
+ $versionString2 = str_replace('<', '', $constraint);
+ return $this->lessThan($versionString1, $versionString2);
+ } elseif (strpos($constraint, '>=') === 0) {
+ $versionString2 = str_replace('>=', '', $constraint);
+ return $this->greaterThanOrEqualTo($versionString1, $versionString2);
+ } elseif (strpos($constraint, '>') === 0) {
+ $versionString2 = str_replace('>', '', $constraint);
+ return $this->greaterThan($versionString1, $versionString2);
+ } elseif (strpos($constraint, '^') === 0) {
+ $versionString2 = str_replace('^', '', $constraint);
+ return $this->isCompatible($versionString1, $versionString2);
} else {
- $versionString2 = $constrain;
+ $versionString2 = $constraint;
return $this->equalTo($versionString1, $versionString2);
}
}
/**
- * Can satisfy multiple constraints with OR / ||
+ * Can satisfy multiple constraints with OR (||)
*
* Example: ^7.4 || ^8.0
*/
@@ -193,4 +210,21 @@ public function satisfiesOr(string $versionString1, string $constraintOrExpressi
}
return false;
}
+
+ /**
+ * Can satisfy multiple constraints with AND (,)
+ *
+ * Example: ^7.4, ^8.0
+ */
+ public function satisfiesAnd(string $versionString1, string $constraintOrExpression): bool
+ {
+ $constraints = explode(',', $constraintOrExpression);
+ foreach ($constraints as $constraint) {
+ $constraint = trim($constraint);
+ if (!$this->satisfies($versionString1, $constraint)) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/Classes/Semver/Constraint.php b/src/Classes/Semver/Constraint.php
new file mode 100644
index 00000000..36285ff2
--- /dev/null
+++ b/src/Classes/Semver/Constraint.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\Semver;
+
+class Constraint
+{
+ public static function createConstraintFromConstraints(array $constraints): string
+ {
+ $constraint = '';
+ foreach ($constraints as $version) {
+ if ($constraint === '') {
+ $constraint = $version;
+ } else {
+ $constraint .= ', ' . $version;
+ }
+ }
+ return $constraint;
+ }
+
+ public static function resolveCaretRange(string $range): string
+ {
+ if (preg_match('/^\^(?\d+)(\.(?\d+))?(\.(?\d+))?(?.*)$/', $range, $matches)) {
+ $major = intval($matches['major']);
+ $minor = isset($matches['minor']) ? intval($matches['minor']) : 0;
+ $patch = isset($matches['patch']) ? intval($matches['patch']) : 0;
+ $suffix = $matches['suffix'];
+
+ $lower = sprintf("%d.%d.%d%s", $major, $minor, $patch, $suffix);
+
+ if ($major == 0) {
+ $upper = sprintf("%d.%d.%d", $major, $minor + 1, 0);
+ } else {
+ $upper = sprintf("%d.%d.%s", $major + 1, 0, '0');
+ }
+
+ return ">=$lower,<$upper";
+ }
+
+ return $range;
+ }
+
+ public static function resolve(string $constaint): string
+ {
+ return self::resolveCaretRange($constaint);
+ }
+}
diff --git a/src/Classes/Semver/Filter.php b/src/Classes/Semver/Filter.php
index d89b9abe..3ddbeea9 100644
--- a/src/Classes/Semver/Filter.php
+++ b/src/Classes/Semver/Filter.php
@@ -52,7 +52,7 @@ public function byConstraint(string $constraint, array $versions): array
{
$fileredVersions = [];
foreach ($versions as $version) {
- if ($this->comparator->satisfiesOr($version, $constraint)) {
+ if ($this->comparator->satisfies($version, $constraint)) {
$fileredVersions[] = $version;
}
}
diff --git a/src/Classes/ViewModels/ModuleViewModel.php b/src/Classes/ViewModels/ModuleViewModel.php
index 033d9968..55ff2cdd 100644
--- a/src/Classes/ViewModels/ModuleViewModel.php
+++ b/src/Classes/ViewModels/ModuleViewModel.php
@@ -57,6 +57,12 @@ public function getModuleInfoUrl(string $ref = ''): string
return $this->getUrl('moduleInfo', $ref);
}
+ public function getInstalledUrl(string $ref = ''): string
+ {
+ $module = $this->module->getInstalledVersion();
+ return $this->getUrl('moduleInfo', $ref, $module);
+ }
+
public function getLoadModuleUrl(string $ref = ''): string
{
return $this->getUrl('loadRemoteModule', $ref);
@@ -181,12 +187,16 @@ public function isChanged(): bool
return $this->module->isChanged();
}
- private function getUrl(string $action, string $ref): string
+ private function getUrl(string $action, string $ref, ?Module $module = null): string
{
+ if (!$module) {
+ $module = $this->module;
+ }
+
return
'?action=' . $action .
- '&archiveName=' . $this->module->getArchiveName() .
- '&version=' . $this->module->getVersion() .
+ '&archiveName=' . $module->getArchiveName() .
+ '&version=' . $module->getVersion() .
'&ref=' . $ref;
}
diff --git a/src/Classes/ViewModels/NotificationViewModel.php b/src/Classes/ViewModels/NotificationViewModel.php
index 6e8bc623..6717490d 100644
--- a/src/Classes/ViewModels/NotificationViewModel.php
+++ b/src/Classes/ViewModels/NotificationViewModel.php
@@ -32,7 +32,7 @@ public function renderFlashMessage(string $message, string $type): string
if ($type == 'warning') {
return ' ' . $message . '';
} elseif ($type == 'error') {
- return '' . $message . '';
+ return ' ' . $message . '';
} elseif ($type == 'success') {
return ' ' . $message . '';
}
diff --git a/src/Templates/ModuleInfo.tmpl.php b/src/Templates/ModuleInfo.tmpl.php
index 7b653cf7..80bceb3c 100644
--- a/src/Templates/ModuleInfo.tmpl.php
+++ b/src/Templates/ModuleInfo.tmpl.php
@@ -39,6 +39,34 @@
= $notificationView->renderFlashMessages() ?>
+ isRepairable()) { ?>
+
+
+ Einige Dateien befinden sich nicht mehr im Originalzustand. Möglicherweise hast du an
+ diesen Anpassungen vorgenommen. Deinstallation und
+ Update stehen dir nur bei unveränderten Modulen zur Verfügung, damit
+ deine Arbeit nicht verloren geht.
+ Alle Änderungen ansehen.
+
+
+
+ isLoadable()) { ?>
+
+
+ Um dieses Modul zu verwenden, nimm bitte Kontakt zum Entwickler auf. Der Entwickler kann
+ dir das Modul (z. B. nach einem Kauf) freischalten.
+
+
+
+ isCompatible()) { ?>
+ getCompatibleStrings() as $string) { ?>
+
+
+ = $string ?>
+
+
+
+
@@ -58,34 +86,6 @@
-
- isRepairable()) { ?>
-
-
- Einige Dateien befinden sich nicht mehr im Originalzustand. Möglicherweise hast du an
- diesen Anpassungen vorgenommen. Deinstallation und
- Update stehen dir nur bei unveränderten Modulen zur Verfügung, damit
- deine Arbeit nicht verloren geht.
- Alle Änderungen ansehen.
-
-
-
- isLoadable()) { ?>
-
-
- Um dieses Modul zu verwenden, nimm bitte Kontakt zum Entwickler auf. Der Entwickler kann
- dir das Modul (z. B. nach einem Kauf) freischalten.
-
-
-
- isCompatible()) { ?>
- getCompatibleStrings() as $string) { ?>
-
-
- = $string ?>
-
-
-
@@ -131,7 +131,7 @@
Installieren (inkompatible Version)
hasInstalledVersion()) { ?>
- Zur installierten Version
+ Zur installierten Version
isRemote() && $moduleView->isLoaded() && !$moduleView->isInstalled()) { ?>
@@ -289,7 +289,7 @@
Kompatibel mit MMLC
getMmlc()) { ?>
- getMmlc()['version'] ?? '^1.20.0') as $version) { ?>
+ getMmlc()['version'] ?? '') as $version) { ?>
= trim($version); ?>
diff --git a/tests/unit/DependencyManager/CombinationTest.php b/tests/unit/DependencyManager/CombinationTest.php
new file mode 100644
index 00000000..f1ba2fa3
--- /dev/null
+++ b/tests/unit/DependencyManager/CombinationTest.php
@@ -0,0 +1,90 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\Tests\Unit\DependencyManager;
+
+use Exception;
+use PHPUnit\Framework\TestCase;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\Combination;
+
+class CombinationTest extends TestCase
+{
+ public function testAdd()
+ {
+ $combination = new Combination();
+ $combination->add('foo/bar', '1.0.0');
+
+ $this->assertEquals(['foo/bar' => '1.0.0'], $combination->getAll());
+ }
+
+ public function testCanNotAddTwice()
+ {
+ $this->expectException(Exception::class);
+
+ $combination = new Combination();
+ $combination->add('foo/bar', '1.0.0');
+ $combination->add('foo/bar', '2.0.0');
+ }
+
+ public function testOverwrite()
+ {
+ $combination = new Combination();
+ $combination->add('foo/bar', '1.0.0');
+ $combination->overwrite('foo/bar', '2.0.0');
+
+ $this->assertEquals(['foo/bar' => '2.0.0'], $combination->getAll());
+ }
+
+ public function testGetVersion()
+ {
+ $combination = new Combination();
+ $combination->add('foo', '1.0.0');
+ $combination->add('foo/bar', '2.0.0');
+
+ $this->assertEquals('2.0.0', $combination->getVersion('foo/bar'));
+ }
+
+ public function testGetVersionThrowsException()
+ {
+ $this->expectException(Exception::class);
+
+ $combination = new Combination();
+ $combination->add('foo', '1.0.0');
+ $combination->add('foo/bar', '2.0.0');
+
+ $combination->getVersion('bar');
+ }
+
+ public function testStrip()
+ {
+ $combination = new Combination();
+ $combination->add('foo', '1.0.0');
+ $combination->add('foo/bar', '2.0.0');
+ $stripedCombination = $combination->strip();
+
+ $this->assertEquals(
+ [
+ 'foo' => '1.0.0',
+ 'foo/bar' => '2.0.0'
+ ],
+ $combination->getAll()
+ );
+
+ $this->assertEquals(
+ [
+ 'foo/bar' => '2.0.0'
+ ],
+ $stripedCombination->getAll()
+ );
+ }
+}
diff --git a/tests/unit/DependencyManager/CounterTest.php b/tests/unit/DependencyManager/CounterTest.php
new file mode 100644
index 00000000..74daa199
--- /dev/null
+++ b/tests/unit/DependencyManager/CounterTest.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\Tests\Unit\DependencyManager;
+
+use PHPUnit\Framework\TestCase;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\Counter;
+
+class CounterTest extends TestCase
+{
+ public function testCurrent()
+ {
+ $counter = new Counter([
+ 2, 1, 3
+ ]);
+
+ $this->assertEquals([0, 0, 0], $counter->current());
+ }
+
+ public function testNext()
+ {
+ $counter = new Counter([
+ 2, 1, 3
+ ]);
+
+ $this->assertEquals([0, 0, 1], $counter->next());
+ $this->assertEquals([0, 0, 2], $counter->next());
+ $this->assertEquals([0, 0, 3], $counter->next());
+ $this->assertEquals([0, 1, 0], $counter->next());
+ $this->assertEquals([0, 1, 1], $counter->next());
+ $this->assertEquals([0, 1, 2], $counter->next());
+ $this->assertEquals([0, 1, 3], $counter->next());
+ $this->assertEquals([1, 0, 0], $counter->next());
+ $this->assertEquals([1, 0, 1], $counter->next());
+ $this->assertEquals([1, 0, 2], $counter->next());
+ $this->assertEquals([1, 0, 3], $counter->next());
+ $this->assertEquals([1, 1, 0], $counter->next());
+ $this->assertEquals([1, 1, 1], $counter->next());
+ $this->assertEquals([1, 1, 2], $counter->next());
+ $this->assertEquals([1, 1, 3], $counter->next());
+ $this->assertEquals([2, 0, 0], $counter->next());
+ $this->assertEquals([2, 0, 1], $counter->next());
+ $this->assertEquals([2, 0, 2], $counter->next());
+ $this->assertEquals([2, 0, 3], $counter->next());
+ $this->assertEquals([2, 1, 0], $counter->next());
+ $this->assertEquals([2, 1, 1], $counter->next());
+ $this->assertEquals([2, 1, 2], $counter->next());
+ $this->assertEquals([2, 1, 3], $counter->next());
+ $this->assertEquals([0, 0, 0], $counter->next());
+ }
+
+ public function testStart()
+ {
+ $counter = new Counter([
+ 2, 1
+ ]);
+
+ $this->assertEquals(true, $counter->isStart());
+ $this->assertEquals([0, 0], $counter->current());
+ $this->assertEquals([0, 1], $counter->next());
+ $this->assertEquals(false, $counter->isStart());
+ $this->assertEquals([1, 0], $counter->next());
+ $this->assertEquals([1, 1], $counter->next());
+ $this->assertEquals([2, 0], $counter->next());
+ $this->assertEquals([2, 1], $counter->next());
+ $this->assertEquals([0, 0], $counter->next());
+ $this->assertEquals(true, $counter->isStart());
+ }
+}
diff --git a/tests/unit/DependencyManager/DependencyBuilderTest.php b/tests/unit/DependencyManager/DependencyBuilderTest.php
new file mode 100644
index 00000000..e22896f5
--- /dev/null
+++ b/tests/unit/DependencyManager/DependencyBuilderTest.php
@@ -0,0 +1,78 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\Tests\Unit\DependencyManager;
+
+use PHPUnit\Framework\TestCase;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\DependencyBuilder;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\SystemSet;
+
+class DependencyBuilderTest extends TestCase
+{
+ public function testSatisfies()
+ {
+ $dependencyBuilder = new DependencyBuilder();
+ $systemSet = new SystemSet();
+
+ $systemSet->set([
+ "modified" => '2.0.4.2',
+ "php" => '7.4.0',
+ "mmlc" => '1.20.0-beta.1',
+ "composer/autoload" => '1.3.0',
+ "robinthehood/modified-std-module" => '0.9.0',
+ "robinthehood/modified-orm" => '1.8.1',
+ "robinthehood/pdf-bill" => '0.17.0',
+ "foo/bar" => '1.2.3'
+ ]);
+
+ $combinationSatisfyerResult = $dependencyBuilder->satisfies('firstweb/multi-order', '^1.0.0', $systemSet);
+
+ $this->assertEqualsCanonicalizing(
+ [
+ "modified" => '2.0.4.2',
+ "php" => '7.4.0',
+ "mmlc" => '1.20.0-beta.1',
+ "composer/autoload" => '1.3.0',
+ "robinthehood/modified-std-module" => '0.9.0',
+ "robinthehood/modified-orm" => '1.8.1',
+ "robinthehood/modified-ui" => '0.1.0',
+ "robinthehood/pdf-bill" => '0.17.0',
+ "robinthehood/tfpdf" => '0.3.0',
+ 'firstweb/multi-order' => '1.13.3',
+ "foo/bar" => '1.2.3'
+ ],
+ $combinationSatisfyerResult->testCombination->getAll()
+ );
+
+ $this->assertEqualsCanonicalizing(
+ [
+ "modified" => '2.0.4.2',
+ "composer/autoload" => '1.3.0',
+ "robinthehood/modified-std-module" => '0.9.0',
+ "robinthehood/modified-orm" => '1.8.1',
+ "robinthehood/modified-ui" => '0.1.0',
+ "robinthehood/pdf-bill" => '0.17.0',
+ "robinthehood/tfpdf" => '0.3.0',
+ 'firstweb/multi-order' => '1.13.3',
+ ],
+ $combinationSatisfyerResult->foundCombination->getAll()
+ );
+ }
+
+ public function atestInvokeDependency()
+ {
+ $dpb = new DependencyBuilder();
+ $dpb->test();
+ die('TEST DONE');
+ }
+}
diff --git a/tests/unit/DependencyManager/SystemSetTest.php b/tests/unit/DependencyManager/SystemSetTest.php
new file mode 100644
index 00000000..28afb96f
--- /dev/null
+++ b/tests/unit/DependencyManager/SystemSetTest.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\Tests\Unit\DependencyManager;
+
+use PHPUnit\Framework\TestCase;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\Combination;
+use RobinTheHood\ModifiedModuleLoaderClient\DependencyManager\SystemSet;
+
+class SystemSetTest extends TestCase
+{
+ public function testGetArchiveNamesReturnsExpectedArray()
+ {
+ // Arrange
+ $systems = [
+ 'systemA' => '1.0',
+ 'systemB/1.2' => '1.2',
+ 'systemC/2.0' => '2.0',
+ 'systemD/2.1' => '2.1',
+ ];
+ $systemSet = new SystemSet();
+ $systemSet->set($systems);
+
+ // Act
+ $result = $systemSet->getArchiveNames();
+
+ // Assert
+ $expected = ['systemB/1.2', 'systemC/2.0', 'systemD/2.1'];
+ $this->assertEquals($expected, $result);
+ }
+}
diff --git a/tests/unit/RemoteModuleLoaderTest.php b/tests/unit/RemoteModuleLoaderTest.php
index 4409ed58..15df5bf5 100644
--- a/tests/unit/RemoteModuleLoaderTest.php
+++ b/tests/unit/RemoteModuleLoaderTest.php
@@ -20,6 +20,8 @@
class RemoteModuleLoaderTest extends TestCase
{
+ private $loader;
+
protected function setUp(): void
{
$this->loader = RemoteModuleLoader::getModuleLoader();
diff --git a/tests/unit/SemverTests/ConstraintTest.php b/tests/unit/SemverTests/ConstraintTest.php
new file mode 100644
index 00000000..ca419042
--- /dev/null
+++ b/tests/unit/SemverTests/ConstraintTest.php
@@ -0,0 +1,112 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace RobinTheHood\ModifiedModuleLoaderClient\Tests\Unit\SemverTests;
+
+use PHPUnit\Framework\TestCase;
+use RobinTheHood\ModifiedModuleLoaderClient\Semver\Constraint;
+
+class ConstraintTest extends TestCase
+{
+ public function testResolveCaretRangeWithMajor()
+ {
+ $range = '^1.0.0';
+ $expected = '>=1.0.0,<2.0.0';
+ $this->assertEquals($expected, Constraint::resolveCaretRange($range));
+ }
+
+ public function testResolveCaretRangeWithMajorMinor()
+ {
+ $range = '^1.2.0';
+ $expected = '>=1.2.0,<2.0.0';
+ $this->assertEquals($expected, Constraint::resolveCaretRange($range));
+ }
+
+ public function testResolveCaretRangeWithMajorMinorPatch()
+ {
+ $range = '^1.2.3';
+ $expected = '>=1.2.3,<2.0.0';
+ $this->assertEquals($expected, Constraint::resolveCaretRange($range));
+ }
+
+ public function testResolveCaretRangeWithSuffix()
+ {
+ $range = '^1.2.3-alpha';
+ $expected = '>=1.2.3-alpha,<2.0.0';
+ $this->assertEquals($expected, Constraint::resolveCaretRange($range));
+ }
+
+ public function testResolveCaretRangeWithMajorAndTag()
+ {
+ $range = '^1.0.0-beta';
+ $expected = '>=1.0.0-beta,<2.0.0';
+ $this->assertEquals($expected, Constraint::resolveCaretRange($range));
+ }
+
+ public function testResolveCaretRangeWithMajorMinorAndTag()
+ {
+ $range = '^1.2.0-rc';
+ $expected = '>=1.2.0-rc,<2.0.0';
+ $this->assertEquals($expected, Constraint::resolveCaretRange($range));
+ }
+
+ public function testResolveCaretRangeWithMajorMinorPatchAndTag()
+ {
+ $range = '^1.2.3-dev';
+ $expected = '>=1.2.3-dev,<2.0.0';
+ $this->assertEquals($expected, Constraint::resolveCaretRange($range));
+ }
+
+ public function testResolveCaretPreRelease()
+ {
+ // Test resolving a range with a caret and a major version of 0
+ $this->assertEquals(">=0.2.0,<0.3.0", Constraint::resolveCaretRange("^0.2.0"));
+
+ // Test resolving a range with a caret and a minor version of 0 and a patch version of 1
+ $this->assertEquals(">=0.0.1,<0.1.0", Constraint::resolveCaretRange("^0.0.1"));
+ }
+
+ public function testCreateConstraintFromConstraints()
+ {
+ $constraints = [
+ '>1.0', '>=2.3,<4.0',
+ ];
+ $expected = '>1.0, >=2.3,<4.0';
+ $this->assertEquals($expected, Constraint::createConstraintFromConstraints($constraints));
+ }
+
+ public function testCreateConstraintFromConstraintsWithDuplicateVersions()
+ {
+ $constraints = [
+ '>=2.3,<4.0', '>=2.3,<=3.0',
+ ];
+ $expected = '>=2.3,<4.0, >=2.3,<=3.0';
+ $this->assertEquals($expected, Constraint::createConstraintFromConstraints($constraints));
+ }
+
+ public function testCreateConstraintFromConstraintsWithOneConstraint()
+ {
+ $constraints = [
+ '>=1.0',
+ ];
+ $expected = '>=1.0';
+ $this->assertEquals($expected, Constraint::createConstraintFromConstraints($constraints));
+ }
+
+ public function testCreateConstraintFromConstraintsWithEmptyArray()
+ {
+ $constraints = [];
+ $expected = '';
+ $this->assertEquals($expected, Constraint::createConstraintFromConstraints($constraints));
+ }
+}
diff --git a/tests/unit/SemverTests/SemverComparatorTest.php b/tests/unit/SemverTests/SemverComparatorTest.php
index 9b4137f7..4ae00d0d 100644
--- a/tests/unit/SemverTests/SemverComparatorTest.php
+++ b/tests/unit/SemverTests/SemverComparatorTest.php
@@ -130,7 +130,13 @@ public function testThatVersionASatisfiesConstraint()
public function testThatVersionASatisfiesOrConstraint()
{
- $this->assertTrue($this->comparator->satisfiesOr('3.3.3', '^2.2.2 || ^3.3.3'));
- $this->assertFalse($this->comparator->satisfiesOr('4.4.4', '^2.2.2 || ^3.3.3'));
+ $this->assertTrue($this->comparator->satisfies('3.3.3', '^2.2.2 || ^3.3.3'));
+ $this->assertFalse($this->comparator->satisfies('4.4.4', '^2.2.2 || ^3.3.3'));
+ }
+
+ public function testThatVersionASatisfiesAndConstraint()
+ {
+ $this->assertFalse($this->comparator->satisfies('3.3.3', '^2.2.2, ^3.3.3'));
+ $this->assertTrue($this->comparator->satisfies('4.4.4', '^4.4.2, ^4.4.3'));
}
}
diff --git a/tests/unused_unit_tests/DependencyBuilderTest.php b/tests/unused_unit_tests/DependencyBuilderTest.php
deleted file mode 100644
index eb03d828..00000000
--- a/tests/unused_unit_tests/DependencyBuilderTest.php
+++ /dev/null
@@ -1,29 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace RobinTheHood\ModifiedModuleLoaderClient\Tests\Unit;
-
-use PHPUnit\Framework\TestCase;
-
-class DependencyBuilderTest //extends TestCase
-{
-
- /*
- public function invokeDependencyTest()
- {
- $dpb = new DependencyBuilder();
- $dpb->test();
- die('TEST DONE');
- }
- */
-}