Site Health) or when * core runs its background/async Site Health checks — not on ordinary front-end * or admin page loads. * * @package Activity_Log_Pro * @subpackage Activity_Log_Pro/includes * @since 1.0.8 */ class ACTLOGPRO_Site_Health_Monitor { /** Option key used to persist the last-known status for each test. */ private const STATE_OPTION = 'actlogpro_site_health_test_results'; /** Option key that controls whether this monitor is active. */ private const TRACKING_OPTION = 'actlogpro_track_site_health'; /** * Register the Site Health result filter (fires only when WP runs those tests; see class docblock). */ public function __construct() { add_filter( 'site_status_test_result', array( $this, 'on_site_status_test_result' ) ); } /** * Intercept each Site Health test result and log any status change. * * @param mixed $result Test result from WP_Site_Health. * @return mixed Unmodified result. */ public function on_site_status_test_result( $result ) { if ( get_option( self::TRACKING_OPTION, '1' ) !== '1' ) { return $result; } if ( ! is_array( $result ) || empty( $result['test'] ) || empty( $result['status'] ) ) { return $result; } $test_id = sanitize_key( $result['test'] ); $new_status = sanitize_text_field( $result['status'] ); $stored = get_option( self::STATE_OPTION, array() ); if ( ! is_array( $stored ) ) { $stored = array(); } $previous_status = $stored[ $test_id ] ?? null; // No change — nothing to log. if ( $previous_status === $new_status ) { return $result; } // Persist the updated status; autoload disabled as this is only needed during health checks. $stored[ $test_id ] = $new_status; update_option( self::STATE_OPTION, $stored, false ); // First time seeing this test — store as baseline without logging. if ( null === $previous_status ) { return $result; } $this->log_status_change( $result, $new_status, $previous_status ); return $result; } /** * Build and write a log entry for a test status transition. * * @param array $result Full test result array. * @param string $new_status Current status (good / recommended / critical). * @param string $previous_status Previous status. */ private function log_status_change( array $result, string $new_status, string $previous_status ): void { $test_label = ! empty( $result['label'] ) ? sanitize_text_field( $result['label'] ) : sanitize_key( $result['test'] ); $badge_label = isset( $result['badge']['label'] ) ? strtolower( sanitize_text_field( $result['badge']['label'] ) ) : ''; if ( 'good' === $new_status ) { $action = 'issue_resolved'; $description = sprintf( /* translators: %s: Site Health test label */ __( 'Site Health Issue Resolved: %s', 'activity-log-pro' ), $test_label ); $severity = ACTLOGPRO_Severity::LOW; } elseif ( 'critical' === $new_status ) { $action = 'issue_detected'; $description = sprintf( /* translators: %s: Site Health test label */ __( 'Site Health Issue Detected: %s', 'activity-log-pro' ), $test_label ); $severity = ACTLOGPRO_Severity::CRITICAL; } elseif ( 'good' === $previous_status ) { $action = 'issue_detected'; $description = sprintf( /* translators: %s: Site Health test label */ __( 'Site Health Issue Detected: %s', 'activity-log-pro' ), $test_label ); $severity = ACTLOGPRO_Severity::MEDIUM; } else { // Both statuses are non-good — severity level changed between issue states. $action = 'severity_changed'; $description = sprintf( /* translators: %s: Site Health test label */ __( 'Site Health Issue Severity Changed: %s', 'activity-log-pro' ), $test_label ); $severity = ( 'critical' === $new_status ) ? ACTLOGPRO_Severity::CRITICAL : ACTLOGPRO_Severity::MEDIUM; } ACTLOGPRO_Logger::log_activity( 'wordpress', 'site_health', 0, $test_label, $action, $description, array( 'test_id' => sanitize_key( $result['test'] ), 'new_status' => $new_status, 'previous_status' => $previous_status, 'category' => $badge_label, ), $severity ); } }