current_time( 'mysql' ), 'level' => sanitize_key( $level ), 'message' => sanitize_text_field( $message ), 'context' => $this->sanitize_log_context( $context ), ); $log = get_option( self::OPTION_NAME, array() ); $log = is_array( $log ) ? $log : array(); $log[] = $entry; if ( count( $log ) > self::MAX_ENTRIES ) { $log = array_slice( $log, -self::MAX_ENTRIES, null, true ); } update_option( self::OPTION_NAME, $log, false ); } /** * Sanitize context for storage (scalars only). * * @param array $context Raw context. * @return array */ private function sanitize_log_context( array $context ): array { $out = array(); foreach ( $context as $k => $v ) { if ( is_scalar( $v ) ) { $out[ sanitize_key( (string) $k ) ] = $v; } } return $out; } /** * Get total number of log entries. * * @return int */ public function get_log_count(): int { $log = get_option( self::OPTION_NAME, array() ); $log = is_array( $log ) ? $log : array(); return count( $log ); } /** * Get a page of log entries (newest first). * * @param int $per_page Entries per page. * @param int $page Page number (1-based). * @return array{items: array, total: int} */ public function get_log_page( int $per_page = 20, int $page = 1 ): array { $log = get_option( self::OPTION_NAME, array() ); $log = is_array( $log ) ? $log : array(); $total = count( $log ); $reversed = array_reverse( $log ); $offset = max( 0, ( $page - 1 ) * $per_page ); $items = array_slice( $reversed, $offset, $per_page ); return array( 'items' => $items, 'total' => $total, ); } /** * Get recent log entries (for backward compatibility). * * @param int $limit Max entries. * @return array */ public function get_log( int $limit = 100 ): array { $result = $this->get_log_page( $limit, 1 ); return $result['items']; } /** * Clear the log. */ public function clear(): void { delete_option( self::OPTION_NAME ); } }