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
);
}
}