ervin11 / honeypot-bundle
HoneypotBundle is a very simple way to implement a honeypot system for your symfony contact forms to block spam coming from spambots.
Requires
- php: >=7.3
This package is auto-updated.
Last update: 2025-06-29 02:00:13 UTC
README
HoneypotBundle is a very simple way to implement a honeypot system for your symfony contact forms to block spam coming from spambots.
Install the package with :
composer require ervin11/honeypot-bundle
And... that's it! If you're not using Symfony Flex, you'll also need to enable the Ervin11\HoneypotBundle\HoneypotBundle in your AppKernel.php file.
Files
This bundle provides a :
- HoneypotTrait that provides an email field stored in database and a fakeEmail field only used for spam verification.
# Ervin11\HoneypotBundle\Traits;
trait HoneypotableTrait {
/**
* @ORM\Column(type="string", length=255)
*/
protected ?string $email;
protected string $fakeEmail;
// ...
}
- HoneypotType that you can use to add an
`email
and a hidden
fakeEmail
form field. The
fakeEmail
field has a callback validation that calls
manageSpam
` function in which spam verification is performed.
# Ervin11\HoneypotBundle\Types;
class HoneypotType extends AbstractType
{
public const CODE = 'SPAM';
// ...
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class,
[
'required' => true,
'constraints' => [
new Assert\NotNull(),
new Assert\Email(null, "The email '{{ value }}' is not a valid email.", "strict")
]
])
->add('fakeEmail', EmailType::class,
[
'label_attr' => [
'style' => 'font-size: 0;'
],
'attr' => [
'style' => 'height: 0; width: 0; margin: 0; padding: 0; text-decoration: none; border: none;'
],
'row_attr' => [
'style' => 'margin: 0 !important; height: 0; width: 0; color:transparent;'
],
'constraints' => new Assert\Callback([$this, 'manageSpam']),
'required' => false,
]);
}
public function manageSpam($data, ExecutionContextInterface $context): void
{
$form = $context->getRoot();
$fakeEmail = ($form->get("honeypot"))->get('fakeEmail')->getData();
/** @var Request $request */
$request = $this->requestStack->getCurrentRequest();
$ip = $request->getClientIp();
if ($fakeEmail) {
if ($this->logger) {
$this->logger->alert("Spam tried with $fakeEmail from $ip");
}
$context
->buildViolation("Spam tried with $fakeEmail from $ip")
->setCode(self::CODE)
->setParameters(['email' => $fakeEmail, 'ip' => $ip])
->addViolation();
}
}
// ...
}
Usage
First you have to add the `use HoneypotableTrait
` in your entity class containing the contact form data.
class Message
{
use HoneypotableTrait;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private int $id;
// ...
}
Then you will need to create a new migration and run it with :
php bin/console make:mig && php bin/console d:m:m -n
Once the migration is done, you can add the HoneypotType field in your contact form
# src/Form/MessageType
class MessageType extends AbstractType
{
// ...
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('firstname', TextType::class)
->add('lastname', TextType::class)
->add('honeypot', HoneypotType::class)
->add('save', SubmitType::class, ['label' => 'Send Message']);
}
// ...
}
Then you can get the honeypot fields in the twig template containing the contact form fields like this :
// ...
{% block body %}
{{ form_start(form) }}
// ...
{{ form_row(form.honeypot.email) }}
{{ form_row(form.honeypot.fakeEmail) }}
// ...
{{ form_end(form) }}
{% endblock %}
On form submit, if HoneypotType's `manageSpam
function detects a spam, it builds and adds a violation to the form's hidden
fakeEmail
field, so at this point
$form->isValid()
` will return false.
This violation contains an array of parameters that you can use if you need to.
[
'email' => 'honeypot@test.com',
'ip' => '127.0.0.1'
]
$errors = $form->getErrors(true);
$spamDetected = $errors->findByCodes(HoneypotType::CODE);
if (count($spamDetected) !== 0) {
$spamData = $spamDetected->current()->getMessageParameters();
}
Subscriber
When HoneypotType's manageSpam function detects a spam it also dispatches a `HoneypotSpamDetectedEvent
` that contains the email and ip of the spammer. You can listen to it by creating a subscriber.
<?php
namespace App\EventSubscriber;
use Ervin11\HoneypotBundle\Event\HoneypotSpamDetectedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class HoneypotSpamSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
HoneypotSpamDetectedEvent::NAME => 'onSpamDetected',
];
}
public function onSpamDetected(HoneypotSpamDetectedEvent $event)
{
// ... Do something
}
}
Logging
If you have a monolog default main channel in dev mode, it will log the email and the IP address in /var/log/dev.log