int, type => string, message => string, severity => 'info' | * 'warn' | 'error' | 'success' ] * * Callers (from Cache.php and elsewhere): * Activity_Log::record('cache_purged', 'Cache purged (settings change)'); * Activity_Log::record('cache_enabled_event', 'Cache enabled', 'success'); * Activity_Log::record('conflict_detected', 'WP Rocket activated', 'warn'); * * Event type ids are snake_case — sanitize_key (called on the way in) * strips dots, so dotted ids would collapse silently. * * @package XSpeed */ declare(strict_types=1); namespace XSpeed; defined( 'ABSPATH' ) || exit; final class Activity_Log { public const TRANSIENT_KEY = 'xspeed_activity_log'; public const TTL = 2592000; // 30 days public const MAX_ENTRIES = 50; public const INFO = 'info'; public const WARN = 'warn'; public const ERROR = 'error'; public const SUCCESS = 'success'; public static function record( string $type, string $message, string $severity = self::INFO ): void { $entries = self::entries(); array_unshift( $entries, array( 'ts' => time(), 'type' => sanitize_key( $type ), 'message' => $message, // caller must produce already-safe text. 'severity' => in_array( $severity, array( self::INFO, self::WARN, self::ERROR, self::SUCCESS ), true ) ? $severity : self::INFO, ) ); if ( count( $entries ) > self::MAX_ENTRIES ) { $entries = array_slice( $entries, 0, self::MAX_ENTRIES ); } set_transient( self::TRANSIENT_KEY, $entries, self::TTL ); } /** * Newest-first entries (up to MAX_ENTRIES). Defensive shape coercion * so a malformed transient never breaks the dashboard. * * @return array */ public static function entries(): array { $raw = get_transient( self::TRANSIENT_KEY ); if ( ! is_array( $raw ) ) { return array(); } $out = array(); foreach ( $raw as $e ) { if ( is_array( $e ) && isset( $e['ts'], $e['type'], $e['message'] ) ) { $out[] = array( 'ts' => (int) $e['ts'], 'type' => (string) $e['type'], 'message' => (string) $e['message'], 'severity' => isset( $e['severity'] ) ? (string) $e['severity'] : self::INFO, ); } } return $out; } public static function clear(): void { delete_transient( self::TRANSIENT_KEY ); } }