Skip to content

Symfony

EU CAPTCHA integrates with Symfony via the myra-security-gmbh/eu-captcha PHP package. The package ships with EuCaptchaInterface, which you can type-hint in controllers and services so Symfony autowires the client without coupling your code to the concrete class.

Installation

composer require myra-security-gmbh/eu-captcha

Configuration

.env

EUCAPTCHA_SITE_KEY=YOUR_SITEKEY
EUCAPTCHA_SECRET_KEY=YOUR_SECRET

config/services.yaml

Register EuCaptcha as a service and alias the interface to it:

services:
    Myrasec\EuCaptcha:
        arguments:
            $sitekey: '%env(EUCAPTCHA_SITE_KEY)%'
            $secret:  '%env(EUCAPTCHA_SECRET_KEY)%'
    Myrasec\EuCaptchaInterface: '@Myrasec\EuCaptcha'

Symfony resolves the alias automatically, so any service or controller that type-hints EuCaptchaInterface receives the configured EuCaptcha instance.

Controller

<?php

namespace App\Controller;

use Myrasec\EuCaptchaInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class ContactController extends AbstractController
{
    public function __construct(private EuCaptchaInterface $captcha) {}

    #[Route('/contact', name: 'contact', methods: ['POST'])]
    public function submit(Request $request): Response
    {
        $result = $this->captcha->validate(
            $request->request->getString('eu-captcha-response'),
            $request->getClientIp() ?? '',
        );

        if (!$result->success()) {
            return $this->json(['error' => 'CAPTCHA verification failed'], Response::HTTP_BAD_REQUEST);
        }

        // process the form...

        return $this->json(['status' => 'ok']);
    }
}

$request->getClientIp() respects Symfony's trusted proxy configuration, so the real visitor IP is forwarded correctly when running behind a load balancer or CDN. Pass the User-Agent as a third argument to validate() if you want to forward it to the API as well.