PHPackages                             geekk/multi-captcha - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. geekk/multi-captcha

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

geekk/multi-captcha
===================

Various captchas that support the mechanism for switching between them in the configuration file

1.4.0(2mo ago)023.6k↓43.3%1MITPHPPHP &gt;=7.2

Since Jun 5Pushed 2mo ago2 watchersCompare

[ Source](https://github.com/geekk-net/multi-captcha)[ Packagist](https://packagist.org/packages/geekk/multi-captcha)[ Docs](https://github.com/geekk-net/multi-captcha)[ RSS](/packages/geekk-multi-captcha/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (14)Used By (1)

multi-captcha
=============

[](#multi-captcha)

Common php class interfaces for captchas. And various captchas implementation. You can use it for a switching of captcha's type in your project.

Supported types of captcha
--------------------------

[](#supported-types-of-captcha)

Now packages supports these types:

- Google ReCaptcha v2
- HCaptcha
- KCaptcha
- Cloudflare Turnstile
- Gregwar (image captcha, based on `gregwar/captcha`)

You can add new one type. You need implement CaptchaInterface and CaptchaRequestInterface.

Frameworks support
------------------

[](#frameworks-support)

This package didn't tie with any framework. It doesn't work with specific framework's classes or interfaces, for example `Illuminate\Http\Request`for http requests.

But you can ease create needed wrappers and factories for a working your framework and configuration files.

For Laravel, you can use the geekk/multi-captcha-laravel package.

Installation
------------

[](#installation)

Install package:

```
composer require geekk/multi-captcha

```

Using
-----

[](#using)

Configuration array:

```
$config = [
        'default' => 'hcaptcha',

        'connections' => [

            'recaptcha2' => [
                'driver' => 'recaptcha2',
                'site_key' => '...',
                'secret_key' => '...'
            ],

            'hcaptcha' => [
                'driver' => 'hcaptcha',
                'site_key' => '...',
                'secret_key' => '...'
            ],

            'kcaptcha' => [
                'driver' => 'kcaptcha',
                'show_credits' => false
            ],

            'gregwar' => [
                'driver' => 'gregwar',
                // optional: width, height, length, quality, allowed_symbols
            ],

            'turnstile' => [
                'driver' => 'turnstile',
                'site_key' => '...',
                'secret_key' => '...'
            ]
        ]
]
```

If you plan to use KCaptcha, you need implement storage class:

```
class CaptchaStore  implements CaptchaStoreInterface {

    protected $store;
    protected $prefix;
    protected $seconds;

    public function __construct(Repository $store, $prefix = 'kcaptcha:', int $seconds = 5*60)
    {
        $this->store = $store;
        $this->prefix = $prefix;
        $this->seconds = $seconds;
    }

    protected function getStoreKey($key)
    {
        return "$this->prefix:{$key}";
    }

    public function getValue(?string $key = null): ?string
    {
        $value = $this->store->get($this->getStoreKey($key));
        $this->store->forget($this->getStoreKey($key));
        return $value;
    }

    public function setValue(string $value, ?string $key = null)
    {
        $this->store->put($this->getStoreKey($key), $value);
    }
}
```

where Repository is some cache repository. Or you can use session instead of cache.

Implement the CaptchaManager - factory class:

```
use SomeNamespace/Request; // Request which you use

class CaptchaManager
{

    protected $config;

    protected $connectionName;

    protected $connectionConfig;

    public function __construct(array $config)
    {
        $this->config = $config;
    }

    private function loadDriverConfig()
    {
        $this->connectionName = $this->config['default'];
        $this->connectionConfig = $this->config['connections'][$this->connectionName];
    }

    public function getCaptcha(): CaptchaInterface
    {
        $this->loadDriverConfig();
        $driverName = $this->connectionConfig['driver'];
        switch ($driverName) {
            case 'recaptcha2':
                return new ReCaptcha2($this->connectionConfig);
            case 'hcaptcha':
                return new HCaptcha($this->connectionConfig);
            case 'kcaptcha':
                $store = new CaptchaStore();
                return new KCaptcha($this->connectionConfig, $store);
            case 'gregwar':
                $store = new CaptchaStore();
                return new Gregwar($this->connectionConfig, $store);
            case 'turnstile':
                return new Turnstile($this->connectionConfig);
        }
        throw new \Exception(sprintf('Unknown captcha driver: %s', $driverName));
    }

    public function getRequest(Request $request): CaptchaRequestInterface
    {
        $driverName = $this->connectionConfig['driver'];
        switch ($driverName) {
            case 'recaptcha2':
                return new ReCaptcha2Request(count($request->post()), $request->post(ReCaptcha2Request::RESPONSE_NAME), $request->ip());
            case 'hcaptcha':
                return new HCaptchaRequest(count($request->post()), $request->post(HCaptchaRequest::RESPONSE_NAME), $request->ip());
            case 'kcaptcha':
                return new KCaptchaRequest(count($request->post()), $request->post(KCaptchaRequest::RESPONSE_NAME), $request->post(KCaptchaRequest::KEY_NAME));
            case 'gregwar':
                return new GregwarRequest(count($request->post()), $request->post(GregwarRequest::RESPONSE_NAME), $request->post(GregwarRequest::KEY_NAME));
            case 'turnstile':
                return new TurnstileRequest(count($request->post()), $request->post(TurnstileRequest::RESPONSE_NAME), $request->ip());
        }
        throw new \Exception(sprintf('Unknown captcha driver: %s', $driverName));
    }
}
```

**Alternative: single request via `CaptchaRequest`**

If the frontend uses the same component for all captcha types (e.g. a Vue component with unified field names), you can avoid distinguishing request type and always create a `CaptchaRequest`. Form field names are defined once (e.g. `captcha-response`):

```
use Geekk\MultiCaptcha\CaptchaRequest;

public function getRequest(Request $request): CaptchaRequestInterface
{
    // For recaptcha2, hcaptcha, turnstile
    $submitted = (bool) count($request->post());
    $response = $request->post('captcha-response');
    $context = $request->ip();
    return new CaptchaRequest($submitted, $response, $context);
}
```

Note: for KCaptcha and Gregwar the context is the store key (sent from the frontend), not the IP, so with this approach either do not use KCaptcha/Gregwar or handle them in a separate branch (e.g. a separate `if` by driver).

Then you can use it

```
$captchaManager = new CaptchaManager($config);

$captcha = $captchaManager->getCaptcha();

// Render captcha in template
echo $captcha->render();

// Verify user's response
$result = $captcha->verify($captchaManager->getRequest($request));
```

Customising captcha's view
--------------------------

[](#customising-captchas-view)

Use css for a customizing.

For captcha's templates generated on frontend side you can get data from method `CaptchaInterface::getViewData()`.

###  Health Score

48

—

FairBetter than 94% of packages

Maintenance86

Actively maintained with recent releases

Popularity26

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 80% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~144 days

Recently: every ~3 days

Total

13

Last Release

75d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f5d036db1914a24e650265fabd3566b0218090e02a210bf7fa5dc0c1023463c5?d=identicon)[geekk-net](/maintainers/geekk-net)

---

Top Contributors

[![itelmenko](https://avatars.githubusercontent.com/u/495449?v=4)](https://github.com/itelmenko "itelmenko (16 commits)")[![geekk-net](https://avatars.githubusercontent.com/u/74247579?v=4)](https://github.com/geekk-net "geekk-net (4 commits)")

---

Tags

recaptchacaptchahcaptchakcaptcha

### Embed Badge

![Health badge](/badges/geekk-multi-captcha/health.svg)

```
[![Health](https://phpackages.com/badges/geekk-multi-captcha/health.svg)](https://phpackages.com/packages/geekk-multi-captcha)
```

###  Alternatives

[karser/karser-recaptcha3-bundle

Google ReCAPTCHA v3 for Symfony

1862.4M7](/packages/karser-karser-recaptcha3-bundle)[aryehraber/statamic-captcha

Protect your Statamic forms using a Captcha service

16194.4k](/packages/aryehraber-statamic-captcha)[abanoubnassem/filament-grecaptcha-field

Provides a Google reCaptcha V2 field for the Filament Forms

27116.1k2](/packages/abanoubnassem-filament-grecaptcha-field)[palmtree/form

Form builder with Bootstrap v5/v4 classes, validation, Recaptcha support, AJAX submissions and more

384.8k1](/packages/palmtree-form)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
