gmdate( 'Y-m-d H:i:s', strtotime( '-30 days' ) ), 'period_end' => gmdate( 'Y-m-d H:i:s' ), 'health' => self::aggregate_health(), 'fsm_trajectory' => self::aggregate_fsm(), 'snapshots' => self::aggregate_snapshots(), 'zone_growth' => self::aggregate_zone_growth(), 'autoload_size' => self::measure_autoload(), 'duration_ms' => 0, 'generated_at' => gmdate( 'Y-m-d H:i:s' ), ); $summary['duration_ms'] = (int) round( ( microtime( true ) - $started_at ) * 1000 ); update_option( self::OPTION_LATEST, $summary, false ); // v2.5.0 M14: archive into history (max 12 entries). $history = (array) get_option( 'wpdo_monthly_summary_history', array() ); array_unshift( $history, $summary ); $history = array_slice( $history, 0, 12 ); update_option( 'wpdo_monthly_summary_history', $history, false ); if ( class_exists( 'TMDO_Logger' ) ) { TMDO_Logger::info( 'monthly_summary', array( 'health_success' => (int) ( $summary['health']['success'] ?? 0 ), 'health_critical' => (int) ( $summary['health']['critical'] ?? 0 ), 'fsm_promotions' => count( $summary['fsm_trajectory']['promotions'] ?? array() ), 'fsm_rollbacks' => count( $summary['fsm_trajectory']['rollbacks'] ?? array() ), 'snapshots_total' => (int) ( $summary['snapshots']['total'] ?? 0 ), 'autoload_kb' => (int) round( ( $summary['autoload_size'] ?? 0 ) / 1024 ), ) ); } do_action( 'wpdo/monthly_summary_generated', $summary ); return $summary; } /** * Read the latest summary (from wp_options). * * @return array|null */ public static function get_latest(): ?array { $v = get_option( self::OPTION_LATEST ); return is_array( $v ) ? $v : null; } // ─── private ──────────────────────────────────────────────────── /** * Aggregate health-check audit log entries from last 30 days. * * @return array {success:int, recommended:int, critical:int, total:int} */ private static function aggregate_health(): array { global $wpdb; $out = array( 'success' => 0, 'recommended' => 0, 'critical' => 0, 'total' => 0, ); // wp_wpdo_audit may not exist on early v1.x installs; check first. $audit = $wpdb->prefix . 'wpdo_audit'; $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', $audit ) ); if ( 0 === $exists ) { return $out; } // Health check audit rows have op='health_check_daily'. $rows = (array) $wpdb->get_results( "SELECT * FROM `{$audit}` WHERE op = 'health_check_daily' AND ts >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY)", ARRAY_A ); // phpcs:ignore WordPress.DB foreach ( $rows as $r ) { $ctx = isset( $r['value_after'] ) ? json_decode( (string) $r['value_after'], true ) : null; if ( ! is_array( $ctx ) ) { continue; } $crit = (int) ( $ctx['critical'] ?? 0 ); $rec = (int) ( $ctx['recommended'] ?? 0 ); ++$out['total']; if ( $crit > 0 ) { ++$out['critical']; } elseif ( $rec > 0 ) { ++$out['recommended']; } else { ++$out['success']; } } return $out; } /** * Best-effort FSM trajectory: which modules changed state in the last 30 * days. Reads `wpdo_fsm_state_entered` + current state. * * @return array {promotions:array, rollbacks:array, stationary:array} */ private static function aggregate_fsm(): array { $out = array( 'promotions' => array(), 'rollbacks' => array(), 'stationary' => array(), ); if ( ! class_exists( 'TMDO_Feature_Flags' ) ) { return $out; } $entered = (array) get_option( 'wpdo_fsm_state_entered', array() ); $now = time(); foreach ( TMDO_Feature_Flags::all() as $module => $state ) { $ts = isset( $entered[ $module ]['entered_at'] ) ? strtotime( (string) $entered[ $module ]['entered_at'] . ' UTC' ) : 0; $age_days = $ts > 0 ? (int) floor( ( $now - $ts ) / DAY_IN_SECONDS ) : null; if ( $ts > 0 && ( $now - $ts ) <= ( 30 * DAY_IN_SECONDS ) ) { // Recently changed β€” bucket by direction. if ( 'idle' === $state ) { $out['rollbacks'][ $module ] = array( 'state' => $state, 'days_in_state' => $age_days, ); } else { $out['promotions'][ $module ] = array( 'state' => $state, 'days_in_state' => $age_days, ); } } else { $out['stationary'][ $module ] = array( 'state' => $state, 'days_in_state' => $age_days, ); } } return $out; } /** * Aggregate snapshot retention stats from the snapshots table. * * @return array {total:int, oldest:string|null, newest:string|null, total_bytes:int} */ private static function aggregate_snapshots(): array { global $wpdb; $table = $wpdb->prefix . 'wpdo_snapshots'; $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 array( 'total' => 0, 'oldest' => null, 'newest' => null, 'total_bytes' => 0, ); } $row = $wpdb->get_row( "SELECT COUNT(*) AS c, MIN(created_at) AS oldest, MAX(created_at) AS newest, COALESCE(SUM(size_bytes),0) AS bytes FROM `{$table}`", ARRAY_A ); // phpcs:ignore WordPress.DB return array( 'total' => (int) ( $row['c'] ?? 0 ), 'oldest' => isset( $row['oldest'] ) ? (string) $row['oldest'] : null, 'newest' => isset( $row['newest'] ) ? (string) $row['newest'] : null, 'total_bytes' => (int) ( $row['bytes'] ?? 0 ), ); } /** * Per-zone row count snapshot. Compares to previous month's value when * available (delta_rows positive = growth). * * @return array */ private static function aggregate_zone_growth(): array { // Use historical site_metrics when available β€” avoids live COUNT(*) on large tables // and provides a 30-day delta (current βˆ’ oldest snapshot). if ( class_exists( 'TMDO_Site_Metrics_Collector' ) ) { $latest = TMDO_Site_Metrics_Collector::get_latest_snapshot(); if ( ! empty( $latest ) ) { // Get oldest snapshot within 30 days for delta calculation. $keys_of_interest = array( 'eav.postmeta_rows', 'flat.hot_rows', 'flat.cold_rows', 'flat.warm_rows', 'custom_tables.total_rows' ); $out = array(); foreach ( $keys_of_interest as $mk ) { if ( ! isset( $latest[ $mk ] ) ) { continue; } $history = TMDO_Site_Metrics_Collector::get_history( $mk, 30 ); $oldest = ! empty( $history ) ? (int) $history[0]['value'] : null; $current = (int) $latest[ $mk ]; $out[ $mk ] = array( 'rows' => $current, 'delta_rows' => null !== $oldest ? $current - $oldest : null, ); } return $out; } } // Fallback: live COUNT(*) for warm + archive tables (pre-v2.6.2 installs). global $wpdb; $out = array(); $keys = array( 'wpdo_warm', 'wpdo_archive' ); foreach ( $keys as $slug ) { $table = $wpdb->prefix . $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', $table ) ); if ( 0 === $exists ) { continue; } $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}`" ); // phpcs:ignore WordPress.DB $out[ $slug ] = array( 'rows' => $count, 'delta_rows' => null, ); } return $out; } /** * Return the total byte size of autoloaded options. * * @return int Bytes in autoloaded options. */ private static function measure_autoload(): int { global $wpdb; return (int) $wpdb->get_var( "SELECT COALESCE(SUM(LENGTH(option_value)),0) FROM `{$wpdb->options}` WHERE autoload = 'yes'" ); // phpcs:ignore WordPress.DB } }