Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Antispam Laravel Package

cleantalk/php-antispam

CleanTalk PHP Anti-Spam is a lightweight PHP library that blocks spam form and comment submissions without CAPTCHA. It connects to the CleanTalk cloud service and works with Laravel and other PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install via Composer (the package uses PSR-4 autoloading, requires ext-curl and ext-json):

    composer require cleantalk/php-antispam
    
  2. Get an access key — register the site at cleantalk.org and copy the key from the dashboard. The key is bound to a domain, so use one key per site/environment (you can register a separate key for staging).

  3. Construct the client once per request lifecycle and configure it before calling any check:

    use Cleantalk\Antispam\Cleantalk;
    use Cleantalk\Antispam\CleantalkRequest;
    
    $ct = new Cleantalk();
    $ct->authKey      = getenv('CLEANTALK_KEY');
    $ct->serverUrl    = 'https://moderate.cleantalk.org';
    $ct->serverPort   = 443;
    $ct->serverTimeout = 5;
    $ct->workUrl      = 'https://your-site.test';
    $ct->agent       .= ' my-app/1.0';
    
  4. First use case — protect a comment/contact form with isSpam():

    $req = new CleantalkRequest();
    $req->message        = $comment;
    $req->senderIp       = $_SERVER['REMOTE_ADDR'];
    $req->senderEmail    = $email;
    $req->senderNickname = $name;
    $req->jsOn           = 1;                 // set by JS only if JS actually ran
    $req->submitTime     = time() - $formRenderedAt; // seconds the user spent on the form
    
    $res = $ct->isSpam($req);
    
    if ((int) $res->allow === 0) {
        // treat as spam; $res->comment holds a human-readable reason
    }
    
  5. Where to look first:

    • src/CleantalkRequest.php — the full list of fields you can supply (this file is the real documentation; the README only shows a subset).
    • src/Cleantalk.php — available check methods (isSpam, checkNewuser, checkCaptcha, getApiKey) and transport config (serverUrl, serverPort, serverTimeout, debug).
    • src/CleantalkResponse.php — response contract (allow, comment, errno, errstr, id, stop_queue, inactive).
    • src/CleantalkHelper.php — helpers such as IP extraction you can reuse.

Implementation Patterns

Wrap it in a small service/repository. Don't scatter new Cleantalk() across controllers. Create one service that reads key/URL from config and exposes isSpam(array $payload): bool plus checkRegistration(array $payload): bool. In Laravel, bind it as a singleton in a service provider and inject it.

final class AntiSpam
{
    public function __construct(private Cleantalk $ct) {}

    public function check(array $data): CleantalkResponse
    {
        $req = new CleantalkRequest();
        $req->message        = $data['message'] ?? '';
        $req->senderIp       = $data['ip'];
        $req->senderEmail    = $data['email'] ?? '';
        $req->senderNickname = $data['name'] ?? '';
        $req->jsOn           = $data['js_on'] ?? 0;
        $req->submitTime     = $data['submit_time'] ?? 0;
        $req->agent          = 'php app/1.0';
        return $this->ct->isSpam($req);
    }
}

Middleware pattern (Laravel / any framework with middleware). Apply to POST routes that create user content — comments, contact forms, reviews, ticket creation. Check the response, and on allow === 0 abort with a user-facing message; don't dump $res->comment verbatim (it's a debug hint, not UX copy).

// app/Http/Middleware/CleanTalkSpam.php
public function handle($request, Closure $next)
{
    $res = $this->antispam->check([
        'message' => (string) $request->input('message'),
        'email'   => (string) $request->input('email'),
        'name'    => (string) $request->input('name'),
        'ip'      => $request->ip(),
        'js_on'   => (int) $request->boolean('js_on'),
        'submit_time' => time() - (int) $request->input('form_ts', time()),
    ]);

    if ($res->errno === 0 && (int) $res->allow === 0) {
        return response()->json(['error' => 'Your message was flagged as spam.'], 422);
    }
    return $next($request);
}

