# contact-forms/2.3.0/classes/Validation/CaptchaSpam.php

Contact Forms by Cimatti, version 2.3.0. 87 lines.

- Page: https://pluginprobe.com/plugins/contact-forms/2.3.0/code/classes/Validation/CaptchaSpam.php
- Raw: https://pluginprobe.com/plugins/contact-forms/2.3.0/raw/classes/Validation/CaptchaSpam.php
- Modified: 2026-08-05T09:38:42+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/contact-forms/2.3.0/code/classes/Validation/CaptchaSpam.php#L10-L20`.

```php
<?php
/**
 * Shared base for captcha validations with a configurable spam action
 * (reCAPTCHA v2 and v3).
 *
 * The secret key, the flag key and the per-form spam action live on the
 * validation object (not on the Element) so they survive the encrypted-form
 * serialization round trip: Element::__sleep() only keeps attributes, label,
 * validation and errors.
 *
 * Spam action ($spamAction):
 * - '' / 'reject' — a failed check makes validation fail (visible error).
 *   The empty value only occurs on validators serialized before the spam
 *   action existed (encrypted-form round trip of an already-rendered page);
 *   new renders always receive the explicit value resolved by
 *   accua_forms_captcha_spam_action() ('spam' by default).
 * - 'spam' / 'trash' / 'delete' — silent classification: a failed check lets
 *   the submission pass validation but flags it via self::$spam_flagged,
 *   keyed by $captchaAction; the submission handler then marks the stored
 *   submission as Spam, trashes it or deletes it, and skips the emails.
 *
 * @package Contact Forms
 */

abstract class AccuaForm_Validation_CaptchaSpam extends Validation {
	protected $privateKey;

	/**
	 * Request-scoped flag key, accuaform_{fid}. For reCAPTCHA v3 it doubles as
	 * the expected Google action; for v2 it is only the registry key.
	 *
	 * @var string
	 */
	protected $captchaAction = '';
	protected $spamAction = '';

	/**
	 * Flag keys (accuaform_{fid}) whose captcha verification failed during this
	 * request while a silent spam action was configured, mapped to the silent
	 * action that validator was configured with. Shared by all captcha
	 * validators (the static lives on this base class).
	 *
	 * The action is recorded rather than a bare true because v2 and v3 have
	 * different defaults ('reject' and 'spam'): on a form carrying both, the
	 * submission handler must follow the action of the field that actually
	 * failed, not the first captcha field it happens to find.
	 *
	 * @var array<string, string>
	 */
	protected static $spam_flagged = array();

	/**
	 * Whether the current request's submission for the given flag key was
	 * silently classified as spam.
	 *
	 * @param string $captcha_action The per-form flag key (accuaform_{fid}).
	 * @return bool
	 */
	public static function isSpamFlagged( $captcha_action ) {
		return ! empty( self::$spam_flagged[ $captcha_action ] );
	}

	/**
	 * The silent spam action of the validator that flagged this request.
	 *
	 * @param string $captcha_action The per-form flag key (accuaform_{fid}).
	 * @return string 'spam', 'trash', 'delete', or '' when nothing was flagged.
	 */
	public static function getSpamAction( $captcha_action ) {
		return isset( self::$spam_flagged[ $captcha_action ] ) ? (string) self::$spam_flagged[ $captcha_action ] : '';
	}

	/**
	 * Handle a failed verification according to the configured spam action:
	 * fail validation (reject) or flag the submission and let it pass (silent).
	 *
	 * @return bool The value isValid() must return.
	 */
	protected function failed() {
		if ( in_array( $this->spamAction, array( 'spam', 'trash', 'delete' ), true ) ) {
			self::$spam_flagged[ $this->captchaAction ] = $this->spamAction;
			return true;
		}
		return false;
	}
}

```
