Current File : /home/digitaw/www/wp-content/plugins/simple-history/inc/channels/formatters/class-formatter.php
<?php

namespace Simple_History\Channels\Formatters;

use Simple_History\Log_Levels;

/**
 * Abstract base class for log entry formatters.
 *
 * Provides common functionality for all formatters including
 * log level to syslog severity mapping and timestamp helpers.
 *
 * @since 5.7.0
 */
abstract class Formatter implements Formatter_Interface {
	/**
	 * Map Simple History log levels to syslog severity (RFC 5424).
	 *
	 * @var array<string, int>
	 */
	protected const SYSLOG_SEVERITY_MAP = [
		Log_Levels::EMERGENCY => 0,
		Log_Levels::ALERT     => 1,
		Log_Levels::CRITICAL  => 2,
		Log_Levels::ERROR     => 3,
		Log_Levels::WARNING   => 4,
		Log_Levels::NOTICE    => 5,
		Log_Levels::INFO      => 6,
		Log_Levels::DEBUG     => 7,
	];

	/**
	 * Essential context fields to include in formatted output.
	 *
	 * @var array<int, string>
	 */
	protected const ESSENTIAL_FIELDS = [
		'_message_key',
		'_server_remote_addr',
		'_user_id',
		'_user_login',
		'_user_email',
	];

	/**
	 * Get the event ID from event data.
	 *
	 * @param array $event_data The event data array.
	 * @return int|null The event ID or null if not available.
	 */
	protected function get_event_id( array $event_data ): ?int {
		return isset( $event_data['id'] ) ? (int) $event_data['id'] : null;
	}

	/**
	 * Get syslog severity from Simple History log level.
	 *
	 * @param string $level The Simple History log level.
	 * @return int Syslog severity (0-7).
	 */
	protected function get_syslog_severity( string $level ): int {
		$level_lower = strtolower( $level );
		return self::SYSLOG_SEVERITY_MAP[ $level_lower ] ?? 6;
	}

	/**
	 * Get event timestamp in ISO 8601 UTC format.
	 *
	 * The event date is already stored in UTC/GMT in the database.
	 *
	 * @param string $event_date The event date in MySQL format (Y-m-d H:i:s), already in UTC.
	 * @return string ISO 8601 formatted timestamp in UTC with 'Z' suffix.
	 */
	protected function get_event_timestamp_utc( string $event_date ): string {
		// Event date is already in UTC, just reformat to ISO 8601.
		$datetime = date_create( $event_date, new \DateTimeZone( 'UTC' ) );
		if ( $datetime === false ) {
			// Fallback to current UTC time if parsing fails.
			return gmdate( 'Y-m-d\TH:i:s\Z' );
		}
		return $datetime->format( 'Y-m-d\TH:i:s\Z' );
	}

	/**
	 * Get current timestamp in ISO 8601 format with timezone.
	 *
	 * @deprecated Use get_event_timestamp_utc() with event date instead.
	 * @return string ISO 8601 formatted timestamp.
	 */
	protected function get_iso8601_timestamp(): string {
		return current_time( 'c' );
	}

	/**
	 * Get event timestamp as Unix timestamp.
	 *
	 * The event date is already stored in UTC/GMT in the database.
	 *
	 * @param string $event_date The event date in MySQL format (Y-m-d H:i:s), already in UTC.
	 * @return float Unix timestamp.
	 */
	protected function get_event_timestamp_unix( string $event_date ): float {
		// Event date is already in UTC.
		$datetime = date_create( $event_date, new \DateTimeZone( 'UTC' ) );
		if ( $datetime === false ) {
			// Fallback to current time if parsing fails.
			return microtime( true );
		}
		return (float) $datetime->format( 'U' );
	}

	/**
	 * Get current timestamp as Unix timestamp with milliseconds.
	 *
	 * @deprecated Use get_event_timestamp_unix() with event date instead.
	 * @return float Unix timestamp with milliseconds.
	 */
	protected function get_unix_timestamp(): float {
		return microtime( true );
	}

	/**
	 * Get the site hostname.
	 *
	 * @return string The site hostname.
	 */
	protected function get_hostname(): string {
		$parsed = wp_parse_url( get_site_url() );
		return $parsed['host'] ?? 'localhost';
	}

	/**
	 * Extract essential fields from event context.
	 *
	 * Returns only scalar values from the essential fields list,
	 * with leading underscores removed from keys.
	 *
	 * @param array $context The event context array.
	 * @return array<string, mixed> Filtered context with clean keys.
	 */
	protected function get_essential_context( array $context ): array {
		$result = [];

		foreach ( self::ESSENTIAL_FIELDS as $field ) {
			if ( isset( $context[ $field ] ) && is_scalar( $context[ $field ] ) ) {
				$clean_key            = ltrim( $field, '_' );
				$result[ $clean_key ] = $context[ $field ];
			}
		}

		return $result;
	}

	/**
	 * Sanitize a value for safe inclusion in structured data.
	 *
	 * @param mixed $value The value to sanitize.
	 * @return string|int|float|bool The sanitized scalar value.
	 */
	protected function sanitize_value( $value ) {
		if ( is_scalar( $value ) ) {
			return $value;
		}
		return wp_json_encode( $value );
	}
}