getComponentRepository(); * $service = $container->getValidationService(); * ``` * * @package ROITheme\Shared\Infrastructure\DI */ final class DIContainer { private array $instances = []; /** * Instancia singleton del contenedor * @var self|null */ private static ?self $instance = null; /** * Obtiene la instancia singleton del contenedor * * NOTA: Se debe haber creado una instancia previamente en functions.php * El constructor registra automáticamente la instancia. * * @return self * @throws \RuntimeException Si no se ha inicializado el contenedor */ public static function getInstance(): self { if (self::$instance === null) { throw new \RuntimeException( 'DIContainer no ha sido inicializado. Asegúrate de que functions.php se haya ejecutado primero.' ); } return self::$instance; } public function __construct( private \wpdb $wpdb, private string $schemasPath ) { // Registrar como instancia singleton self::$instance = $this; } /** * Obtener repositorio de componentes * * Lazy initialization: Crea la instancia solo en la primera llamada * * @return ComponentRepositoryInterface */ public function getComponentRepository(): ComponentRepositoryInterface { if (!isset($this->instances['componentRepository'])) { $this->instances['componentRepository'] = new WordPressComponentRepository( $this->wpdb ); } return $this->instances['componentRepository']; } /** * Obtener repositorio de defaults * * Lazy initialization: Crea la instancia solo en la primera llamada * * @return ComponentDefaultsRepositoryInterface */ public function getDefaultsRepository(): ComponentDefaultsRepositoryInterface { if (!isset($this->instances['defaultsRepository'])) { $this->instances['defaultsRepository'] = new WordPressDefaultsRepository( $this->wpdb ); } return $this->instances['defaultsRepository']; } /** * Obtener servicio de validación * * Lazy initialization: Crea la instancia solo en la primera llamada * Resuelve dependencia: getDefaultsRepository() * * @return ValidationServiceInterface */ public function getValidationService(): ValidationServiceInterface { if (!isset($this->instances['validationService'])) { $this->instances['validationService'] = new WordPressValidationService( $this->getDefaultsRepository() ); } return $this->instances['validationService']; } /** * Obtener servicio de cache * * Lazy initialization: Crea la instancia solo en la primera llamada * * @return CacheServiceInterface */ public function getCacheService(): CacheServiceInterface { if (!isset($this->instances['cacheService'])) { $this->instances['cacheService'] = new WordPressCacheService( $this->wpdb ); } return $this->instances['cacheService']; } /** * Obtener servicio de sincronización de schemas * * Lazy initialization: Crea la instancia solo en la primera llamada * Resuelve dependencia: getDefaultsRepository() * * @return SchemaSyncService */ public function getSchemaSyncService(): SchemaSyncService { if (!isset($this->instances['schemaSyncService'])) { $this->instances['schemaSyncService'] = new SchemaSyncService( $this->getDefaultsRepository(), $this->schemasPath ); } return $this->instances['schemaSyncService']; } /** * Obtener servicio de limpieza * * Lazy initialization: Crea la instancia solo en la primera llamada * Resuelve dependencias: getComponentRepository(), getDefaultsRepository() * * @return CleanupService */ public function getCleanupService(): CleanupService { if (!isset($this->instances['cleanupService'])) { $this->instances['cleanupService'] = new CleanupService( $this->getComponentRepository(), $this->getDefaultsRepository() ); } return $this->instances['cleanupService']; } /** * Obtener servicio de generación de CSS * * Lazy initialization: Crea la instancia solo en la primera llamada * Sin dependencias * * @return CSSGeneratorInterface */ public function getCSSGeneratorService(): CSSGeneratorInterface { if (!isset($this->instances['cssGeneratorService'])) { $this->instances['cssGeneratorService'] = new CSSGeneratorService(); } return $this->instances['cssGeneratorService']; } /** * Obtener repositorio de configuraciones de componentes * * Lazy initialization: Crea la instancia solo en la primera llamada * * @return ComponentSettingsRepositoryInterface */ public function getComponentSettingsRepository(): ComponentSettingsRepositoryInterface { if (!isset($this->instances['componentSettingsRepository'])) { $this->instances['componentSettingsRepository'] = new WordPressComponentSettingsRepository( $this->wpdb ); } return $this->instances['componentSettingsRepository']; } /** * Obtener caso de uso para obtener configuraciones de componentes * * Lazy initialization: Crea la instancia solo en la primera llamada * Resuelve dependencia: getComponentSettingsRepository() * * @return GetComponentSettingsUseCase */ public function getGetComponentSettingsUseCase(): GetComponentSettingsUseCase { if (!isset($this->instances['getComponentSettingsUseCase'])) { $this->instances['getComponentSettingsUseCase'] = new GetComponentSettingsUseCase( $this->getComponentSettingsRepository() ); } return $this->instances['getComponentSettingsUseCase']; } /** * Obtener caso de uso para guardar configuraciones de componentes * * Lazy initialization: Crea la instancia solo en la primera llamada * Resuelve dependencia: getComponentSettingsRepository() * * @return SaveComponentSettingsUseCase */ public function getSaveComponentSettingsUseCase(): SaveComponentSettingsUseCase { if (!isset($this->instances['saveComponentSettingsUseCase'])) { $this->instances['saveComponentSettingsUseCase'] = new SaveComponentSettingsUseCase( $this->getComponentSettingsRepository() ); } return $this->instances['saveComponentSettingsUseCase']; } /** * Obtener renderer de AdSense Placement * * Lazy initialization: Crea la instancia solo en la primera llamada * Resuelve dependencia: getCSSGeneratorService() * * @return AdsensePlacementRenderer */ public function getAdsensePlacementRenderer(): AdsensePlacementRenderer { if (!isset($this->instances['adsensePlacementRenderer'])) { $this->instances['adsensePlacementRenderer'] = new AdsensePlacementRenderer( $this->getCSSGeneratorService() ); } return $this->instances['adsensePlacementRenderer']; } /** * Obtener colector de CSS crítico * * Lazy initialization: Crea la instancia solo en la primera llamada * IMPORTANTE: Singleton - misma instancia para todos los Renderers * * @return CriticalCSSCollectorInterface */ public function getCriticalCSSCollector(): CriticalCSSCollectorInterface { if (!isset($this->instances['criticalCSSCollector'])) { $this->instances['criticalCSSCollector'] = new CriticalCSSCollector(); } return $this->instances['criticalCSSCollector']; } // =============================== // Page Visibility System // =============================== /** * Obtiene el repositorio de visibilidad de página * * IMPORTANTE: Inyecta $wpdb para consistencia con el resto del código * (WordPressComponentSettingsRepository también recibe $wpdb por constructor) */ public function getPageVisibilityRepository(): PageVisibilityRepositoryInterface { if (!isset($this->instances['pageVisibilityRepository'])) { // Inyectar $wpdb siguiendo el patrón existente $this->instances['pageVisibilityRepository'] = new WordPressPageVisibilityRepository($this->wpdb); } return $this->instances['pageVisibilityRepository']; } /** * Obtiene el detector de tipo de página */ public function getPageTypeDetector(): PageTypeDetectorInterface { if (!isset($this->instances['pageTypeDetector'])) { $this->instances['pageTypeDetector'] = new WordPressPageTypeDetector(); } return $this->instances['pageTypeDetector']; } /** * Obtiene el caso de uso de evaluación de visibilidad */ public function getEvaluatePageVisibilityUseCase(): EvaluatePageVisibilityUseCase { if (!isset($this->instances['evaluatePageVisibilityUseCase'])) { $this->instances['evaluatePageVisibilityUseCase'] = new EvaluatePageVisibilityUseCase( $this->getPageTypeDetector(), $this->getPageVisibilityRepository() ); } return $this->instances['evaluatePageVisibilityUseCase']; } /** * Obtiene el servicio de migración de visibilidad */ public function getMigratePageVisibilityService(): MigratePageVisibilityService { if (!isset($this->instances['migratePageVisibilityService'])) { $this->instances['migratePageVisibilityService'] = new MigratePageVisibilityService( $this->getPageVisibilityRepository() ); } return $this->instances['migratePageVisibilityService']; } // =============================== // Exclusion System (Plan 99.11) // =============================== /** * Obtiene el proveedor de request HTTP * * Encapsula acceso a $_SERVER */ public function getServerRequestProvider(): ServerRequestProviderInterface { if (!isset($this->instances['serverRequestProvider'])) { $this->instances['serverRequestProvider'] = new WordPressServerRequestProvider(); } return $this->instances['serverRequestProvider']; } /** * Obtiene el repositorio de exclusiones */ public function getExclusionRepository(): ExclusionRepositoryInterface { if (!isset($this->instances['exclusionRepository'])) { $this->instances['exclusionRepository'] = new WordPressExclusionRepository($this->wpdb); } return $this->instances['exclusionRepository']; } /** * Obtiene el proveedor de contexto de página */ public function getPageContextProvider(): PageContextProviderInterface { if (!isset($this->instances['pageContextProvider'])) { $this->instances['pageContextProvider'] = new WordPressPageContextProvider( $this->getServerRequestProvider() ); } return $this->instances['pageContextProvider']; } /** * Obtiene el caso de uso de evaluación de exclusiones */ public function getEvaluateExclusionsUseCase(): EvaluateExclusionsUseCase { if (!isset($this->instances['evaluateExclusionsUseCase'])) { $this->instances['evaluateExclusionsUseCase'] = new EvaluateExclusionsUseCase( $this->getExclusionRepository(), $this->getPageContextProvider() ); } return $this->instances['evaluateExclusionsUseCase']; } /** * Obtiene el caso de uso orquestador de visibilidad completa * * Combina visibilidad por tipo de página + exclusiones */ public function getEvaluateComponentVisibilityUseCase(): EvaluateComponentVisibilityUseCase { if (!isset($this->instances['evaluateComponentVisibilityUseCase'])) { $this->instances['evaluateComponentVisibilityUseCase'] = new EvaluateComponentVisibilityUseCase( $this->getEvaluatePageVisibilityUseCase(), $this->getEvaluateExclusionsUseCase() ); } return $this->instances['evaluateComponentVisibilityUseCase']; } // =============================== // Wrapper Visibility System (Plan 99.15) // =============================== /** * Obtiene el repositorio de visibilidad de wrappers * * Implementa WrapperVisibilityCheckerInterface */ public function getWrapperVisibilityChecker(): WrapperVisibilityCheckerInterface { if (!isset($this->instances['wrapperVisibilityChecker'])) { $this->instances['wrapperVisibilityChecker'] = new WordPressComponentVisibilityRepository($this->wpdb); } return $this->instances['wrapperVisibilityChecker']; } /** * Obtiene el caso de uso para verificar visibilidad de wrappers * * Usado por WrapperVisibilityService para templates */ public function getCheckWrapperVisibilityUseCase(): CheckWrapperVisibilityUseCase { if (!isset($this->instances['checkWrapperVisibilityUseCase'])) { $this->instances['checkWrapperVisibilityUseCase'] = new CheckWrapperVisibilityUseCase( $this->getWrapperVisibilityChecker() ); } return $this->instances['checkWrapperVisibilityUseCase']; } /** * Obtiene el registrador de hooks para body_class * * CSS failsafe: Agrega clases cuando componentes están ocultos */ public function getBodyClassHooksRegistrar(): BodyClassHooksRegistrar { if (!isset($this->instances['bodyClassHooksRegistrar'])) { $this->instances['bodyClassHooksRegistrar'] = new BodyClassHooksRegistrar(); } return $this->instances['bodyClassHooksRegistrar']; } // =============================== // Post Grid Shortcode System // =============================== /** * Obtiene el query builder para post grid shortcode */ public function getPostGridQueryBuilder(): PostGridQueryBuilderInterface { if (!isset($this->instances['postGridQueryBuilder'])) { $this->instances['postGridQueryBuilder'] = new PostGridQueryBuilder(); } return $this->instances['postGridQueryBuilder']; } /** * Obtiene el renderer para post grid shortcode */ public function getPostGridShortcodeRenderer(): PostGridShortcodeRendererInterface { if (!isset($this->instances['postGridShortcodeRenderer'])) { $this->instances['postGridShortcodeRenderer'] = new PostGridShortcodeRenderer( $this->getCSSGeneratorService() ); } return $this->instances['postGridShortcodeRenderer']; } /** * Obtiene el caso de uso para renderizar post grid shortcode */ public function getRenderPostGridUseCase(): RenderPostGridUseCase { if (!isset($this->instances['renderPostGridUseCase'])) { $this->instances['renderPostGridUseCase'] = new RenderPostGridUseCase( $this->getPostGridQueryBuilder(), $this->getPostGridShortcodeRenderer(), $this->getComponentSettingsRepository() ); } return $this->instances['renderPostGridUseCase']; } }