From 65ec318c3096da089afc37777749f09bb48f8ca8 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Wed, 31 Aug 2022 18:43:30 +0100 Subject: [PATCH] PluginManager: Ensure dependents are disabled before dependencies in disablePlugins() (#5227) this could later be expanded to disablePlugin() to make this disable order mandatory, to provide certainty for plugin devs. Alternative solutions to this include disabling plugins in the opposite order that they were enabled in, but this doesn't allow for random plugin disables. This way seemed to make sense. --- src/plugin/PluginManager.php | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index acffae53e..4fdd7ee76 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -75,6 +75,9 @@ class PluginManager{ /** @var Plugin[] */ protected $enabledPlugins = []; + /** @var array> */ + private array $pluginDependents = []; + private bool $loadPluginsGuard = false; /** @@ -453,6 +456,15 @@ class PluginManager{ if($plugin->isEnabled()){ //the plugin may have disabled itself during onEnable() $this->enabledPlugins[$plugin->getDescription()->getName()] = $plugin; + foreach($plugin->getDescription()->getDepend() as $dependency){ + $this->pluginDependents[$dependency][$plugin->getDescription()->getName()] = true; + } + foreach($plugin->getDescription()->getSoftDepend() as $dependency){ + if(isset($this->plugins[$dependency])){ + $this->pluginDependents[$dependency][$plugin->getDescription()->getName()] = true; + } + } + (new PluginEnableEvent($plugin))->call(); return true; @@ -472,8 +484,19 @@ class PluginManager{ } public function disablePlugins() : void{ - foreach($this->getPlugins() as $plugin){ - $this->disablePlugin($plugin); + while(count($this->enabledPlugins) > 0){ + foreach($this->enabledPlugins as $plugin){ + if(!$plugin->isEnabled()){ + continue; //in case a plugin disabled another plugin + } + $name = $plugin->getDescription()->getName(); + if(isset($this->pluginDependents[$name]) && count($this->pluginDependents[$name]) > 0){ + $this->server->getLogger()->debug("Deferring disable of plugin $name due to dependent plugins still enabled: " . implode(", ", array_keys($this->pluginDependents[$name]))); + continue; + } + + $this->disablePlugin($plugin); + } } } @@ -483,6 +506,12 @@ class PluginManager{ (new PluginDisableEvent($plugin))->call(); unset($this->enabledPlugins[$plugin->getDescription()->getName()]); + foreach(Utils::stringifyKeys($this->pluginDependents) as $dependency => $dependentList){ + unset($this->pluginDependents[$dependency][$plugin->getDescription()->getName()]); + if(count($this->pluginDependents[$dependency]) === 0){ + unset($this->pluginDependents[$dependency]); + } + } $plugin->onEnableStateChange(false); $plugin->getScheduler()->shutdown();