Two-phase form integration. The invisible scoring is far better when you feed it real signals:

  • Render a hidden form_ts (server time() at render) and a hidden js_on that gets flipped to 1 by a tiny inline script on submit.
  • On POST, compute submit_time = time() - form_ts. A value under ~2–3 seconds is a strong bot signal (and you'll want a minimum on the server side too, otherwise humans with autofill trip it).

Use the right method per context.

  • isSpam() for messages, comments, contact forms, reviews.
  • checkNewuser() for registration forms (different heuristics, plus email/username signals).
  • getApiKey() if you want to drop the CleanTalk JS snippet in and let the browser pre-score — useful for high-traffic forms where you want to avoid a server round trip on every submission.

Fail policy: choose explicitly. Wrap the call in try/catch and branch on $res->errno:

  • errno === 0 → trust allow.
  • errno !== 0 (timeout, DNS, blocked egress) → either allow (fail-open, better for conversions) or reject (fail-closed, only if you also have a honeypot/rate limit). Never silently swallow an error without logging errno/errstr, or you'll be blind to a dead integration.

Skip checks for trusted traffic. Don't call the API for authenticated members with a good history, admin-preview requests, or non-mutating requests. It costs latency and quota.

Log the returned id. It's the CleanTalk-side request id, and it's what support asks for when you need to dispute a false positive.

Gotchas and Tips

  • The field names changed between major versions. Older tutorials show $request->auth_key, $request->sender_ip, $request->js_on; current versions use camelCase (authKey, senderIp, jsOn). Always verify against the installed src/CleantalkRequest.php rather than copy-pasting a blog snippet.
  • allow is inverted from what you expect. 1 = allowed/not spam, 0 = spam/blocked. Read the value as an int; some code paths return strings.
  • Outbound HTTPS is mandatory. Shared hosts and corporate egress rules frequently block moderate.cleantalk.org:443. Test with curl -v https://moderate.cleantalk.org from the same PHP user; a blocked firewall makes every submission pass or fail uniformly, which looks like "the plugin stopped working".
  • jsOn must reflect reality. Hardcoding 1 fakes the signal and degrades scoring; hardcoding 0 for everyone throws away free accuracy. Set it from a JS-driven hidden field.
  • submitTime is a duration, not a timestamp. Pass seconds-since-form-render. Passing time() directly makes every user look like they submitted instantly or spent decades on the page. If you truly don't know, pass 0 rather than a wrong value.
  • IP detection behind proxies is a classic footgun. REMOTE_ADDR will be your load balancer/Cloudflare edge. Don't trust X-Forwarded-For blindly (it's user-supplied); use your framework's trusted-proxy config or CleantalkHelper::ipGet(), and ensure the IP is IPv4/IPv6 valid — malformed IPs are silently dropped and degrade scoring.
  • Wrong key → errno set, allow often still 1. A mismatched domain/key combo won't hard-fail the request, so the integration looks "fine" while every check passes. Assert errno === 0 in your tests.
  • comment is not user-facing copy. It's an English debug string (or localized via responseLang). Show your own message and keep comment for logs.
  • senderInfo must be valid JSON if you pass it. Malformed JSON is dropped without an error.
  • Set serverTimeout explicitly. Defaults are not tuned for web request paths; 3–5s is a sane ceiling. A slow CleanTalk response will otherwise stall your form submission.
  • debug = 1 dumps the raw request/response — invaluable when debugging scoring, but never leave it on in production (it can print the auth key and user data).
  • Testing hook: point serverUrl at a local stub server (or subclass Cleantalk and override the HTTP send method) and assert on errno/allow combinations — success, spam, timeout, and invalid key. Don't hit the live API from CI.
  • False positives are real. This is a background scoring service, not a deterministic filter. Pair it with a honeypot field and a rate limiter, and always provide a way for a flagged user to contact you — log the response id so you can ask CleanTalk to whitelist.
  • Don't create a new Cleantalk instance per check in a long-running worker (Octane/queue daemon) unless you rebuild config each time; it's cheap, but the config assignment is the part that matters.
  • Version pin and read the CHANGELOG on upgrade — response fields (inactive, stop_queue) and request fields have been added/renamed across releases, and silent renames are the usual cause of "spam protection stopped working after composer update".
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings