register($container); } // ── Module registrars (run after all Core registrars) ──────────────── // Module registrars MUST NOT overwrite existing container keys. // The ModuleRegistry is stored in the container for downstream access. $modulesConfig = is_file(__DIR__ . '/../../config/modules.php') ? (array) require __DIR__ . '/../../config/modules.php' : []; $enabledModules = ModuleRegistry::resolveEnabledModules($modulesConfig); $modulesDir = dirname(__DIR__, 2) . '/modules'; $moduleRegistry = ModuleRegistry::boot($modulesDir, $enabledModules); $container->set(ModuleRegistry::class, static fn (): ModuleRegistry => $moduleRegistry); $container->set(ModuleEventDispatcher::class, static fn () => new ModuleEventDispatcher( $moduleRegistry->getEventListeners(), $container, $container->has(AuditRecorderInterface::class) ? $container->get(AuditRecorderInterface::class) : null )); // Disallow overriding existing core bindings from module registrars. $container->protectExistingBindings(); foreach ($moduleRegistry->getContainerRegistrars() as $registrarClass) { if (!class_exists($registrarClass)) { throw new RuntimeException("Module container registrar class not found: {$registrarClass}"); } $registrarInstance = new $registrarClass(); if (!$registrarInstance instanceof ContainerRegistrar) { throw new RuntimeException( "Module container registrar '{$registrarClass}' must implement " . ContainerRegistrar::class ); } $registrarInstance->register($container); } // ── Audit interface fallbacks (null implementations when audit module is disabled) ── if (!$container->has(AuditRecorderInterface::class)) { $container->set(AuditRecorderInterface::class, static fn (): AuditRecorderInterface => new NullAuditRecorder()); } if (!$container->has(UserLifecycleAuditInterface::class)) { $container->set(UserLifecycleAuditInterface::class, static fn (): UserLifecycleAuditInterface => new NullUserLifecycleAudit()); } if (!$container->has(ImportAuditInterface::class)) { $container->set(ImportAuditInterface::class, static fn (): ImportAuditInterface => new NullImportAudit()); } if (!$container->has(AuditMetadataEnricherInterface::class)) { $container->set(AuditMetadataEnricherInterface::class, static fn (): AuditMetadataEnricherInterface => new NullAuditMetadataEnricher()); } if (!$container->has(ApiAuditServiceInterface::class)) { $container->set(ApiAuditServiceInterface::class, static fn (): ApiAuditServiceInterface => new NullApiAuditService()); } if (!$container->has(ApiSystemAuditReporterInterface::class)) { $container->set(ApiSystemAuditReporterInterface::class, static fn (): ApiSystemAuditReporterInterface => new NullApiSystemAuditReporter()); } return $container;