diff --git a/framework/core/locale/core.yml b/framework/core/locale/core.yml index b842cd72b..c35d0964e 100644 --- a/framework/core/locale/core.yml +++ b/framework/core/locale/core.yml @@ -605,6 +605,7 @@ core: # These translations are displayed as error messages. error: + circular_dependencies_message: "Circular dependencies detected: {extensions}. Aborting. Please disable one of the extensions and try again." dependent_extensions_message: "Cannot disable {extension} until the following dependent extensions are disabled: {extensions}" extension_initialiation_failed_message: "{extension} failed to initialize, check the browser console for further information." generic_message: "Oops! Something went wrong. Please reload the page and try again." diff --git a/framework/core/src/Extension/Exception/CircularDependenciesException.php b/framework/core/src/Extension/Exception/CircularDependenciesException.php new file mode 100644 index 000000000..c2c052839 --- /dev/null +++ b/framework/core/src/Extension/Exception/CircularDependenciesException.php @@ -0,0 +1,25 @@ +circular_dependencies = $circularDependencies; + + parent::__construct('Circular dependencies detected: '.implode(', ', ExtensionManager::pluckTitles($circularDependencies)).' - aborting. Please fix this by disabling the extensions that are causing the circular dependencies.'); + } +} diff --git a/framework/core/src/Extension/Exception/CircularDependenciesExceptionHandler.php b/framework/core/src/Extension/Exception/CircularDependenciesExceptionHandler.php new file mode 100644 index 000000000..67e7cdf8d --- /dev/null +++ b/framework/core/src/Extension/Exception/CircularDependenciesExceptionHandler.php @@ -0,0 +1,32 @@ +withDetails($this->errorDetails($e)); + } + + protected function errorDetails(CircularDependenciesException $e): array + { + return [[ + 'extensions' => ExtensionManager::pluckTitles($e->circular_dependencies), + ]]; + } +} diff --git a/framework/core/src/Extension/ExtensionManager.php b/framework/core/src/Extension/ExtensionManager.php index 5e33db2bb..08d0be0ef 100644 --- a/framework/core/src/Extension/ExtensionManager.php +++ b/framework/core/src/Extension/ExtensionManager.php @@ -15,6 +15,7 @@ use Flarum\Extension\Event\Disabling; use Flarum\Extension\Event\Enabled; use Flarum\Extension\Event\Enabling; use Flarum\Extension\Event\Uninstalled; +use Flarum\Extension\Exception\CircularDependenciesException; use Flarum\Foundation\Paths; use Flarum\Settings\SettingsRepositoryInterface; use Illuminate\Contracts\Container\Container; @@ -157,6 +158,13 @@ class ExtensionManager return $this->extensions; } + public function getExtensionsById(array $ids): Collection + { + return $this->getExtensions()->filter(function (Extension $extension) use ($ids) { + return in_array($extension->getId(), $ids); + }); + } + /** * Loads an Extension with all information. * @@ -397,10 +405,19 @@ class ExtensionManager * Persist the currently enabled extensions. * * @param array $enabledExtensions + * @throws CircularDependenciesException */ protected function setEnabledExtensions(array $enabledExtensions) { - $sortedEnabled = static::resolveExtensionOrder($enabledExtensions)['valid']; + $resolved = static::resolveExtensionOrder($enabledExtensions); + + if (! empty($resolved['circularDependencies'])) { + throw new Exception\CircularDependenciesException( + $this->getExtensionsById($resolved['circularDependencies'])->values()->all() + ); + } + + $sortedEnabled = $resolved['valid']; $sortedEnabledIds = array_map(function (Extension $extension) { return $extension->getId(); diff --git a/framework/core/src/Foundation/ErrorServiceProvider.php b/framework/core/src/Foundation/ErrorServiceProvider.php index 495303b61..7e9d63d41 100644 --- a/framework/core/src/Foundation/ErrorServiceProvider.php +++ b/framework/core/src/Foundation/ErrorServiceProvider.php @@ -58,6 +58,7 @@ class ErrorServiceProvider extends AbstractServiceProvider return [ IlluminateValidationException::class => Handling\ExceptionHandler\IlluminateValidationExceptionHandler::class, ValidationException::class => Handling\ExceptionHandler\ValidationExceptionHandler::class, + ExtensionException\CircularDependenciesException::class => ExtensionException\CircularDependenciesExceptionHandler::class, ExtensionException\DependentExtensionsException::class => ExtensionException\DependentExtensionsExceptionHandler::class, ExtensionException\MissingDependenciesException::class => ExtensionException\MissingDependenciesExceptionHandler::class, ];