time(), 'path' => substr($path, 0, 255), // Limit length 'referrer' => !empty($referrer_raw) ? esc_url_raw(substr($referrer_raw, 0, 255)) : '', 'ua' => !empty($ua_raw) ? sanitize_text_field(substr($ua_raw, 0, 100)) : '' ]; $this->append_log($log_entry); } private function append_log($entry) { // We use get_option / update_option. // Note: For high traffic sites this faces race conditions. // PRD 2.4 "Minimal", "No background processing". // Acceptance criteria says "404 monitor records and displays recent 404s". // Options API is atomic enough for low volume, but risky for high volume. // Given constraints (no DB tables), we accept this limitation. $log = get_option(self::OPTION_KEY_LOG, []); if (!is_array($log)) { $log = []; } // Prepend new entry array_unshift($log, $entry); // Trim if (count($log) > self::MAX_LOG_SIZE) { $log = array_slice($log, 0, self::MAX_LOG_SIZE); } update_option(self::OPTION_KEY_LOG, $log, false); // autoload=false } public static function get_log() { $log = get_option(self::OPTION_KEY_LOG, []); return is_array($log) ? $log : []; } public function handle_clear_log() { if (!current_user_can('manage_options')) { wp_die('Unauthorized'); } check_admin_referer('at_seo_clear_404', 'at_seo_min_nonce'); update_option(self::OPTION_KEY_LOG, []); wp_safe_redirect(add_query_arg(['page' => 'active-twist-seo-status', 'tab' => 'errors', 'cleared' => '1'], admin_url('admin.php'))); exit; } }