<?php 
 
/* 
 * This file is part of the Symfony package. 
 * 
 * (c) Fabien Potencier <fabien@symfony.com> 
 * 
 * For the full copyright and license information, please view the LICENSE 
 * file that was distributed with this source code. 
 */ 
 
namespace Symfony\Component\Form\Extension\Csrf\Type; 
 
use Symfony\Component\Form\AbstractTypeExtension; 
use Symfony\Component\Form\Extension\Core\Type\FormType; 
use Symfony\Component\Form\Extension\Core\Type\HiddenType; 
use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; 
use Symfony\Component\Form\FormBuilderInterface; 
use Symfony\Component\Form\FormInterface; 
use Symfony\Component\Form\FormView; 
use Symfony\Component\Form\Util\ServerParams; 
use Symfony\Component\OptionsResolver\OptionsResolver; 
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; 
use Symfony\Contracts\Translation\TranslatorInterface; 
 
/** 
 * @author Bernhard Schussek <bschussek@gmail.com> 
 */ 
class FormTypeCsrfExtension extends AbstractTypeExtension 
{ 
    private $defaultTokenManager; 
    private $defaultEnabled; 
    private $defaultFieldName; 
    private $translator; 
    private $translationDomain; 
    private $serverParams; 
 
    public function __construct(CsrfTokenManagerInterface $defaultTokenManager, bool $defaultEnabled = true, string $defaultFieldName = '_token', TranslatorInterface $translator = null, string $translationDomain = null, ServerParams $serverParams = null) 
    { 
        $this->defaultTokenManager = $defaultTokenManager; 
        $this->defaultEnabled = $defaultEnabled; 
        $this->defaultFieldName = $defaultFieldName; 
        $this->translator = $translator; 
        $this->translationDomain = $translationDomain; 
        $this->serverParams = $serverParams; 
    } 
 
    /** 
     * Adds a CSRF field to the form when the CSRF protection is enabled. 
     */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
        if (!$options['csrf_protection']) { 
            return; 
        } 
 
        $builder 
            ->addEventSubscriber(new CsrfValidationListener( 
                $options['csrf_field_name'], 
                $options['csrf_token_manager'], 
                $options['csrf_token_id'] ?: ($builder->getName() ?: \get_class($builder->getType()->getInnerType())), 
                $options['csrf_message'], 
                $this->translator, 
                $this->translationDomain, 
                $this->serverParams 
            )) 
        ; 
    } 
 
    /** 
     * Adds a CSRF field to the root form view. 
     */ 
    public function finishView(FormView $view, FormInterface $form, array $options) 
    { 
        if ($options['csrf_protection'] && !$view->parent && $options['compound']) { 
            $factory = $form->getConfig()->getFormFactory(); 
            $tokenId = $options['csrf_token_id'] ?: ($form->getName() ?: \get_class($form->getConfig()->getType()->getInnerType())); 
            $data = (string) $options['csrf_token_manager']->getToken($tokenId); 
 
            $csrfForm = $factory->createNamed($options['csrf_field_name'], HiddenType::class, $data, [ 
                'block_prefix' => 'csrf_token', 
                'mapped' => false, 
            ]); 
 
            $view->children[$options['csrf_field_name']] = $csrfForm->createView($view); 
        } 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function configureOptions(OptionsResolver $resolver) 
    { 
        $resolver->setDefaults([ 
            'csrf_protection' => $this->defaultEnabled, 
            'csrf_field_name' => $this->defaultFieldName, 
            'csrf_message' => 'The CSRF token is invalid. Please try to resubmit the form.', 
            'csrf_token_manager' => $this->defaultTokenManager, 
            'csrf_token_id' => null, 
        ]); 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public static function getExtendedTypes(): iterable 
    { 
        return [FormType::class]; 
    } 
}