b4400a68e5
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
243 lines
7.6 KiB
PHP
243 lines
7.6 KiB
PHP
<?php
|
|
/**
|
|
* HivePress family integration bootstrap.
|
|
*
|
|
* Single entry point for the WPDO ↔ HivePress integration:
|
|
*
|
|
* 1. Run conflict guard (hp-custom-tables / hp-info-cards detection)
|
|
* 2. Detect installed HivePress family addons
|
|
* 3. For each detected addon, instantiate the matching adapter and bind
|
|
* its lifecycle hooks (register_fields / register_custom_tables /
|
|
* register_entity_fields / query / event)
|
|
* 4. Honour the `wpdo_hivepress_should_bind` filter (conflict-aware)
|
|
*
|
|
* Booted by `TMDO_Core::run()` once during request boot. Idempotent —
|
|
* subsequent calls return early. Order:
|
|
*
|
|
* plugins_loaded:4 → TMDO_Core::run() → calls Bootstrap::boot()
|
|
* plugins_loaded:5+ → adapters' on_register_* hooks fire when WPDO actions dispatch
|
|
* init:1 → re-fired by WPDO core safety net
|
|
*
|
|
* @package WP_Data_Optimizer
|
|
* @since 3.0.0
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
if ( ! class_exists( 'TMDO_HivePress_Bootstrap' ) ) {
|
|
|
|
/**
|
|
* Bootstrap orchestrator for HivePress family adapters.
|
|
*
|
|
* Stateful only across the current request (instantiated adapters cached
|
|
* for `wp wpdo hivepress score/doctor` access). Reset between tests via
|
|
* `reset_for_tests()`.
|
|
*/
|
|
final class TMDO_HivePress_Bootstrap {
|
|
|
|
/** Slug → adapter class name registry. */
|
|
private const ADAPTER_MAP = array(
|
|
'hivepress' => '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<string, TMDO_HivePress_Adapter>
|
|
*/
|
|
private static array $adapters = array();
|
|
|
|
/**
|
|
* Detection snapshot at boot time (captured for diagnostics).
|
|
*
|
|
* @var array<string,string>
|
|
*/
|
|
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<string,string> $detected Slug → version map.
|
|
* @param array<string,TMDO_HivePress_Adapter> $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<string, TMDO_HivePress_Adapter>
|
|
*/
|
|
public static function adapters(): array {
|
|
return self::$adapters;
|
|
}
|
|
|
|
/**
|
|
* Read-only accessor for detection snapshot at boot time.
|
|
*
|
|
* @return array<string,string>
|
|
*/
|
|
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 )
|