From 4d63d7de4a7d980d2cae8e33c702c061bf487cd5 Mon Sep 17 00:00:00 2001 From: Gregor Harlan Date: Sun, 31 May 2026 15:46:35 +0200 Subject: [PATCH 1/4] refactor!: replace package.yml default_config with Addon::$defaultConfig property The default_config setting was the last documented value in package.yml. Addons now declare their config defaults via a public protected(set) $defaultConfig property (overridable in subclasses), mirroring how $load is handled. --- schemas/package.json | 7 +------ src/Addon/Addon.php | 7 +++++++ src/Addon/AddonManager.php | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/schemas/package.json b/schemas/package.json index 8f883e909d..2af904c5fa 100644 --- a/schemas/package.json +++ b/schemas/package.json @@ -3,11 +3,6 @@ "$id": "https://redaxo.org/schemas/package.json", "title": "JSON schema for REDAXO package.yml", "type": "object", - "properties": { - "default_config": { - "description": "Default values for Redaxo\\Core\\Config", - "type": "object" - } - }, + "properties": {}, "additionalProperties": true } diff --git a/src/Addon/Addon.php b/src/Addon/Addon.php index eb9e2cda31..f1ae1330c9 100644 --- a/src/Addon/Addon.php +++ b/src/Addon/Addon.php @@ -62,6 +62,13 @@ abstract class Addon /** Loading position relative to other addons during boot. Override to load this addon early or late. */ public protected(set) LoadOrder $load = LoadOrder::Normal; + /** + * Default config values applied on install (only for keys that are not already set). Override to provide defaults. + * + * @var array + */ + public protected(set) array $defaultConfig = []; + /** Lifecycle state of the addon. */ public private(set) AddonState $state = AddonState::Uninstalled; diff --git a/src/Addon/AddonManager.php b/src/Addon/AddonManager.php index 9b8f8c69be..2d25e247d9 100644 --- a/src/Addon/AddonManager.php +++ b/src/Addon/AddonManager.php @@ -91,7 +91,7 @@ public function install(): bool $this->addon->install(); $successMessage = (string) $this->addon->getProperty('successmsg', ''); - foreach ($this->addon->getProperty('default_config', []) as $key => $value) { + foreach ($this->addon->defaultConfig as $key => $value) { if (!$this->addon->hasConfig($key)) { $this->addon->setConfig($key, $value); } From 1cb582a80ab114900db3fa8e7eeec4ec26ca107d Mon Sep 17 00:00:00 2001 From: Gregor Harlan Date: Sun, 31 May 2026 16:11:15 +0200 Subject: [PATCH 2/4] refactor!: drop package.yml loading and its caching package.yml is no longer read at all. The property getters/setters (get/has/set/removeProperty) remain as an in-memory store (e.g. successmsg set from within the install() hook), but loadProperties(), the package.yml properties cache, the install-time package.yml parse check and the package.yml JSON schema are gone. --- .idea/jsonSchemas.xml | 18 -------- AGENTS.md | 2 +- lang/de_de.lang | 1 - lang/en_gb.lang | 1 - pages/system/report.html.php | 2 +- schemas/package.json | 8 ---- src/Addon/Addon.php | 86 ------------------------------------ src/Addon/AddonManager.php | 9 ---- 8 files changed, 2 insertions(+), 125 deletions(-) delete mode 100644 schemas/package.json diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml index 5534e9f5c0..8fcbb5a86e 100644 --- a/.idea/jsonSchemas.xml +++ b/.idea/jsonSchemas.xml @@ -22,24 +22,6 @@ - - - - - - - diff --git a/AGENTS.md b/AGENTS.md index 19b86ff7cb..60c201f01a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,7 @@ This repository contains the **REDAXO core** under `src/` (`Redaxo\Core\`), back ### Key concepts - **`Core` static class** (`src/Core.php`) — central application registry for paths, config, request, current user. -- **Addon system** — `Addon` / `AddonManager` (`src/Addon/`). Each addon has a `package.yml` (metadata + page config) plus optional `boot.php` (runtime init) and `install.php` (schema/data setup — must be idempotent, runs on every `console migrate`). +- **Addon system** — `Addon` / `AddonManager` (`src/Addon/`). Each addon is a subclass of `Addon`, registered via composer.json `extra.redaxo.addon-class`. Metadata comes from composer.json; integration happens through overridable hooks — `boot()` (runtime init), `install()`/`uninstall()` (schema/data setup — must be idempotent, runs on every `console migrate`), `getPages()` (backend pages) — plus the `$load` and `$defaultConfig` properties. - **Extension points** — REDAXO's hook/event system: register listeners with `Extension::register('NAME', ...)`, fire points with `Extension::registerPoint(new ExtensionPoint(...))`. Classes live under `Redaxo\Core\ExtensionPoint`. This is the primary integration mechanism for addons. - **Fragments** (`fragments/`) — template snippets rendered via `Fragment` (`src/View/Fragment.php`). - **Boot flow** — `AbstractProject` (Symfony `RuntimeInterface`) drives boot via `boot/core.php` → `boot/addons.php` → environment entry (`boot/backend.php`, `boot/frontend.php`, `boot/console.php`). diff --git a/lang/de_de.lang b/lang/de_de.lang index de82267b9f..01a675cfa6 100644 --- a/lang/de_de.lang +++ b/lang/de_de.lang @@ -327,7 +327,6 @@ package_activate = aktivieren package_yes = ja package_no = nein package_caption = Liste der verfügbaren Packages -package_invalid_yml_file = Die package.yml ist invalid: package_install_cant_copy_files = Fehler beim Kopieren des /assets Ordners! package_install_cant_delete_files = Fehler beim Löschen des /assets Ordners! package_jump_to = Zu "{0}" springen diff --git a/lang/en_gb.lang b/lang/en_gb.lang index 012cea274c..a63dc2bf81 100644 --- a/lang/en_gb.lang +++ b/lang/en_gb.lang @@ -326,7 +326,6 @@ package_activate = activate package_yes = yes package_no = no package_caption = List of available packages -package_invalid_yml_file = The file package.yml is invalid: package_install_cant_copy_files = An error occurred when copying the /assets folder! package_install_cant_delete_files = An error occurred when deleting the /assets folder! package_jump_to = Jump to "{0}" diff --git a/pages/system/report.html.php b/pages/system/report.html.php index 362f490951..56de286c99 100644 --- a/pages/system/report.html.php +++ b/pages/system/report.html.php @@ -23,7 +23,7 @@ foreach ($group as $label => $value) { if (SystemReport::TITLE_PACKAGES === $title || SystemReport::TITLE_REDAXO === $title) { if (null === $value) { - throw new RuntimeException('Package ' . $label . ' does not define a proper version in its package.yml.'); + throw new RuntimeException('Package ' . $label . ' does not define a proper version in its composer.json.'); } if (Version::isUnstable($value)) { $value = ' ' . escape($value); diff --git a/schemas/package.json b/schemas/package.json deleted file mode 100644 index 2af904c5fa..0000000000 --- a/schemas/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://redaxo.org/schemas/package.json", - "title": "JSON schema for REDAXO package.yml", - "type": "object", - "properties": {}, - "additionalProperties": true -} diff --git a/src/Addon/Addon.php b/src/Addon/Addon.php index f1ae1330c9..9a03b1cf90 100644 --- a/src/Addon/Addon.php +++ b/src/Addon/Addon.php @@ -16,7 +16,6 @@ use Redaxo\Core\Filesystem\Path; use Redaxo\Core\Filesystem\Url; use Redaxo\Core\Translation\I18n; -use Redaxo\Core\Util\Exception\YamlParseException; use Redaxo\Core\Util\Formatter; use Redaxo\Core\Util\Type; use Redaxo\Core\View\Fragment; @@ -33,10 +32,6 @@ abstract class Addon { - final public const string FILE_PACKAGE = 'package.yml'; - - private const string PROPERTIES_CACHE_FILE = 'packages.cache'; - /** * Array of all addons. * @@ -79,9 +74,6 @@ abstract class Addon */ private array $properties = []; - /** Flag whether the properties of package.yml are loaded. */ - private bool $propertiesLoaded = false; - /** @var array|null */ private ?array $composerJson = null; @@ -212,9 +204,6 @@ final public function getProperty(string $key, mixed $default = null): mixed /** @param non-empty-string $key */ final public function hasProperty(string $key): bool { - if (!isset($this->properties[$key]) && !$this->propertiesLoaded) { - $this->loadProperties(); - } return isset($this->properties[$key]); } @@ -328,75 +317,6 @@ final public function i18n(string $key, string|int ...$replacements): string return I18n::msg($key, ...$replacements); } - /** Loads the properties of package.yml. */ - final public function loadProperties(bool $force = false): void - { - $file = $this->getPath(self::FILE_PACKAGE); - if (!is_file($file)) { - $this->propertiesLoaded = true; - return; - } - - /** @var array}>|null $cache */ - static $cache = null; - if (null === $cache) { - /** @var array}> $cache */ - $cache = File::getCache(Path::coreCache(self::PROPERTIES_CACHE_FILE)); - } - $id = $this->name; - - if ($force) { - unset($cache[$id]); - } - - $isCached = isset($cache[$id]); - $isBackendAdmin = Core::isBackend() && Core::getUser()?->admin; - if (!$isCached || (Core::getConsole() || $isBackendAdmin) && $cache[$id]['timestamp'] < filemtime($file)) { - try { - $properties = File::getConfig($file); - - $cache[$id]['timestamp'] = filemtime($file); - $cache[$id]['data'] = $properties; - - /** @var bool $registeredShutdown */ - static $registeredShutdown = false; - if (!$registeredShutdown) { - $registeredShutdown = true; - register_shutdown_function(static function () use (&$cache) { - foreach ($cache as $addon => $_) { - if (!self::exists($addon)) { - unset($cache[$addon]); - } - } - File::putCache(Path::coreCache(self::PROPERTIES_CACHE_FILE), $cache); - }); - } - } catch (YamlParseException $exception) { - if ($this->isInstalled()) { - throw $exception; - } - - $properties = []; - } - } else { - $properties = $cache[$id]['data']; - } - - $this->properties = []; - if ($properties) { - foreach ($properties as $key => $value) { - $key = Type::string($key); - if ('supportpage' !== $key) { - $value = I18n::translateArray($value, false, $this->i18n(...)); - } elseif (null !== $value && !preg_match('@^https?://@i', $value)) { - $value = 'https://' . $value; - } - $this->properties[$key] = $value; - } - } - $this->propertiesLoaded = true; - } - final public function getLicense(): ?string { /** @var string|list|null $license */ @@ -430,12 +350,6 @@ final public function clearCache(): void throw new RuntimeException('Addon cache directory "' . $cacheDir . '" is not writable.'); } - $cache = File::getCache($path = Path::coreCache(self::PROPERTIES_CACHE_FILE)); - if ($cache) { - unset($cache[$this->name]); - File::putCache($path, $cache); - } - Extension::registerPoint(new AddonCacheDeleted($this)); } diff --git a/src/Addon/AddonManager.php b/src/Addon/AddonManager.php index 2d25e247d9..bf3d1aa0be 100644 --- a/src/Addon/AddonManager.php +++ b/src/Addon/AddonManager.php @@ -14,7 +14,6 @@ use Redaxo\Core\Filesystem\Path; use Redaxo\Core\Filesystem\Url; use Redaxo\Core\Translation\I18n; -use Redaxo\Core\Util\Exception\YamlParseException; use Redaxo\Core\Util\Str; use Redaxo\Core\Util\Type; @@ -66,14 +65,6 @@ public function getMessage(): string public function install(): bool { try { - // check package.yml - $addonFile = $this->addon->getPath(Addon::FILE_PACKAGE); - try { - File::getConfig($addonFile); - } catch (YamlParseException $e) { - throw new UserMessageException($this->i18n('invalid_yml_file') . ' ' . $e->getMessage()); - } - // check requirements and conflicts $message = ''; if (!$this->checkRequirements()) { From 8f3001e39cf9abf8cc0bf6156a85bdeb73588e73 Mon Sep 17 00:00:00 2001 From: gharlan <330436+gharlan@users.noreply.github.com> Date: Sun, 31 May 2026 14:21:22 +0000 Subject: [PATCH 3/4] chore: update psalm baseline --- .tools/psalm/baseline.xml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.tools/psalm/baseline.xml b/.tools/psalm/baseline.xml index a505cf08d8..1fc2bbf9a0 100644 --- a/.tools/psalm/baseline.xml +++ b/.tools/psalm/baseline.xml @@ -1451,31 +1451,17 @@ - - - - - - - - - - - - - - From 731e47d8a53d780ebaf04923301e7fce61a9bd76 Mon Sep 17 00:00:00 2001 From: Gregor Harlan Date: Sun, 31 May 2026 16:32:21 +0200 Subject: [PATCH 4/4] update docs --- src/Console/CommandLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/CommandLoader.php b/src/Console/CommandLoader.php index 236d50f3a6..88527f6173 100644 --- a/src/Console/CommandLoader.php +++ b/src/Console/CommandLoader.php @@ -22,7 +22,7 @@ /** * Discovers all commands that are marked with the {@see AsCommand} attribute and extend {@see AbstractCommand}, * both in the core and in the active addons. Addons therefore register their commands simply by adding the - * attribute to a command class — no `package.yml` configuration is required. + * attribute to a command class. * * Commands are returned as {@see LazyCommand}, so that listing the commands (e.g. `console list`) does not * instantiate every command class — only the command that is actually executed is instantiated.