diff --git a/install/class.Setup.php b/install/class.Setup.php index 514a156965..ff83c58920 100644 --- a/install/class.Setup.php +++ b/install/class.Setup.php @@ -26,6 +26,7 @@ use oat\oatbox\log\LoggerService; use oat\oatbox\service\ConfigurableService; use oat\oatbox\service\ServiceManager; +use oat\tao\model\install\seed\SeedSorter; use Zend\ServiceManager\ServiceLocatorAwareInterface; class tao_install_Setup implements Action @@ -52,6 +53,7 @@ class tao_install_Setup implements Action * @throws common_ext_ExtensionException When a presented parameter is invalid or malformed. * @throws InvalidArgumentException * @throws tao_install_utils_Exception + * @throws ReflectionException */ public function __invoke($params) { @@ -223,12 +225,10 @@ public function __invoke($params) } elseif (!isset($persistences['type'])) { throw new InvalidArgumentException('Your config should have a \'default\' key under \'persistences\''); } - + $seedSorter = new SeedSorter(); foreach ($parameters['configuration'] as $extension => $configs) { - $sortedConfigs = $this->sortConfigs($serviceManager, $configs); - - var_dump(array_keys($sortedConfigs)); + $configs = $seedSorter->sort($configs); foreach ($configs as $key => $config) { if (isset($config['type']) && $config['type'] === 'configurableService') { @@ -292,64 +292,6 @@ public function __invoke($params) $this->logNotice('Installation completed!'); } - private function getDependencies(string $className): array - { - $reflection = new ReflectionClass($className); - - $constructor = $reflection->getConstructor(); - - $result = []; - - foreach ($constructor->getParameters() as $parameter) { - if ($class = $parameter->getClass()) { - $result[] = $class->name; - } - } - - return $result; - } - - private function sortDependencies(ServiceManager $serviceManager, array $graph, string $key, array &$result): void - { - echo "> $key\n"; - echo ">> {$graph[$key]['class']}\n"; - -// if (!isset($graph[$key]) || !$serviceManager->get($graph[$key]['class'])) { -// throw new Exception("No '$key' in graph"); -// } - - if (isset($result[$key])) { - return; - } - - if (!isset($graph[$key]['class'])) { - return; - } - - $className = $graph[$key]['class']; - - $deps = $this->getDependencies($className); - - var_dump($deps); - - foreach ($deps as $depKey) { - $this->sortDependencies($serviceManager, $graph, $depKey, $result); - } - - $result[$key] = $graph[$key]; - } - - private function sortConfigs(ServiceManager $serviceManager, array $config): array - { - $result = []; - - foreach ($config as $key => $value) { - $this->sortDependencies($serviceManager, $config, $key, $result); - } - - return $result; - } - /** * @param string $class * @param array $parametersToSort diff --git a/models/classes/install/seed/SeedSorter.php b/models/classes/install/seed/SeedSorter.php new file mode 100644 index 0000000000..e089f1403f --- /dev/null +++ b/models/classes/install/seed/SeedSorter.php @@ -0,0 +1,120 @@ + $serviceConfig) { + $this->sortDependencies($config, $serviceKey, $sortedConfig); + } + + return $sortedConfig; + } + + /** + * @throws ReflectionException + */ + private function getDependencies(string $className): array + { + $reflection = new ReflectionClass($className); + + $constructor = $reflection->getConstructor(); + + if (null === $constructor) { + return []; + } + + $dependencies = []; + + foreach ($constructor->getParameters() as $parameter) { + if ($class = $parameter->getClass()) { + $dependencies[] = $class->name; + } + } + + return $dependencies; + } + + /** + * @throws ReflectionException + */ + private function sortDependencies(array $config, string $serviceKey, array &$result): void + { + if (isset($result[$serviceKey])) { + return; + } + + if (!isset($config[$serviceKey]['class'])) { + $result[$serviceKey] = $config[$serviceKey]; + + return; + } + + $serviceClassName = $config[$serviceKey]['class']; + + $dependencies = $this->getDependencies($serviceClassName); + + foreach ($dependencies as $dependencyClassName) { + try { + $dependencyKey = $this->findServiceKey($config, $dependencyClassName); + $this->sortDependencies($config, $dependencyKey, $result); + } catch (SeedSorterException $e) { + continue; + } + } + + $result[$serviceKey] = $config[$serviceKey]; + } + + private function findServiceKey(array $config, string $className) + { + foreach ($config as $serviceKey => $serviceConfig) { + if (empty($serviceConfig['class'])) { + throw new SeedSorterException( + sprintf('%s not a class', $className) + ); + } + + if ( + $serviceConfig['class'] === $className + || is_subclass_of($serviceConfig['class'], $className) + ) { + return $serviceKey; + } + } + + throw new SeedSorterException( + sprintf('Service key for class %s not found', $className) + ); + } +} diff --git a/models/classes/install/seed/SeedSorterException.php b/models/classes/install/seed/SeedSorterException.php new file mode 100644 index 0000000000..347454fbf8 --- /dev/null +++ b/models/classes/install/seed/SeedSorterException.php @@ -0,0 +1,29 @@ +sorter = new SeedSorter(); + } + + /** + * @throws ReflectionException + */ + public function testSortConfigs(): void + { + $configConfiguration = [ + 'DepFromInterfaceInConfigClass' => [ + 'class' => DepFromInterfaceInConfigClass::class, + ], + 'DoubleDepFromServiceInConfigAndNotInConfigClass' => [ + 'class' => DoubleDepFromServiceInConfigAndNotInConfigClass::class, + ], + 'DepFromServiceNotInConfigClass' => [ + 'class' => DepFromServiceNotInConfigClass::class, + ], + 'Dep2FromServiceInConfigClass' => [ + 'class' => Dep2FromServiceInConfigClass::class, + ], + 'DoubleDepFromServiceInConfigClass' => [ + 'class' => DoubleDepFromServiceInConfigClass::class, + ], + 'DepFromServiceInConfigClass' => [ + 'class' => DepFromServiceInConfigClass::class, + ], + 'NoDepArrayClass' => [ + 'class' => NoDepArrayClass::class, + ], + 'NoDepClass' => [ + 'class' => NoDepClass::class, + ], + 'NotAClass' => [ + 'some' => 'config', + ], + ]; + + $sortedConfigs = $this->sorter->sort($configConfiguration); + + $this->assertKeysAfter(['NoDepClass'], 'DepFromServiceInConfigClass', $sortedConfigs); + $this->assertKeysAfter(['NoDepClass'], 'DepFromInterfaceInConfigClass', $sortedConfigs); + $this->assertKeysAfter( + ['DoubleDepFromServiceInConfigClass'], + 'DoubleDepFromServiceInConfigAndNotInConfigClass', + $sortedConfigs + ); + $this->assertKeysAfter( + ['NoDepClass', 'DepFromServiceInConfigClass'], + 'DoubleDepFromServiceInConfigClass', + $sortedConfigs + ); + + self::assertArrayHasKey('NoDepClass', $sortedConfigs); + self::assertArrayHasKey('NoDepArrayClass', $sortedConfigs); + self::assertArrayHasKey('DepFromServiceNotInConfigClass', $sortedConfigs); + self::assertArrayHasKey('NotAClass', $sortedConfigs); + } + + private function assertKeysAfter(array $priorKeys, string $needleKey, array $array): void + { + $maxKeyIndex = 0; + + $arrayKeys = array_keys($array); + + foreach ($priorKeys as $priorKey) { + $keyIndex = array_search($priorKey, $arrayKeys, true); + + self::assertIsInt($keyIndex); + + if ($maxKeyIndex < $keyIndex) { + $maxKeyIndex = $keyIndex; + } + } + + self::assertTrue($maxKeyIndex < array_search($needleKey, $arrayKeys, true)); + } +}