$id, 'site_id' => $config['site_id'], 'at' => time()], '', false)) { self::$fresh[] = $name; } } public static function flushNew(): void { $names = self::$fresh; self::$fresh = []; if ($names) { self::flush($names); } } // Direct SQL avoids stale object caches. Unique option_name plus a // compare-and-swap prevents a stale worker from replacing a newer lease. private static function acquire(): ?string { global $wpdb; $token = (time() + 30) . ':' . wp_generate_uuid4(); $inserted = $wpdb->query($wpdb->prepare("INSERT IGNORE INTO {$wpdb->options} (option_name,option_value,autoload) VALUES (%s,%s,'no')", self::LOCK, $token)); wp_cache_delete(self::LOCK, 'options'); wp_cache_delete('notoptions', 'options'); if ($inserted === 1) { return $token; } $old = $wpdb->get_var($wpdb->prepare("SELECT option_value FROM {$wpdb->options} WHERE option_name=%s", self::LOCK)); if (!is_string($old) || (int) $old > time()) { return null; } $updated = $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET option_value=%s WHERE option_name=%s AND option_value=%s", $token, self::LOCK, $old)); wp_cache_delete(self::LOCK, 'options'); return $updated === 1 ? $token : null; } public static function flush(?array $names = null): void { global $wpdb; $config = get_option('formguard_connection', []); if (empty($config['site_id'])) { return; } $token = self::acquire(); if ($token === null) { return; } $failed = false; $deadline = microtime(true) + 5; try { // Old retries run through the existing five-minute Cron, not page views. $names = $names ?? $wpdb->get_col($wpdb->prepare("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s ORDER BY option_id LIMIT 10", $wpdb->esc_like('formguard_receipt_') . '%')); $names = array_slice($names, 0, 10); foreach ($names as $name) { $pending = get_option($name, []); if (!is_array($pending) || !is_string($pending['request_id'] ?? null) || ($pending['at'] ?? 0) <= time() - 1800 || ($pending['site_id'] ?? '') !== $config['site_id']) { delete_option($name); continue; } $remaining = $deadline - microtime(true); if ($remaining < 0.1) { break; } $response = FormGuard_Releases::request('/v1/receipts', ['decision_request_id' => $pending['request_id']], $remaining); if (($response['acknowledged'] ?? false) === true || ($response['reason'] ?? '') === 'not_confirmable') { delete_option($name); } else { $failed = true; break; } } } catch (\Throwable $error) { // Keep the confirmation queued if transport or a WP hook fails. $failed = true; } finally { $next = ($failed ? time() + 60 : 0) . ':' . wp_generate_uuid4(); $wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET option_value=%s WHERE option_name=%s AND option_value=%s", $next, self::LOCK, $token)); wp_cache_delete(self::LOCK, 'options'); } } }