'check_schema_drift', 'wpdo_error_budget' => 'check_error_budget', 'wpdo_hook_conflicts' => 'check_hook_conflicts', 'wpdo_autoload_bloat' => 'check_autoload_bloat', 'wpdo_postmeta_explosion' => 'check_postmeta_explosion', 'wpdo_orphan_zone_rows' => 'check_orphan_zone_rows', 'wpdo_missing_snapshot' => 'check_missing_snapshot', ); /** * Hook into Site Health. * * @return void */ public static function register(): void { add_filter( 'site_status_tests', array( __CLASS__, 'register_tests' ) ); } /** * Register the 7 tests with WP Site Health. * * @param array $tests Existing tests. * @return array */ public static function register_tests( array $tests ): array { foreach ( self::TESTS as $key => $cb_suffix ) { $tests['direct'][ $key ] = array( 'label' => self::label_for( $key ), 'test' => array( __CLASS__, $cb_suffix ), ); } return $tests; } /** * Human-readable label for each test. * * @param string $key Test slug. * @return string */ private static function label_for( string $key ): string { $map = array( 'wpdo_schema_drift' => __( 'WPDO schema drift', '2meet-data-optimizer' ), 'wpdo_error_budget' => __( 'WPDO error budget', '2meet-data-optimizer' ), 'wpdo_hook_conflicts' => __( 'WPDO hook conflicts', '2meet-data-optimizer' ), 'wpdo_autoload_bloat' => __( 'WPDO autoload bloat', '2meet-data-optimizer' ), 'wpdo_postmeta_explosion' => __( 'WPDO postmeta explosion', '2meet-data-optimizer' ), 'wpdo_orphan_zone_rows' => __( 'WPDO orphan zone rows', '2meet-data-optimizer' ), 'wpdo_missing_snapshot' => __( 'WPDO missing snapshot', '2meet-data-optimizer' ), ); return $map[ $key ] ?? $key; } // ─── Test 1: Schema drift ───────────────────────────────────────────── /** * Verify all expected v2 tables exist. * * @return array Site Health result. */ public static function check_schema_drift(): array { $result = self::cached( 'wpdo_sh_schema_drift', static function () { if ( ! class_exists( 'TMDO_Installer' ) ) { return self::pass( __( 'WPDO installer not loaded.', '2meet-data-optimizer' ) ); } if ( ! method_exists( 'TMDO_Installer', 'v2_tables_status' ) ) { return self::pass( __( 'Schema check unavailable on this version.', '2meet-data-optimizer' ) ); } $status = TMDO_Installer::v2_tables_status(); $missing = array_keys( array_filter( $status, static fn( $exists ) => ! $exists ) ); if ( empty( $missing ) ) { return self::pass( __( 'All WPDO v2 tables exist.', '2meet-data-optimizer' ) ); } return self::fail( __( 'WPDO v2 tables missing', '2meet-data-optimizer' ), sprintf( /* translators: %s: comma-separated list of missing table names */ __( 'Missing tables: %s. Run wp wpdo install or re-activate the plugin.', '2meet-data-optimizer' ), implode( ', ', $missing ) ), 'critical' ); } ); return self::wrap( 'wpdo_schema_drift', $result ); } // ─── Test 2: Error budget (last 7 days) ────────────────────────────── /** * Count errors in wp_wpdo_errors over the last 7 days. * * @return array Site Health result. */ public static function check_error_budget(): array { $result = self::cached( 'wpdo_sh_error_budget', static function () { global $wpdb; $table = $wpdb->prefix . 'wpdo_errors'; $exists = (int) $wpdb->get_var( $wpdb->prepare( // phpcs:ignore WordPress.DB 'SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s', $table ) ); if ( 0 === $exists ) { return self::pass( __( 'Error log not present (yet) — clean.', '2meet-data-optimizer' ) ); } $threshold = (int) apply_filters( 'wpdo/site_health/error_budget_threshold', 100 ); $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}` WHERE created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 7 DAY)" ); // phpcs:ignore WordPress.DB if ( $count <= $threshold ) { return self::pass( sprintf( /* translators: %d: error count */ __( '%d errors in the last 7 days (within budget).', '2meet-data-optimizer' ), $count ) ); } return self::fail( __( 'WPDO error budget exceeded', '2meet-data-optimizer' ), sprintf( /* translators: 1: error count, 2: threshold */ __( '%1$d errors in the last 7 days (threshold %2$d). Inspect under Tools → WP Data Optimizer → Logs.', '2meet-data-optimizer' ), $count, $threshold ), 'recommended' ); } ); return self::wrap( 'wpdo_error_budget', $result ); } // ─── Test 3: Hook conflicts ────────────────────────────────────────── /** * Check for interceptor or hook conflicts via TMDO_Conflict_Monitor. * * @return array Site Health result. */ public static function check_hook_conflicts(): array { $result = self::cached( 'wpdo_sh_hook_conflicts', static function () { if ( ! class_exists( 'TMDO_Conflict_Monitor' ) ) { return self::pass( __( 'Conflict monitor not loaded.', '2meet-data-optimizer' ) ); } $summary = TMDO_Conflict_Monitor::get_summary(); $total = (int) ( $summary['total'] ?? 0 ); if ( 0 === $total ) { return self::pass( __( 'No interceptor / hook conflicts detected.', '2meet-data-optimizer' ) ); } return self::fail( __( 'WPDO hook conflicts detected', '2meet-data-optimizer' ), sprintf( /* translators: %d: number of conflicts */ __( '%d interceptor/hook conflicts detected. Run wp wpdo conflict-scan for details.', '2meet-data-optimizer' ), $total ), 'recommended' ); } ); return self::wrap( 'wpdo_hook_conflicts', $result ); } // ─── Test 4: Autoload bloat ────────────────────────────────────────── /** * Check total autoloaded options size against a configurable threshold. * * @return array Site Health result. */ public static function check_autoload_bloat(): array { $result = self::cached( 'wpdo_sh_autoload_bloat', static function () { global $wpdb; $threshold_mb = (int) apply_filters( 'wpdo/site_health/autoload_threshold_mb', 5 ); $bytes = (int) $wpdb->get_var( "SELECT SUM(LENGTH(option_value)) FROM `{$wpdb->options}` WHERE autoload = 'yes'" ); // phpcs:ignore WordPress.DB $mb = $bytes / 1024 / 1024; if ( $mb < $threshold_mb ) { return self::pass( sprintf( /* translators: 1: actual size in MB */ __( 'Autoload total %1$0.2f MB (under %2$d MB threshold).', '2meet-data-optimizer' ), $mb, $threshold_mb ) ); } return self::fail( __( 'Autoload size large', '2meet-data-optimizer' ), sprintf( /* translators: 1: actual size in MB, 2: threshold in MB */ __( 'Autoload total %1$0.2f MB exceeds %2$d MB threshold. Consider migrating large autoloaded options to wp_wpdo_uni_options.', '2meet-data-optimizer' ), $mb, $threshold_mb ), 'recommended' ); } ); return self::wrap( 'wpdo_autoload_bloat', $result ); } // ─── Test 5: Postmeta explosion ────────────────────────────────────── /** * Check whether wp_postmeta row count exceeds the explosion threshold. * * @return array Site Health result. */ public static function check_postmeta_explosion(): array { $result = self::cached( 'wpdo_sh_postmeta_explosion', static function () { global $wpdb; $threshold = (int) apply_filters( 'wpdo/site_health/postmeta_threshold', 5_000_000 ); $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$wpdb->postmeta}`" ); // phpcs:ignore WordPress.DB if ( $count < $threshold ) { return self::pass( sprintf( /* translators: %s: row count */ __( 'wp_postmeta has %s rows (under threshold).', '2meet-data-optimizer' ), number_format_i18n( $count ) ) ); } // Also check if any zone module is active. $any_active = false; if ( class_exists( 'TMDO_Feature_Flags' ) ) { foreach ( TMDO_Feature_Flags::all() as $state ) { if ( 'idle' !== $state ) { $any_active = true; break; } } } $severity = $any_active ? 'recommended' : 'critical'; return self::fail( __( 'wp_postmeta is large', '2meet-data-optimizer' ), sprintf( /* translators: %s: row count */ __( 'wp_postmeta has %s rows. Run the Classifier to identify candidates for migration into Hot/Warm/Cold/Archive zones.', '2meet-data-optimizer' ), number_format_i18n( $count ) ), $severity ); } ); return self::wrap( 'wpdo_postmeta_explosion', $result ); } // ─── Test 6: Orphan zone rows ──────────────────────────────────────── /** * Detect zone table rows that belong to modules currently in idle state. * * @return array Site Health result. */ public static function check_orphan_zone_rows(): array { $result = self::cached( 'wpdo_sh_orphan_zone', static function () { global $wpdb; if ( ! class_exists( 'TMDO_Feature_Flags' ) ) { return self::pass( __( 'Feature flags not loaded.', '2meet-data-optimizer' ) ); } $idle_modules = array_keys( array_filter( TMDO_Feature_Flags::all(), static fn( $state ) => 'idle' === $state ) ); $orphans = array(); foreach ( $idle_modules as $module ) { // Best-effort: zone tables for hot_*/cold_* are dynamically named. $candidates = array( $wpdb->prefix . 'wpdo_warm', $wpdb->prefix . 'wpdo_archive', ); foreach ( $candidates as $table ) { $exists = (int) $wpdb->get_var( $wpdb->prepare( // phpcs:ignore WordPress.DB 'SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s', $table ) ); if ( 0 === $exists ) { continue; } $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}`" ); // phpcs:ignore WordPress.DB if ( $count > 0 ) { $orphans[ $table ] = $count; } } } if ( empty( $orphans ) ) { return self::pass( __( 'No orphan zone rows from idle modules.', '2meet-data-optimizer' ) ); } $lines = array(); foreach ( $orphans as $t => $n ) { $lines[] = sprintf( '%s (%s rows)', $t, number_format_i18n( $n ) ); } return self::fail( __( 'Orphan zone rows detected', '2meet-data-optimizer' ), sprintf( /* translators: %s: list of tables and row counts */ __( 'Modules in idle state but zone tables still hold data: %s. These rows are typically cleanup leftovers — verify before truncating.', '2meet-data-optimizer' ), implode( ', ', $lines ) ), 'recommended' ); } ); return self::wrap( 'wpdo_orphan_zone_rows', $result ); } // ─── Test 7: Missing snapshot ──────────────────────────────────────── /** * Verify that a recent snapshot exists when modules are in risk states. * * @return array Site Health result. */ public static function check_missing_snapshot(): array { $result = self::cached( 'wpdo_sh_missing_snapshot', static function () { global $wpdb; if ( ! class_exists( 'TMDO_Snapshot_Manager' ) || ! class_exists( 'TMDO_Feature_Flags' ) ) { return self::pass( __( 'Snapshot system not yet available.', '2meet-data-optimizer' ) ); } // Risk only applies to modules in cutover/cleanup/complete (data is in custom tables). $risk_modules = array_keys( array_filter( TMDO_Feature_Flags::all(), static fn( $state ) => in_array( $state, array( 'cutover', 'cleanup', 'complete' ), true ) ) ); if ( empty( $risk_modules ) ) { return self::pass( __( 'No modules in risk state — snapshot not required.', '2meet-data-optimizer' ) ); } $snap_table = $wpdb->prefix . TMDO_Snapshot_Manager::TABLE_SLUG; $exists = (int) $wpdb->get_var( $wpdb->prepare( // phpcs:ignore WordPress.DB 'SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s', $snap_table ) ); if ( 0 === $exists ) { return self::fail( __( 'Snapshot table missing', '2meet-data-optimizer' ), __( 'Snapshot system table not present. Run wp wpdo install.', '2meet-data-optimizer' ), 'critical' ); } $days = (int) apply_filters( 'wpdo/site_health/snapshot_max_age_days', 7 ); $recent = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$snap_table}` WHERE created_at >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d DAY)", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- {$snap_table} is $wpdb->prefix + TABLE_SLUG constant (no user input) $days ) ); if ( $recent > 0 ) { return self::pass( sprintf( /* translators: 1: count, 2: days */ __( 'Found %1$d snapshot(s) within the last %2$d days.', '2meet-data-optimizer' ), $recent, $days ) ); } return self::fail( __( 'No recent WPDO snapshot', '2meet-data-optimizer' ), sprintf( /* translators: 1: comma-separated module names, 2: days */ __( 'Modules in risk state (%1$s) but no snapshot in last %2$d days. Run: wp wpdo snapshot create --trigger=manual --notes="catch-up safety net"', '2meet-data-optimizer' ), implode( ', ', $risk_modules ), $days ), 'critical' ); } ); return self::wrap( 'wpdo_missing_snapshot', $result ); } // ─── helpers ────────────────────────────────────────────────────────── /** * Wrap a raw check result with required Site Health envelope fields. * * @param string $key Test slug. * @param array $result Raw result with keys label, status, description, severity. * @return array */ private static function wrap( string $key, array $result ): array { $result['test'] = $key; $result['badge'] = array( 'label' => 'WPDO', 'color' => 'blue', ); return $result; } /** * Cache a callable's return value via transient. * * @param string $key Transient key. * @param callable $producer Callable returning result array. * @return array */ private static function cached( string $key, callable $producer ): array { $cached = get_transient( $key ); if ( is_array( $cached ) ) { return $cached; } $result = $producer(); set_transient( $key, $result, self::CACHE_TTL ); return $result; } /** * Build a "pass" result envelope. * * @param string $description Body text. * @return array */ private static function pass( string $description ): array { return array( 'label' => __( 'WPDO check passed', '2meet-data-optimizer' ), 'status' => 'good', 'description' => '
' . esc_html( $description ) . '
', 'severity' => 'good', 'actions' => '', ); } /** * Build a fail / warning result envelope. * * @param string $label Test heading. * @param string $description Body. * @param string $severity 'critical' | 'recommended'. * @return array */ private static function fail( string $label, string $description, string $severity ): array { return array( 'label' => $label, 'status' => 'critical' === $severity ? 'critical' : 'recommended', 'description' => '' . esc_html( $description ) . '
', 'severity' => $severity, 'actions' => '' . esc_html__( 'Open WPDO admin', '2meet-data-optimizer' ) . '
', ); } }