From 5ba1716c53dc2ac7803dfaa7bb82d0157d864255 Mon Sep 17 00:00:00 2001 From: Giuseppe Criscione <18699708+giuscris@users.noreply.github.com> Date: Sat, 14 Jun 2025 00:15:09 +0200 Subject: [PATCH 1/4] Decouple config from defaults --- formwork/src/Cms/App.php | 12 +------ formwork/src/Cms/Site.php | 23 +++---------- formwork/src/Config/Config.php | 34 +++---------------- .../Panel/Controllers/OptionsController.php | 20 +++++++++-- .../Services/Loaders/ConfigServiceLoader.php | 2 +- 5 files changed, 27 insertions(+), 64 deletions(-) diff --git a/formwork/src/Cms/App.php b/formwork/src/Cms/App.php index 2f58f75ec..a6940bcc4 100644 --- a/formwork/src/Cms/App.php +++ b/formwork/src/Cms/App.php @@ -130,16 +130,6 @@ public function panel(): Panel return $this->container->get(Panel::class); } - /** - * Return default options - * - * @return array - */ - public function defaults(): array - { - return $this->config()->getDefaults('system'); - } - /** * @template T of object * @@ -189,7 +179,7 @@ private function loadServices(Container $container): void ->alias('config'); $container->define(ViewFactory::class) - ->parameter('methods', fn(Container $container) => $container->call(require SYSTEM_PATH . '/methods.php')); + ->parameter('methods', fn(Container $container, Config $config) => $container->call(require $config->get('system.views.methods.system'))); $container->define(Request::class, fn() => Request::fromGlobals()) ->alias('request'); diff --git a/formwork/src/Cms/Site.php b/formwork/src/Cms/Site.php index 6384f680c..937ead670 100644 --- a/formwork/src/Cms/Site.php +++ b/formwork/src/Cms/Site.php @@ -21,7 +21,6 @@ use Formwork\Schemes\Schemes; use Formwork\Templates\Templates; use Formwork\Users\Users; -use Formwork\Utils\Arr; use Formwork\Utils\FileSystem; use Formwork\Utils\Str; use Stringable; @@ -137,18 +136,6 @@ public function site(): Site return $this; } - /** - * Return site default data - * - * @return array - */ - public function defaults(): array - { - $defaults = $this->config->getDefaults('site'); - - return [...$defaults, ...Arr::reject($this->fields()->pluck('default'), fn($value) => $value === null)]; - } - public function parent(): Page|Site|null { return $this->parent ??= null; @@ -470,11 +457,8 @@ public function load(): void $this->fields = $this->scheme->fields(); $this->fields->setModel($this); - $this->data = [...$this->defaults(), ...$this->data]; $this->fields->setValues($this->data); - - $this->loadRouteAliases(); } /** @@ -558,11 +542,12 @@ protected function setContentPath(string $path): void /** * Load site aliases + * + * @param array $aliases */ - protected function loadRouteAliases(): void + protected function setRouteAliases(array $aliases): void { - $this->routeAliases = []; - foreach ($this->data['routeAliases'] as $from => $to) { + foreach ($aliases as $from => $to) { $this->routeAliases[trim((string) $from, '/')] = trim((string) $to, '/'); } } diff --git a/formwork/src/Config/Config.php b/formwork/src/Config/Config.php index 3c483da6f..17d1791f4 100644 --- a/formwork/src/Config/Config.php +++ b/formwork/src/Config/Config.php @@ -23,11 +23,9 @@ class Config implements ArraySerializable /** * @param array $config - * @param array $defaults */ final public function __construct( protected array $config = [], - protected array $defaults = [], ) {} /** @@ -38,14 +36,6 @@ public function has(string $key): bool return Arr::has($this->config, $key); } - /** - * Check if a key exists in the defaults - */ - public function hasDefaults(string $key): bool - { - return Arr::has($this->defaults, $key); - } - /** * Get a value from the config */ @@ -57,38 +47,24 @@ public function get(string $key, mixed $default = null): mixed return Arr::get($this->config, $key, $default); } - /** - * Get a value from the defaults - */ - public function getDefaults(string $key, mixed $default = null): mixed - { - if (!$this->resolved) { - throw new UnresolvedConfigException('Unresolved config'); - } - return Arr::get($this->defaults, $key, $default); - } - /** * Load config from a path */ public function loadFromPath(string $path, bool $defaultConfig = false): void { foreach (FileSystem::listFiles($path) as $file) { - $this->loadFile(FileSystem::joinPaths($path, $file), $defaultConfig); + $this->loadFile(FileSystem::joinPaths($path, $file)); } } /** * Load config from a file */ - public function loadFile(string $path, bool $defaultConfig = false): void + public function loadFile(string $path): void { if (FileSystem::isReadable($path) && FileSystem::extension($path) === 'yaml') { $name = FileSystem::name($path); $data = (array) Yaml::parseFile($path); - if ($defaultConfig) { - $this->defaults[$name] = isset($this->defaults[$name]) ? array_replace_recursive($this->defaults[$name], $data) : $data; - } $this->config[$name] = isset($this->config[$name]) ? array_replace_recursive($this->config[$name], $data) : $data; } } @@ -122,7 +98,6 @@ public function resolve(array $vars = []): void }); }; - $resolver($this->defaults); $resolver($this->config); $this->resolved = true; @@ -134,14 +109,13 @@ public function toArray(): array throw new UnresolvedConfigException('Unresolved config'); } return [ - 'config' => $this->config, - 'defaults' => $this->defaults, + 'config' => $this->config, ]; } public static function fromArray(array $data): static { - $static = new static($data['config'], $data['defaults']); + $static = new static($data['config']); $static->resolved = true; return $static; } diff --git a/formwork/src/Panel/Controllers/OptionsController.php b/formwork/src/Panel/Controllers/OptionsController.php index c6ee9d727..4f45b7673 100644 --- a/formwork/src/Panel/Controllers/OptionsController.php +++ b/formwork/src/Panel/Controllers/OptionsController.php @@ -2,6 +2,7 @@ namespace Formwork\Panel\Controllers; +use Formwork\Config\Config; use Formwork\Fields\FieldCollection; use Formwork\Http\RequestMethod; use Formwork\Http\Response; @@ -45,9 +46,8 @@ public function systemOptions(Schemes $schemes): Response $fields = $scheme->fields(); if ($this->request->method() === RequestMethod::POST) { - $data = $this->request->input(); $options = $this->config->get('system'); - $defaults = $this->app->defaults(); + $defaults = $this->defaultConfig()->get('system'); $fields->setValuesFromRequest($this->request, null)->validate(); $differ = $this->updateOptions('system', $fields, $options, $defaults); @@ -90,7 +90,7 @@ public function siteOptions(Schemes $schemes): Response if ($this->request->method() === RequestMethod::POST) { $options = $this->site->data(); - $defaults = $this->site->defaults(); + $defaults = $this->defaultConfig()->get('site'); $fields->setValuesFromRequest($this->request, null)->validate(); $differ = $this->updateOptions('site', $fields, $options, $defaults); @@ -118,6 +118,20 @@ public function siteOptions(Schemes $schemes): Response ])); } + /** + * Get default config + */ + private function defaultConfig(): Config + { + $config = new Config(); + $config->loadFromPath(SYSTEM_PATH . '/config/'); + $config->resolve([ + '%ROOT_PATH%' => ROOT_PATH, + '%SYSTEM_PATH%' => SYSTEM_PATH, + ]); + return $config; + } + /** * Update options of a given type with given data * diff --git a/formwork/src/Services/Loaders/ConfigServiceLoader.php b/formwork/src/Services/Loaders/ConfigServiceLoader.php index 07f1907ab..8e48864ec 100644 --- a/formwork/src/Services/Loaders/ConfigServiceLoader.php +++ b/formwork/src/Services/Loaders/ConfigServiceLoader.php @@ -12,7 +12,7 @@ public function load(Container $container): Config { $config = new Config(); - $config->loadFromPath(SYSTEM_PATH . '/config/', defaultConfig: true); + $config->loadFromPath(SYSTEM_PATH . '/config/'); $config->loadFromPath(ROOT_PATH . '/site/config/'); $config->resolve([ From 2f5aa876183665168604b096e26888bdfe89b2f8 Mon Sep 17 00:00:00 2001 From: Giuseppe Criscione <18699708+giuscris@users.noreply.github.com> Date: Sat, 14 Jun 2025 17:20:43 +0200 Subject: [PATCH 2/4] Allow php format config --- formwork/src/Config/Config.php | 28 ++++++++++++++----- .../Exceptions/ConfigLoadingException.php | 7 +++++ 2 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 formwork/src/Config/Exceptions/ConfigLoadingException.php diff --git a/formwork/src/Config/Config.php b/formwork/src/Config/Config.php index 17d1791f4..b154362a4 100644 --- a/formwork/src/Config/Config.php +++ b/formwork/src/Config/Config.php @@ -62,11 +62,27 @@ public function loadFromPath(string $path, bool $defaultConfig = false): void */ public function loadFile(string $path): void { - if (FileSystem::isReadable($path) && FileSystem::extension($path) === 'yaml') { - $name = FileSystem::name($path); - $data = (array) Yaml::parseFile($path); - $this->config[$name] = isset($this->config[$name]) ? array_replace_recursive($this->config[$name], $data) : $data; + if (!FileSystem::isFile($path)) { + throw new ConfigLoadingException(sprintf('Config file "%s" does not exist', $path)); } + + $name = FileSystem::name($path); + $extension = FileSystem::extension($path); + + switch ($extension) { + case 'php': + $data = (array) include $path; + break; + + case 'yaml': + $data = (array) Yaml::parseFile($path); + break; + + default: + throw new ConfigLoadingException(sprintf('Unsupported config file type "%s"', $extension)); + } + + $this->config[$name] = isset($this->config[$name]) ? array_replace_recursive($this->config[$name], $data) : $data; } /** @@ -108,9 +124,7 @@ public function toArray(): array if (!$this->resolved) { throw new UnresolvedConfigException('Unresolved config'); } - return [ - 'config' => $this->config, - ]; + return $this->config; } public static function fromArray(array $data): static diff --git a/formwork/src/Config/Exceptions/ConfigLoadingException.php b/formwork/src/Config/Exceptions/ConfigLoadingException.php new file mode 100644 index 000000000..4d2a17101 --- /dev/null +++ b/formwork/src/Config/Exceptions/ConfigLoadingException.php @@ -0,0 +1,7 @@ + Date: Sat, 14 Jun 2025 17:33:12 +0200 Subject: [PATCH 3/4] Cache resolved config --- formwork/src/Cms/App.php | 6 ++-- formwork/src/Config/Config.php | 7 ++-- .../Services/Loaders/ConfigServiceLoader.php | 34 +++++++++++++++---- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/formwork/src/Cms/App.php b/formwork/src/Cms/App.php index a6940bcc4..7db3aead6 100644 --- a/formwork/src/Cms/App.php +++ b/formwork/src/Cms/App.php @@ -174,6 +174,9 @@ private function loadServices(Container $container): void $container->define(self::class, $this); + $container->define(Request::class, fn() => Request::fromGlobals()) + ->alias('request'); + $container->define(Config::class) ->loader(ConfigServiceLoader::class) ->alias('config'); @@ -181,9 +184,6 @@ private function loadServices(Container $container): void $container->define(ViewFactory::class) ->parameter('methods', fn(Container $container, Config $config) => $container->call(require $config->get('system.views.methods.system'))); - $container->define(Request::class, fn() => Request::fromGlobals()) - ->alias('request'); - $container->define(ErrorsController::class) ->alias(ErrorsControllerInterface::class); diff --git a/formwork/src/Config/Config.php b/formwork/src/Config/Config.php index b154362a4..177dd5b80 100644 --- a/formwork/src/Config/Config.php +++ b/formwork/src/Config/Config.php @@ -2,6 +2,7 @@ namespace Formwork\Config; +use Formwork\Config\Exceptions\ConfigLoadingException; use Formwork\Config\Exceptions\ConfigResolutionException; use Formwork\Config\Exceptions\UnresolvedConfigException; use Formwork\Data\Contracts\ArraySerializable; @@ -16,16 +17,12 @@ class Config implements ArraySerializable */ protected const string INTERPOLATION_REGEX = '/\$(?!\$)\{([%a-z._]+)\}/i'; - /** - * Whether the config has been resolved - */ - protected bool $resolved = false; - /** * @param array $config */ final public function __construct( protected array $config = [], + protected bool $resolved = false, ) {} /** diff --git a/formwork/src/Services/Loaders/ConfigServiceLoader.php b/formwork/src/Services/Loaders/ConfigServiceLoader.php index 8e48864ec..b00f002c1 100644 --- a/formwork/src/Services/Loaders/ConfigServiceLoader.php +++ b/formwork/src/Services/Loaders/ConfigServiceLoader.php @@ -3,22 +3,42 @@ namespace Formwork\Services\Loaders; use Formwork\Config\Config; +use Formwork\Http\Request; +use Formwork\Parsers\Php; use Formwork\Services\Container; use Formwork\Services\ServiceLoaderInterface; +use Formwork\Utils\FileSystem; final class ConfigServiceLoader implements ServiceLoaderInterface { + public function __construct( + private Request $request, + ) {} + public function load(Container $container): Config { - $config = new Config(); + $cachePath = ROOT_PATH . '/cache/config/'; + $cacheFile = FileSystem::joinPaths($cachePath, 'config.' . $this->request->host() . '.php'); + + if (!FileSystem::isDirectory($cachePath, assertExists: false)) { + FileSystem::createDirectory($cachePath); + } + + if (FileSystem::exists($cacheFile) && !FileSystem::directoryModifiedSince(ROOT_PATH . '/site/config/', FileSystem::lastModifiedTime($cacheFile))) { + $config = new Config(require $cacheFile, resolved: true); + } else { + $config = new Config(); + + $config->loadFromPath(SYSTEM_PATH . '/config/'); + $config->loadFromPath(ROOT_PATH . '/site/config/'); - $config->loadFromPath(SYSTEM_PATH . '/config/'); - $config->loadFromPath(ROOT_PATH . '/site/config/'); + $config->resolve([ + '%ROOT_PATH%' => ROOT_PATH, + '%SYSTEM_PATH%' => SYSTEM_PATH, + ]); - $config->resolve([ - '%ROOT_PATH%' => ROOT_PATH, - '%SYSTEM_PATH%' => SYSTEM_PATH, - ]); + Php::encodeToFile($config->toArray(), $cacheFile); + } date_default_timezone_set($config->get('system.date.timezone')); From f82ea6ffe96834605909e9046db338afd175ac02 Mon Sep 17 00:00:00 2001 From: Giuseppe Criscione <18699708+giuscris@users.noreply.github.com> Date: Sat, 14 Jun 2025 21:43:58 +0200 Subject: [PATCH 4/4] Move config-related files to config --- formwork/{ => config/routes}/routes.php | 0 formwork/config/system.yaml | 10 ++++++++-- formwork/{ => config/views}/methods.php | 0 formwork/src/Panel/Panel.php | 4 ++-- formwork/src/Services/Loaders/PanelServiceLoader.php | 2 +- panel/{appconfig.php => config/app.php} | 0 panel/{ => config}/navigation.php | 0 panel/{ => config/routes}/routes.php | 0 panel/{ => config/views}/methods.php | 0 9 files changed, 11 insertions(+), 5 deletions(-) rename formwork/{ => config/routes}/routes.php (100%) rename formwork/{ => config/views}/methods.php (100%) rename panel/{appconfig.php => config/app.php} (100%) rename panel/{ => config}/navigation.php (100%) rename panel/{ => config/routes}/routes.php (100%) rename panel/{ => config/views}/methods.php (100%) diff --git a/formwork/routes.php b/formwork/config/routes/routes.php similarity index 100% rename from formwork/routes.php rename to formwork/config/routes/routes.php diff --git a/formwork/config/system.yaml b/formwork/config/system.yaml index c579dc61c..9ae743f58 100644 --- a/formwork/config/system.yaml +++ b/formwork/config/system.yaml @@ -88,6 +88,9 @@ panel: assets: '${system.panel.path}/assets' logs: '${system.panel.path}/logs' modals: '${system.panel.path}/modals' + config: + app: '${system.panel.path}/config/app.php' + navigation: '${system.panel.path}/config/navigation.php' users: paths: @@ -97,8 +100,8 @@ users: routes: files: - panel: '${system.panel.path}/routes.php' - system: '${%SYSTEM_PATH%}/routes.php' + panel: '${system.panel.path}/config/routes/routes.php' + system: '${%SYSTEM_PATH%}/config/routes/routes.php' schemes: paths: @@ -141,3 +144,6 @@ views: paths: panel: '${system.panel.path}/views' system: '${%SYSTEM_PATH%}/views' + methods: + panel: '${system.panel.path}/config/views/methods.php' + system: '${%SYSTEM_PATH%}/config/views/methods.php' diff --git a/formwork/methods.php b/formwork/config/views/methods.php similarity index 100% rename from formwork/methods.php rename to formwork/config/views/methods.php diff --git a/formwork/src/Panel/Panel.php b/formwork/src/Panel/Panel.php index d447af871..b17d439ce 100644 --- a/formwork/src/Panel/Panel.php +++ b/formwork/src/Panel/Panel.php @@ -111,7 +111,7 @@ public function navigation(): NavigationItemCollection { $translation = $this->translations->getCurrent(); if (!isset($this->navigation)) { - $items = $this->container->call(require FileSystem::joinPaths($this->path(), 'navigation.php'), [ + $items = $this->container->call(require $this->config->get('system.panel.config.navigation'), [ 'translation' => $translation, ]); $this->navigation = new NavigationItemCollection(); @@ -228,7 +228,7 @@ public function getCsrfTokenName(): string */ public function getAppConfig(): array { - return $this->container->call(require FileSystem::joinPaths($this->path(), 'appconfig.php'), [ + return $this->container->call(require $this->config->get('system.panel.config.app'), [ 'translation' => $this->translations->getCurrent(), ]); } diff --git a/formwork/src/Services/Loaders/PanelServiceLoader.php b/formwork/src/Services/Loaders/PanelServiceLoader.php index 176065c5c..6d985e911 100644 --- a/formwork/src/Services/Loaders/PanelServiceLoader.php +++ b/formwork/src/Services/Loaders/PanelServiceLoader.php @@ -53,7 +53,7 @@ public function load(Container $container): Panel */ public function onResolved(object $service, Container $container): void { - $this->viewFactory->setMethods($container->call(require FileSystem::joinPaths($service->path(), 'methods.php'))); + $this->viewFactory->setMethods($container->call(require $this->config->get('system.views.methods.panel'))); $this->schemes->loadFromPath($this->config->get('system.schemes.paths.panel')); diff --git a/panel/appconfig.php b/panel/config/app.php similarity index 100% rename from panel/appconfig.php rename to panel/config/app.php diff --git a/panel/navigation.php b/panel/config/navigation.php similarity index 100% rename from panel/navigation.php rename to panel/config/navigation.php diff --git a/panel/routes.php b/panel/config/routes/routes.php similarity index 100% rename from panel/routes.php rename to panel/config/routes/routes.php diff --git a/panel/methods.php b/panel/config/views/methods.php similarity index 100% rename from panel/methods.php rename to panel/config/views/methods.php