diff --git a/.tools/psalm/baseline.xml b/.tools/psalm/baseline.xml index f0a9eeac41..dfc62ffdb0 100644 --- a/.tools/psalm/baseline.xml +++ b/.tools/psalm/baseline.xml @@ -2075,9 +2075,6 @@ - - - @@ -2101,9 +2098,6 @@ - - ]]> - diff --git a/boot/addons.php b/boot/addons.php index 1c36b42163..fb81f245e8 100644 --- a/boot/addons.php +++ b/boot/addons.php @@ -2,6 +2,7 @@ use Redaxo\Core\AbstractProject; use Redaxo\Core\Addon\Addon; +use Redaxo\Core\Addon\AddonManager; use Redaxo\Core\Core; use Redaxo\Core\ExtensionPoint\Extension; use Redaxo\Core\ExtensionPoint\ExtensionPoint; @@ -17,7 +18,7 @@ if (Core::isSetup() || Core::isSafeMode()) { $packageOrder = array_keys(Addon::getSetupAddons()); } else { - $packageOrder = Core::getPackageOrder(); + $packageOrder = AddonManager::getAddonOrder(); } // in the first run, we register all folders for class- and fragment-loading, diff --git a/boot/console.php b/boot/console.php index a14b580c83..a93681ab76 100644 --- a/boot/console.php +++ b/boot/console.php @@ -2,6 +2,7 @@ use Redaxo\Core\AbstractProject; use Redaxo\Core\Addon\Addon; +use Redaxo\Core\Addon\AddonManager; use Redaxo\Core\Console\Application; use Redaxo\Core\Console\Command\ListCommand; use Redaxo\Core\Console\CommandLoader; @@ -25,7 +26,7 @@ Addon::initialize(!Core::isSetup()); if (!Core::isSetup()) { - foreach (Core::getPackageOrder() as $packageId) { + foreach (AddonManager::getAddonOrder() as $packageId) { Addon::require($packageId)->enlist(); } diff --git a/rector.php b/rector.php index 0765c5b47c..39cdf708ca 100644 --- a/rector.php +++ b/rector.php @@ -443,6 +443,8 @@ new MethodCallRename(Cronjob\CronjobManager::class, 'hasManager', 'hasExecutor'), ]) ->withConfiguredRule(RenameStaticMethodRector::class, [ + new RenameStaticMethod(Core::class, 'getPackageConfig', Addon\AddonManager::class, 'getAddonConfig'), + new RenameStaticMethod(Core::class, 'getPackageOrder', Addon\AddonManager::class, 'getAddonOrder'), new RenameStaticMethod(Core::class, 'getVersionHash', Util\Version::class, 'gitHash'), new RenameStaticMethod(Util\Str::class, 'versionSplit', Util\Version::class, 'split'), new RenameStaticMethod(Util\Str::class, 'versionCompare', Util\Version::class, 'compare'), diff --git a/src/Addon/Addon.php b/src/Addon/Addon.php index 1805686e7e..8ec3f19c92 100644 --- a/src/Addon/Addon.php +++ b/src/Addon/Addon.php @@ -509,7 +509,7 @@ final public static function getSetupAddons(): array final public static function initialize(bool $dbExists = true): void { if ($dbExists) { - $config = Core::getPackageConfig(); + $config = AddonManager::getAddonConfig(); } else { $config = []; foreach (Core::getProperty('setup_addons') as $addon) { diff --git a/src/Addon/AddonManager.php b/src/Addon/AddonManager.php index 2f7c8bc80a..258c0d233e 100644 --- a/src/Addon/AddonManager.php +++ b/src/Addon/AddonManager.php @@ -7,7 +7,6 @@ use Redaxo\Core\Backend\Controller; use Redaxo\Core\Base\FactoryTrait; use Redaxo\Core\Config; -use Redaxo\Core\Core; use Redaxo\Core\Exception\RuntimeException; use Redaxo\Core\Exception\UserMessageException; use Redaxo\Core\Filesystem\Dir; @@ -24,13 +23,22 @@ use function is_string; use function sprintf; +use const JSON_PRETTY_PRINT; use const JSON_THROW_ON_ERROR; +use const JSON_UNESCAPED_SLASHES; +use const JSON_UNESCAPED_UNICODE; +/** + * @phpstan-type TAddonConfig array, install: bool, status: bool}> + * @phpstan-type TAddonOrder list + */ class AddonManager { use FactoryTrait; - protected bool $generatePackageOrder = true; + /** @var array{config: TAddonConfig, order: TAddonOrder}|null */ + private static ?array $addonsData = null; + protected string $message = ''; final protected function __construct( @@ -97,9 +105,7 @@ public function install(): bool $this->addon->setProperty('status', true); } static::saveConfig(); - if ($this->generatePackageOrder) { - self::generatePackageOrder(); - } + self::generateAddonOrder(); foreach ($this->addon->getProperty('default_config', []) as $key => $value) { if (!$this->addon->hasConfig($key)) { @@ -209,8 +215,8 @@ public function activate(): bool $this->addon->setProperty('status', true); static::saveConfig(); } - if (true === $state && $this->generatePackageOrder) { - self::generatePackageOrder(); + if (true === $state) { + self::generateAddonOrder(); } } else { $state = $this->i18n('not_installed', $this->addon->name); @@ -243,9 +249,7 @@ public function deactivate(): bool // clear cache of addon $this->addon->clearCache(); - if ($this->generatePackageOrder) { - self::generatePackageOrder(); - } + self::generateAddonOrder(); $this->message = $this->i18n('deactivated', $this->addon->name); return true; @@ -330,16 +334,28 @@ protected function i18n(string $key, string|int ...$replacements): string return I18n::msg($fullKey, ...$replacements); } + /** @return TAddonConfig */ + public static function getAddonConfig(): array + { + return self::loadAddonsData()['config']; + } + + /** @return TAddonOrder */ + public static function getAddonOrder(): array + { + return self::loadAddonsData()['order']; + } + /** Generates the addon order. */ - public static function generatePackageOrder(): void + public static function generateAddonOrder(): void { - /** @var list $early */ + /** @var list $early */ $early = []; - /** @var list $normal */ + /** @var list $normal */ $normal = []; - /** @var list $late */ + /** @var list $late */ $late = []; - /** @var array> $requires */ + /** @var array> $requires */ $requires = []; $add = static function ($id) use (&$add, &$normal, &$requires) { @@ -369,7 +385,10 @@ public static function generatePackageOrder(): void } } } - Core::setConfig('package-order', array_merge($early, $normal, array_keys($requires), $late)); + + /** @var TAddonOrder $order */ + $order = array_merge($early, $normal, array_keys($requires), $late); + self::saveAddonsData(order: $order); } /** Saves the addon config. */ @@ -381,13 +400,14 @@ protected static function saveConfig(): void $config[$addonName]['install'] = $addon->isInstalled(); $config[$addonName]['status'] = $addon->isAvailable(); } - Core::setConfig('package-config', $config); + + self::saveAddonsData(config: $config); } /** Synchronizes the addons with the file system. */ public static function synchronizeWithFileSystem(): void { - $config = Core::getPackageConfig(); + $config = self::getAddonConfig(); $registeredAddons = Addon::getRegisteredAddons(); $packages = self::getComposerPackages(); $addonClasses = self::getAddonClasses(); @@ -413,10 +433,47 @@ public static function synchronizeWithFileSystem(): void } ksort($config); - Core::setConfig('package-config', $config); + self::saveAddonsData(config: $config); Addon::initialize(); } + /** @return array{config: TAddonConfig, order: TAddonOrder} */ + private static function loadAddonsData(): array + { + if (null !== self::$addonsData) { + return self::$addonsData; + } + + $file = Path::coreData('addons.json'); + if (!is_file($file)) { + return self::$addonsData = ['config' => [], 'order' => []]; + } + + /** @var array{config: TAddonConfig, order: TAddonOrder} $data */ + $data = File::getCache($file) ?? ['config' => [], 'order' => []]; + + return self::$addonsData = $data; + } + + /** + * @param TAddonConfig|null $config + * @param TAddonOrder|null $order + */ + private static function saveAddonsData(?array $config = null, ?array $order = null): void + { + $data = self::loadAddonsData(); + + if (null !== $config) { + $data['config'] = $config; + } + if (null !== $order) { + $data['order'] = $order; + } + + self::$addonsData = $data; + File::put(Path::coreData('addons.json'), (string) json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + /** * Returns the addon-class mapping for all registered `redaxo-addon` packages, read from `vendor/composer/installed.json`. * diff --git a/src/Console/Application.php b/src/Console/Application.php index 866c9802fb..f322a68da8 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -8,6 +8,7 @@ use ParseError; use Redaxo\Core\AbstractProject; use Redaxo\Core\Addon\Addon; +use Redaxo\Core\Addon\AddonManager; use Redaxo\Core\Console\Command\AbstractCommand; use Redaxo\Core\Console\Command\OnlySetupAddonsInterface; use Redaxo\Core\Console\Command\StandaloneInterface; @@ -119,7 +120,7 @@ private function loadPackages(AbstractCommand $command): void if (!Core::isSetup()) { // boot all known packages in the defined order // which reflects dependencies before consumers - foreach (Core::getPackageOrder() as $packageId) { + foreach (AddonManager::getAddonOrder() as $packageId) { Addon::require($packageId)->boot(); } } diff --git a/src/Console/Command/MigrateCommand.php b/src/Console/Command/MigrateCommand.php index 0db55f92a2..94b2f0e9eb 100644 --- a/src/Console/Command/MigrateCommand.php +++ b/src/Console/Command/MigrateCommand.php @@ -4,7 +4,6 @@ use Redaxo\Core\Addon\Addon; use Redaxo\Core\Addon\AddonManager; -use Redaxo\Core\Core; use Redaxo\Core\Database\Sql; use Redaxo\Core\Exception\UserMessageException; use Redaxo\Core\Filesystem\File; @@ -47,7 +46,7 @@ public function __invoke(SymfonyStyle $io): int // align registered addons with composer state: drop config of orphaned addons, register new ones AddonManager::synchronizeWithFileSystem(); - $packages = array_merge(['core'], Core::getPackageOrder()); + $packages = array_merge(['core'], AddonManager::getAddonOrder()); $io->section('Migrating'); diff --git a/src/Core.php b/src/Core.php index 62da39d14e..b766145a21 100644 --- a/src/Core.php +++ b/src/Core.php @@ -3,7 +3,6 @@ namespace Redaxo\Core; use Composer\InstalledVersions; -use Redaxo\Core\Addon\Addon; use Redaxo\Core\Console\Application; use Redaxo\Core\Database\Configuration as DatabaseConfiguration; use Redaxo\Core\Exception\InvalidArgumentException; @@ -455,21 +454,6 @@ public static function getVersion(?string $format = null): string return $version; } - /** - * @return array, install: bool, status: bool}> - * @psalm-suppress MixedReturnTypeCoercion - */ - public static function getPackageConfig(): array - { - return Type::array(self::getConfig('package-config', [])); - } - - /** @return list */ - public static function getPackageOrder(): array - { - return Type::array(self::getConfig('package-order', [])); - } - /** * Returns the title tag and if the property "use_accesskeys" is true, the accesskey tag. * diff --git a/src/Setup/Importer.php b/src/Setup/Importer.php index a2e669ad19..6cd5589a4f 100644 --- a/src/Setup/Importer.php +++ b/src/Setup/Importer.php @@ -172,10 +172,10 @@ private static function reinstallPackages(): string AddonManager::synchronizeWithFileSystem(); // enlist activated packages to ensure that all their classess are known in autoloader and can be referenced in other package's install.php - foreach (Core::getPackageOrder() as $packageId) { + foreach (AddonManager::getAddonOrder() as $packageId) { Addon::require($packageId)->enlist(); } - foreach (Core::getPackageOrder() as $packageId) { + foreach (AddonManager::getAddonOrder() as $packageId) { $package = Addon::require($packageId); $manager = AddonManager::factory($package);