acrobat/recaptcha-bundle
Symfony2 form integration for Google reCAPTCHA. Install via Composer, register the bundle, and configure your public/private keys in config.yml. Supports disabling per environment and optional Ajax loading, helping protect forms from spam and bots.
Installation:
composer require acrobat/recaptcha-bundle:~2.0
Enable the bundle in config/bundles.php:
Acrobat\RecaptchaBundle\AcrobatRecaptchaBundle::class => ['all' => true],
Configuration:
Add your reCAPTCHA keys to config/packages/acrobat_recaptcha.yaml:
acrobat_recaptcha:
site_key: 'YOUR_SITE_KEY'
secret_key: 'YOUR_SECRET_KEY'
version: 'v2' # or 'v1' if using legacy
First Use Case:
Add reCAPTCHA to a form in FormType:
use Acrobat\RecaptchaBundle\Form\Type\RecaptchaType;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('recaptcha', RecaptchaType::class);
}
Render it in your template:
{{ form_row(form.recaptcha) }}
Form Integration:
RecaptchaType in any Symfony form (e.g., contact forms, registration).RecaptchaValidator in a service:
# config/services.yaml
Acrobat\RecaptchaBundle\Validator\RecaptchaValidator:
arguments:
$client: '@acrobat_recaptcha.client'
$version: '%acrobat_recaptcha.version%'
Dynamic Key Handling:
# config/packages/dev/acrobat_recaptcha.yaml
acrobat_recaptcha:
site_key: 'DEV_SITE_KEY'
secret_key: 'DEV_SECRET_KEY'
API Client Customization:
RecaptchaClient to add logging or retry logic:
class CustomRecaptchaClient extends RecaptchaClient
{
public function verify($response, $remoteIp)
{
// Custom logic (e.g., retry on failure)
return parent::verify($response, $remoteIp);
}
}
Register it in services.yaml:
Acrobat\RecaptchaBundle\Client\RecaptchaClient: '@custom_recaptcha.client'
Twig Integration:
{% set recaptcha = app.container.get('acrobat_recaptcha') %}
{{ recaptcha.siteKey }}
Async Validation:
Constraint system for async validation (e.g., with Messenger):
use Acrobat\RecaptchaBundle\Validator\Constraints\Recaptcha;
#[Recaptcha()]
public $recaptchaResponse;
Deprecated Bundle:
symfony/form, guzzlehttp/guzzle) manually.Validation Errors:
secret_key is correct in config.response field name matches the form (recaptcha by default).google.com/recaptcha are not blocked (e.g., by firewall).CSRF Token Conflicts:
{{ form_rest() }} or {{ form_csrf_token() }} to avoid token mismatches.Legacy reCAPTCHA v1:
symfony/webpack-encore + custom JS.Log API Responses:
RecaptchaClient to log raw responses:
public function verify($response, $remoteIp)
{
$this->logger->debug('reCAPTCHA API Response:', ['response' => $response]);
return parent::verify($response, $remoteIp);
}
Test Locally:
6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI) to bypass rate limits during development.Check Headers:
X-Forwarded-For header is passed correctly if behind a proxy:
$remoteIp = $request->getClientIp(); // Use this instead of default
Custom Validation Logic:
RecaptchaValidator to add score thresholds (e.g., for v3):
public function validate($value, Constraint $constraint)
{
$result = $this->client->verify($value, $this->requestStack->getCurrentRequest()->getClientIp());
if ($result->isSuccess() && $result->getScore() < 0.5) {
$this->context->buildViolation($constraint->message)
->addViolation();
}
}
Multi-Language Support:
Resources/views/Recaptcha/recaptcha.html.twig) to support non-English labels:
<div class="g-recaptcha" data-sitekey="{{ recaptcha.siteKey }}" data-callback="onSubmit" data-language="{{ app.request.get('_locale') }}"></div>
Rate Limiting:
RecaptchaClient to cache responses and avoid hitting Google’s limits:
class CachingRecaptchaClient implements RecaptchaClientInterface
{
private $decorated;
private $cache;
public function verify($response, $remoteIp)
{
$cacheKey = md5($response . $remoteIp);
if ($this->cache->has($cacheKey)) {
return $this->cache->get($cacheKey);
}
$result = $this->decorated->verify($response, $remoteIp);
$this->cache->set($cacheKey, $result, 3600); // Cache for 1 hour
return $result;
}
}
Alternative Providers:
RecaptchaClient with a service for hCaptcha or other providers:
class HCaptchaClient implements RecaptchaClientInterface
{
public function verify($response, $remoteIp)
{
// Call hCaptcha API instead
}
}
```markdown
### Laravel-Specific Adaptations
While this bundle is for Symfony, here’s how a Laravel developer might adapt it conceptually:
1. **Service Provider**:
- Register the bundle’s logic in a Laravel service provider:
```php
public function register()
{
$this->app->singleton('recaptcha', function ($app) {
return new \Acrobat\RecaptchaBundle\Client\RecaptchaClient(
$app['config']['recaptcha.site_key'],
$app['config']['recaptcha.secret_key']
);
});
}
```
2. **Form Request Validation**:
- Use Laravel’s `FormRequest` to validate reCAPTCHA:
```php
use Acrobat\RecaptchaBundle\Validator\Constraints\Recaptcha;
public function rules()
{
return [
'g-recaptcha-response' => ['required', new Recaptcha()]
];
}
```
3. **Blade Integration**:
- Render the reCAPTCHA widget in Blade:
```blade
<div class="g-recaptcha"
data-sitekey="{{ config('recaptcha.site_key') }}"
data-callback="onSubmit">
</div>
```
4. **Config File**:
- Define keys in `.env`:
```
RECAPTCHA_SITE_KEY=your_site_key
RECAPTCHA_SECRET_KEY=your_secret_key
```
- Load them in `config/recaptcha.php`:
```php
'site_key' => env('RECAPTCHA_SITE_KEY'),
'secret_key' => env('RECAPTCHA_SECRET_KEY'),
```
5. **Middleware for API Calls**:
- Create middleware to verify reCAPTCHA on specific routes:
```php
public function handle($request, Closure $next)
{
$response = $request->input('g-recaptcha-response');
$client = app('recaptcha');
$result = $client->verify($response, $request->ip());
if (!$result->isSuccess()) {
abort(403, 'Invalid reCAPTCHA');
}
return $next($request);
}
```
How can I help you explore Laravel packages today?