Files
novaconium/novaconium/lib/SpamGuard.php
T
code fbd4e92e9f Replace v1 with v2 codebase
Full rewrite: swap out the v1 framework (src/, controllers/, views/,
twig/, sass/, skeleton/) for the working v2 codebase from phpproject
(App/, novaconium/, public/).
2026-07-14 01:58:25 +00:00

52 lines
1.7 KiB
PHP

<?php
namespace Lib;
/**
* Self-hosted spam detection for any form sidecar: a honeypot field plus
* a submission-timing check. No external CAPTCHA service, no CDN script,
* no site key/secret key, no outbound API call — see /admin/docs/sidecars's
* "Spam prevention" section for the full write-up and App/pages/contact/
* for a working example.
*
* Pair this with a hidden honeypot input (named per $honeypotField below,
* hidden off-screen via the .hp-field CSS class — not display:none, since
* some bots specifically skip fields hidden that way) and a hidden
* `renderedAt()`-valued timestamp field in the form's Twig template.
*/
final class SpamGuard
{
public function __construct(
private readonly string $honeypotField = 'website',
private readonly string $timestampField = 'rendered_at',
private readonly int $minSeconds = 2,
) {
}
/**
* Call this when rendering the form (i.e. in the sidecar's return
* array, GET or POST) and put the result in a hidden field named
* $timestampField for isSpam() to read back on submit.
*/
public function renderedAt(): int
{
return time();
}
/**
* @param array<string,mixed> $post typically $_POST
*/
public function isSpam(array $post): bool
{
$honeypotFilled = trim((string) ($post[$this->honeypotField] ?? '')) !== '';
$renderedAt = (int) ($post[$this->timestampField] ?? 0);
// Not cryptographically signed, so a determined bot could forge
// this — it's a deterrent against unsophisticated spam, not a
// security boundary.
$tooFast = $renderedAt === 0 || (time() - $renderedAt) < $this->minSeconds;
return $honeypotFilled || $tooFast;
}
}