Hi @obtrusive170,
I’ve just emailed you the latest version AI produced, and it seems to be working on my end anyway. Let me know how you get on.
For anyone else interested in the code:
<?php
/**
* Cache-safe Math CAPTCHA for HivePress registration and reviews forms.
* No third-party services. Toggle each form via the constants below.
*
* v2 changes:
* - No admin-ajax.php. Snippet managers (FluentSnippets, WPCode, etc.) often
* don't load "frontend" snippets during admin-ajax requests, because
* admin-ajax.php is admin context (is_admin() === true). That caused a
* 400 Bad Request: the endpoint was simply never registered. The challenge
* is now served from a plain frontend request (?hp_math_challenge=1),
* which is exactly the context where this snippet provably runs.
* - Cache-safe by design: the page HTML contains no randomness, so full page
* caching (LiteSpeed, FlyingPress, WP Rocket, Cloudflare...) stays enabled
* everywhere. The challenge request itself is a POST (page caches only
* serve GET/HEAD), sends nocache_headers(), and defines DONOTCACHEPAGE
* scoped to that single request only.
* - No closing PHP tags anywhere, for maximum snippet-manager compatibility.
*/
// -------------------------------------------------------------------------
// Configuration: enable/disable math CAPTCHA for each form.
// -------------------------------------------------------------------------
define( 'HP_MATH_CAPTCHA_REGISTRATION', true );
define( 'HP_MATH_CAPTCHA_REVIEWS', true );
// -------------------------------------------------------------------------
// 1. Placeholder fields — no randomness at render time, so pages are
// identical for every visitor and 100% cache-safe.
// -------------------------------------------------------------------------
function hp_add_math_challenge_fields( $form ) {
$form['fields']['math_challenge_id'] = [
'type' => 'hidden',
];
$form['fields']['math_challenge'] = [
'label' => 'Spam check',
'type' => 'text',
'required' => true,
'_order' => 100,
'attributes' => [
'autocomplete' => 'off',
],
];
return $form;
}
// -------------------------------------------------------------------------
// 2. Frontend challenge endpoint: POST yoursite.com/?hp_math_challenge=1
// Runs on `init`, before any template or cache logic, and exits with
// JSON. DONOTCACHEPAGE is defined HERE — scoped to this request only,
// so it never disables caching for normal pages.
// -------------------------------------------------------------------------
function hp_math_challenge_endpoint() {
if ( empty( $_GET['hp_math_challenge'] ) ) {
return;
}
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
}
nocache_headers();
$num1 = wp_rand( 5, 15 );
$num2 = wp_rand( 5, 15 );
$challenge_id = wp_generate_uuid4();
set_transient( 'hp_math_answer_' . $challenge_id, (string) ( $num1 + $num2 ), HOUR_IN_SECONDS );
wp_send_json_success(
[
'id' => $challenge_id,
'question' => sprintf( 'What is %d + %d?', $num1, $num2 ),
]
);
}
add_action( 'init', 'hp_math_challenge_endpoint' );
// -------------------------------------------------------------------------
// 3. Frontend loader: fetches a challenge only when a form containing the
// math_challenge field is opened (modal link click), focused, or linked
// directly via a URL hash. Generic — covers registration AND reviews.
// -------------------------------------------------------------------------
function hp_math_challenge_footer_script() {
$endpoint = wp_json_encode( add_query_arg( 'hp_math_challenge', '1', home_url( '/' ) ) );
$script = <<<JS
<script>
(function () {
'use strict';
var ENDPOINT = {$endpoint};
function initChallenge(form) {
if (!form || form.dataset.mathLoaded === '1') {
return;
}
var answerField = form.querySelector('input[name="math_challenge"]');
var idField = form.querySelector('input[name="math_challenge_id"]');
if (!answerField || !idField) {
return;
}
form.dataset.mathLoaded = '1';
fetch(ENDPOINT, { method: 'POST', credentials: 'same-origin', cache: 'no-store' })
.then(function (r) {
if (!r.ok) { throw new Error('HTTP ' + r.status); }
return r.json();
})
.then(function (res) {
if (!res || !res.success) { throw new Error('Bad challenge response'); }
idField.value = res.data.id;
// Always set the placeholder, and also update the label
// if we can find it.
answerField.placeholder = res.data.question;
var wrapper = answerField.closest('.hp-form__field');
var label = wrapper ? wrapper.querySelector('label') : null;
if (label) {
var span = label.querySelector('span');
(span || label).textContent = res.data.question;
}
})
.catch(function () {
// Allow a retry on the next interaction.
form.dataset.mathLoaded = '';
});
}
function initInside(container) {
if (!container) { return; }
var input = container.querySelector('input[name="math_challenge"]');
if (input) {
initChallenge(input.closest('form'));
}
}
function targetFromHash(hash) {
if (!hash || hash.length < 2) { return null; }
try {
return document.querySelector(hash);
} catch (e) {
return null;
}
}
// Any modal trigger link (e.g. #user_register_modal, #review_submit_modal).
document.addEventListener('click', function (e) {
var link = e.target.closest('a[href^="#"], a[href*="#"]');
if (!link) { return; }
var href = link.getAttribute('href') || '';
var idx = href.indexOf('#');
if (idx === -1) { return; }
initInside(targetFromHash(href.slice(idx)));
});
// Focus anywhere inside a form containing the challenge field.
document.addEventListener('focusin', function (e) {
var form = e.target.closest('form');
if (form && form.querySelector('input[name="math_challenge"]')) {
initChallenge(form);
}
});
// Direct links such as https://example.com/#user_register_modal
function initFromLocation() {
initInside(targetFromHash(window.location.hash));
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initFromLocation);
} else {
initFromLocation();
}
})();
</script>
JS;
echo $script; // phpcs:ignore WordPress.Security.EscapeOutput
}
add_action( 'wp_footer', 'hp_math_challenge_footer_script', 100 );
// -------------------------------------------------------------------------
// 4. Validation — consumes the transient on success. Hardened: missing
// fields now fail instead of silently passing (a bot that omitted the
// fields from its POST could previously bypass the check entirely).
// -------------------------------------------------------------------------
function hp_validate_math_challenge( $errors, $form ) {
if ( ! isset( $_POST['math_challenge'], $_POST['math_challenge_id'] ) ) {
$errors[] = 'Spam check missing. Please refresh the page and try again.';
return $errors;
}
$challenge_id = sanitize_text_field( wp_unslash( $_POST['math_challenge_id'] ) );
$user_answer = (int) sanitize_text_field( wp_unslash( $_POST['math_challenge'] ) );
$stored = get_transient( 'hp_math_answer_' . $challenge_id );
if ( false === $stored ) {
$errors[] = 'Spam check expired. Please refresh the page and try again.';
} elseif ( $user_answer !== (int) $stored ) {
$errors[] = 'Incorrect answer. Please try again.';
} else {
// Correct — consume the transient to prevent replay.
delete_transient( 'hp_math_answer_' . $challenge_id );
}
return $errors;
}
// -------------------------------------------------------------------------
// Registration form.
// -------------------------------------------------------------------------
if ( HP_MATH_CAPTCHA_REGISTRATION ) {
add_filter( 'hivepress/v1/forms/user_register', 'hp_add_math_challenge_fields', 1000 );
add_filter( 'hivepress/v1/forms/user_register/errors', 'hp_validate_math_challenge', 1000, 2 );
}
// -------------------------------------------------------------------------
// Reviews form.
// -------------------------------------------------------------------------
if ( HP_MATH_CAPTCHA_REVIEWS ) {
add_filter( 'hivepress/v1/forms/review_submit', 'hp_add_math_challenge_fields', 1000 );
add_filter( 'hivepress/v1/forms/review_submit/errors', 'hp_validate_math_challenge', 1000, 2 );
}
I hope this helps!
Cheers,
Chris 