1 <?php 
declare(strict_types
=1); 
   4  * This file is part of the Rapsys PackBundle package. 
   6  * (c) Raphaël Gertz <symfony@rapsys.eu> 
   8  * For the full copyright and license information, please view the LICENSE 
   9  * file that was distributed with this source code. 
  12 namespace Rapsys\PackBundle\Form
; 
  14 use Rapsys\PackBundle\Util\ImageUtil
; 
  15 use Rapsys\PackBundle\Util\SluggerUtil
; 
  17 use Symfony\Component\Form\AbstractType
; 
  18 use Symfony\Component\Form\Extension\Core\Type\HiddenType
; 
  19 use Symfony\Component\Form\Extension\Core\Type\IntegerType
; 
  20 use Symfony\Component\Form\FormBuilderInterface
; 
  21 use Symfony\Component\Form\FormError
; 
  22 use Symfony\Component\Form\FormEvent
; 
  23 use Symfony\Component\Form\FormEvents
; 
  24 use Symfony\Component\OptionsResolver\OptionsResolver
; 
  25 use Symfony\Contracts\Translation\TranslatorInterface
; 
  28  * Captcha Type class definition 
  30  * @see https://symfony.com/doc/current/form/create_custom_field_type.html 
  32 class CaptchaType 
extends AbstractType 
{ 
  36          * @param ?ImageUtil $image The image instance 
  37          * @param ?SluggerUtil $slugger The slugger instance 
  38          * @param ?TranslatorInterface $translator The translator instance 
  40         public function __construct(protected ?ImageUtil 
$image = null, protected ?SluggerUtil 
$slugger = null, protected ?TranslatorInterface 
$translator = null) { 
  48         public function buildForm(FormBuilderInterface 
$builder, array $options): void { 
  49                 //With image, slugger and translator 
  50                 if (!empty($options['captcha']) && $this->image 
!== null && $this->slugger 
!== null && $this->translator 
!== null) { 
  52                         $captcha = $this->image
->getCaptcha((new \
DateTime('-1 year'))->getTimestamp()); 
  55                         $builder->add('_captcha_token', HiddenType
::class, ['data' => $captcha['token'], 'empty_data' => $captcha['token'], 'mapped' => false]); 
  58                         $builder->add('captcha', IntegerType
::class, ['label_attr' => ['class' => 'captcha'], 'label' => '<img src="'.htmlentities($captcha['src']).'" alt="'.htmlentities($captcha['equation']).'" />', 'label_html' => true, 'mapped' => false, 'translation_domain' => false]); 
  60                         //Add event listener on captcha 
  61                         $builder->addEventListener(FormEvents
::PRE_SUBMIT
, [$this, 'validateCaptcha']); 
  68         public function configureOptions(OptionsResolver 
$resolver): void { 
  69                 //Call parent configure options 
  70                 parent
::configureOptions($resolver); 
  73                 $resolver->setDefaults(['captcha' => false]); 
  75                 //Add extra captcha option 
  76                 $resolver->setAllowedTypes('captcha', 'boolean'); 
  82          * @param FormEvent $event The form event 
  84         public function validateCaptcha(FormEvent 
$event): void { 
  86                 $form = $event->getForm(); 
  89                 $data = $event->getData(); 
  92                 $token = $form->get('_captcha_token')->getConfig()->getData(); 
  95                 if (empty($data['captcha'])) { 
  96                         //Add error on captcha 
  97                         $form->addError(new FormError($this->translator
->trans('Captcha is empty'))); 
 100                         $data['_captcha_token'] = $token; 
 103                         $event->setData($data); 
 104                 //With invalid captcha 
 105                 } elseif ($this->slugger
->hash($data['captcha']) !== $data['_captcha_token']) { 
 106                         //Add error on captcha 
 107                         $form->addError(new FormError($this->translator
->trans('Captcha is invalid'))); 
 109                         //Reset captcha token 
 110                         $data['_captcha_token'] = $token; 
 113                         $event->setData($data);