public function getSortedModuleList() : array {
		$moduleList = $this->modules;

		foreach ($moduleList as $moduleSpec) {
			$moduleClass = $moduleSpec['class'];
			$module      = $moduleSpec['object'];

			if ($module instanceof OrderAwareModule) {
				//All modules that should be loaded after this one
				foreach ($module->getModulesAfter() as $afterModuleClass) {
					if (\array_key_exists($afterModuleClass, $moduleList)) {
						$moduleList[$moduleClass]['after'][] = $afterModuleClass;
						$moduleList[$afterModuleClass]['before'][] = $moduleClass;
					}
				}
				//All modules that should be loaded before this one
				foreach ($module->getModulesBefore() as $afterModuleClass) {
					if (\array_key_exists($afterModuleClass, $moduleList)) {
						$moduleList[$moduleClass]['before'][] = $afterModuleClass;
						$moduleList[$afterModuleClass]['after'][] = $moduleClass;
					}
				}
			}
		}

		$sortedList = [];
		$noIncomingNodes = [];
		foreach ($moduleList as $moduleSpec) {
			$moduleSpec['after']  = \array_unique($moduleSpec['after']);
			$moduleSpec['before'] = \array_unique($moduleSpec['before']);
			if (empty($moduleSpec['before'])) {
				$noIncomingNodes[$moduleSpec['class']] = $moduleSpec;
			}
		}

		//Do the topological sorting (Kahn's algorithm)
		while (!empty($noIncomingNodes)) {
			$moduleSpec = array_shift($noIncomingNodes);
			\array_push($sortedList, $moduleSpec);
			unset($moduleList[$moduleSpec['class']]);

			foreach ($moduleSpec['after'] as $nextModuleKey => $nextModuleClass) {
				unset($moduleList[$nextModuleClass]['before'][
					\array_search($moduleSpec['class'], $moduleList[$nextModuleClass]['before'])
				]);
				if (empty($moduleList[$nextModuleClass]['before'])) {
					$noIncomingNodes[] = $moduleList[$nextModuleClass];
				}
				unset($moduleSpec['after'][$nextModuleKey]);
			}
		}

		foreach ($moduleList as $key => $remainingModuleSpec) {
			if (empty($remainingModuleSpec['before'])) {
				$sortedList[] = $moduleList[$remainingModuleSpec['class']];
				unset($moduleList[$key]);
			}
		}

		if (!empty($moduleList)) {
			throw new CircularModuleLoadOrderDetectedException($this->getDotGraph());
		} else {
			$result = [];
			foreach ($sortedList as $moduleSpec) {
				$result[] = $moduleSpec['object'];
			}
			return $result;
		}
	}