'TMDO_HivePress_Core_Adapter', 'hivepress-reviews' => 'TMDO_HivePress_Reviews_Adapter', 'hivepress-favorites' => 'TMDO_HivePress_Favorites_Adapter', 'hivepress-messages' => 'TMDO_HivePress_Messages_Adapter', 'hivepress-memberships' => 'TMDO_HivePress_Memberships_Adapter', 'hivepress-requests' => 'TMDO_HivePress_Requests_Adapter', 'hivepress-tags' => 'TMDO_HivePress_Tags_Adapter', 'hivepress-seo' => 'TMDO_HivePress_Seo_Adapter', 'hivepress-social-links' => 'TMDO_HivePress_Social_Links_Adapter', 'hivepress-blocks' => 'TMDO_HivePress_Blocks_Adapter', 'hivepress-bookings' => 'TMDO_HivePress_Bookings_Adapter', 'hivepress-marketplace' => 'TMDO_HivePress_Marketplace_Adapter', 'hivepress-statistics' => 'TMDO_HivePress_Statistics_Adapter', ); /** * Whether boot() has already run this request. * * @var bool */ private static bool $booted = false; /** * Instantiated adapters keyed by addon slug. * * @var array */ private static array $adapters = array(); /** * Detection snapshot at boot time (captured for diagnostics). * * @var array */ private static array $detected = array(); /** * Boot the integration. * * @return void */ public static function boot(): void { if ( self::$booted ) { return; } self::$booted = true; // Step 1: conflict guard. Detection + admin_notice + filter install. if ( class_exists( 'TMDO_HivePress_Conflict_Guard' ) ) { TMDO_HivePress_Conflict_Guard::check_and_warn(); } // Step 2: detect addons. Empty array on non-HP sites = zero overhead. self::$detected = class_exists( 'TMDO_HivePress_Detector' ) ? TMDO_HivePress_Detector::detect() : array(); if ( empty( self::$detected ) ) { return; } // Step 3: respect the should_bind filter (conflict guard short-circuits here). $context = array( 'detected' => self::$detected, 'adapters' => self::ADAPTER_MAP, 'conflicts' => class_exists( 'TMDO_HivePress_Conflict_Guard' ) ? TMDO_HivePress_Conflict_Guard::detect_conflicts() : array(), ); $should = apply_filters( 'wpdo_hivepress_should_bind', true, $context ); if ( ! $should ) { return; } // Step 4: instantiate + bind each adapter. foreach ( self::$detected as $slug => $version ) { self::bind_adapter( $slug, $version ); } // Step 5: register cross-cutting components (router + cron optimizer). // Both are gated by their own opt-in option and only activate when // the operator flips it; safe to call here unconditionally. if ( class_exists( 'TMDO_HivePress_Comment_Router' ) ) { TMDO_HivePress_Comment_Router::register(); } if ( class_exists( 'TMDO_HivePress_Cron_Optimizer' ) ) { TMDO_HivePress_Cron_Optimizer::register(); } /** * Action: wpdo_hivepress_booted * * Fires after all adapters are bound. Listeners can introspect * `TMDO_HivePress_Bootstrap::adapters()` for the active set. * * @param array $detected Slug → version map. * @param array $adapters Slug → adapter instance. */ do_action( 'wpdo_hivepress_booted', self::$detected, self::$adapters ); } /** * Force re-boot on next call (for tests + plugin activation). * * @internal */ public static function reset_for_tests(): void { self::$booted = false; self::$adapters = array(); self::$detected = array(); } /** * Read-only accessor for active adapters. * * @return array */ public static function adapters(): array { return self::$adapters; } /** * Read-only accessor for detection snapshot at boot time. * * @return array */ public static function detected(): array { return self::$detected; } /** * Look up the adapter for a given addon slug, or null when none active. * * @param string $slug Addon slug. */ public static function adapter_for( string $slug ): ?object { return self::$adapters[ $slug ] ?? null; } // ── Internal: per-adapter binding ─────────────────────────────────── /** * Instantiate and wire one adapter's lifecycle hooks. * * @param string $slug Detected addon slug. * @param string $version Detected version string ('' if unknown). */ private static function bind_adapter( string $slug, string $version ): void { $class = self::ADAPTER_MAP[ $slug ] ?? ''; if ( '' === $class || ! class_exists( $class ) ) { // Adapter for this slug not yet implemented (future Sprint). return; } try { $adapter = new $class(); } catch ( \Throwable $e ) { if ( class_exists( 'TMDO_Logger' ) && method_exists( 'TMDO_Logger', 'error' ) ) { TMDO_Logger::error( 'hivepress-integration', sprintf( 'Failed to instantiate %s: %s', $class, $e->getMessage() ) ); } return; } if ( ! $adapter instanceof TMDO_HivePress_Adapter ) { if ( class_exists( 'TMDO_Logger' ) && method_exists( 'TMDO_Logger', 'warn' ) ) { TMDO_Logger::warn( 'hivepress-integration', sprintf( '%s does not implement TMDO_HivePress_Adapter — skipping', $class ) ); } return; } // Version floor check. $min = $adapter->minimum_addon_version(); if ( '' !== $min && '' !== $version && version_compare( $version, $min, '<' ) ) { if ( class_exists( 'TMDO_Logger' ) && method_exists( 'TMDO_Logger', 'warn' ) ) { TMDO_Logger::warn( 'hivepress-integration', sprintf( '%s installed version %s is below minimum %s — adapter idle', $slug, $version, $min ) ); } return; } self::$adapters[ $slug ] = $adapter; // Adapter constructor is contractually responsible for calling // `$this->bind_anti_eav_hooks()` (inherited from // TMDO_Anti_EAV_Aware via TMDO_HivePress_Adapter_Trait) so the // three `wpdo_register_*` actions get wired. Bootstrap then only // triggers the addon-specific extras: query/event hooks. $adapter->on_register_query_hooks(); $adapter->on_register_event_hooks(); } } } // end if ( ! class_exists )