From 0b7b27409d5d7f98cdb72bb085582982efde0fb1 Mon Sep 17 00:00:00 2001 From: fs Date: Thu, 19 Mar 2026 08:36:37 +0100 Subject: [PATCH] refactor: remove slot-type validation from ModuleRegistry Drop UI_SLOT_REQUIRED_KEYS constant and validateUiSlotContributionByType() (-97 lines). Slot field requirements are now enforced only by architecture tests (testUiSlotContributionsHaveRequiredKeys, testPanelTemplatesExist) and by the rendering templates themselves. The registry stays responsible for key uniqueness, ordering, and module_id tagging. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/App/Module/ModuleRegistry.php | 97 ------------------------------- 1 file changed, 97 deletions(-) diff --git a/lib/App/Module/ModuleRegistry.php b/lib/App/Module/ModuleRegistry.php index 74dd03e..538a0c1 100644 --- a/lib/App/Module/ModuleRegistry.php +++ b/lib/App/Module/ModuleRegistry.php @@ -16,17 +16,6 @@ use RuntimeException; */ final class ModuleRegistry { - /** @var array> */ - private const UI_SLOT_REQUIRED_KEYS = [ - 'aside.tab_panel' => ['key', 'label', 'icon', 'permission', 'panel_template'], - 'search.resource_item' => ['key', 'label', 'base_url', 'permission'], - 'user.edit.aside_action' => ['key', 'type', 'label', 'permission'], - 'topbar.right_item' => ['key', 'template'], - 'layout.body_end_template' => ['key', 'template'], - 'layout.head_style' => ['key', 'path'], - 'runtime.component' => ['key', 'script'], - ]; - /** @var array keyed by module id */ private array $modules = []; @@ -485,17 +474,6 @@ final class ModuleRegistry } } - foreach (self::UI_SLOT_REQUIRED_KEYS[$slotName] ?? [] as $requiredKey) { - $requiredValue = trim((string) ($rawContribution[$requiredKey] ?? '')); - if ($requiredValue === '') { - throw new RuntimeException( - "UI slot contribution for slot '{$slotName}' in module '{$moduleId}' is missing required key '{$requiredKey}'." - ); - } - } - - $this->validateUiSlotContributionByType($slotName, $rawContribution, $moduleId, $key); - $normalized = $rawContribution; $normalized['key'] = $key; $normalized['order'] = (int) ($rawContribution['order'] ?? 100); @@ -505,81 +483,6 @@ final class ModuleRegistry return $normalized; } - /** - * @param array $rawContribution - */ - private function validateUiSlotContributionByType( - string $slotName, - array $rawContribution, - string $moduleId, - string $slotKey - ): void { - if (in_array($slotName, ['aside.tab_panel', 'topbar.right_item', 'layout.body_end_template'], true)) { - $templatePath = trim((string) ($rawContribution['panel_template'] ?? $rawContribution['template'] ?? '')); - if ($templatePath === '' || !is_file($templatePath)) { - throw new RuntimeException( - "UI slot contribution '{$slotKey}' for slot '{$slotName}' in module '{$moduleId}' references missing template '{$templatePath}'." - ); - } - } - - if ($slotName === 'layout.head_style') { - $path = trim((string) ($rawContribution['path'] ?? '')); - if ($path === '' || str_contains($path, '..')) { - throw new RuntimeException( - "UI slot contribution '{$slotKey}' for slot 'layout.head_style' in module '{$moduleId}' has invalid path '{$path}'." - ); - } - } - - if ($slotName === 'user.edit.aside_action') { - $type = strtolower(trim((string) ($rawContribution['type'] ?? 'link'))); - if ($type !== 'link' && $type !== 'form') { - throw new RuntimeException( - "UI slot contribution '{$slotKey}' for slot 'user.edit.aside_action' in module '{$moduleId}' has invalid type '{$type}'." - ); - } - if ($type === 'form') { - $action = trim((string) ($rawContribution['action'] ?? '')); - if ($action === '') { - throw new RuntimeException( - "UI slot contribution '{$slotKey}' for slot 'user.edit.aside_action' in module '{$moduleId}' requires non-empty 'action' for type=form." - ); - } - } else { - $href = trim((string) ($rawContribution['href'] ?? '')); - if ($href === '') { - throw new RuntimeException( - "UI slot contribution '{$slotKey}' for slot 'user.edit.aside_action' in module '{$moduleId}' requires non-empty 'href' for type=link." - ); - } - } - } - - if ($slotName === 'runtime.component') { - $script = trim((string) ($rawContribution['script'] ?? '')); - if ($script === '' || str_contains($script, '..')) { - throw new RuntimeException( - "UI slot contribution '{$slotKey}' for slot 'runtime.component' in module '{$moduleId}' has invalid script '{$script}'." - ); - } - - $phase = strtolower(trim((string) ($rawContribution['phase'] ?? 'late'))); - if (!in_array($phase, ['early', 'default', 'late'], true)) { - throw new RuntimeException( - "UI slot contribution '{$slotKey}' for slot 'runtime.component' in module '{$moduleId}' has invalid phase '{$phase}'." - ); - } - - $configPath = trim((string) ($rawContribution['config_path'] ?? '')); - if ($configPath !== '' && !preg_match('/^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z0-9_]+)+$/', $configPath)) { - throw new RuntimeException( - "UI slot contribution '{$slotKey}' for slot 'runtime.component' in module '{$moduleId}' has invalid config_path '{$configPath}'." - ); - } - } - } - // ─── Getters ──────────────────────────────────────────────────────── /** @return array */