vendor/symfony/form/FormConfigBuilder.php line 116

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Form;
  11. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  12. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  13. use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
  14. use Symfony\Component\Form\Exception\BadMethodCallException;
  15. use Symfony\Component\Form\Exception\InvalidArgumentException;
  16. use Symfony\Component\Form\Exception\UnexpectedTypeException;
  17. use Symfony\Component\PropertyAccess\PropertyPath;
  18. use Symfony\Component\PropertyAccess\PropertyPathInterface;
  19. /**
  20.  * A basic form configuration.
  21.  *
  22.  * @author Bernhard Schussek <bschussek@gmail.com>
  23.  */
  24. class FormConfigBuilder implements FormConfigBuilderInterface
  25. {
  26.     /**
  27.      * Caches a globally unique {@link NativeRequestHandler} instance.
  28.      *
  29.      * @var NativeRequestHandler
  30.      */
  31.     private static $nativeRequestHandler;
  32.     protected $locked false;
  33.     private $dispatcher;
  34.     private $name;
  35.     /**
  36.      * @var PropertyPathInterface|string|null
  37.      */
  38.     private $propertyPath;
  39.     private $mapped true;
  40.     private $byReference true;
  41.     private $inheritData false;
  42.     private $compound false;
  43.     /**
  44.      * @var ResolvedFormTypeInterface
  45.      */
  46.     private $type;
  47.     private $viewTransformers = [];
  48.     private $modelTransformers = [];
  49.     /**
  50.      * @var DataMapperInterface|null
  51.      */
  52.     private $dataMapper;
  53.     private $required true;
  54.     private $disabled false;
  55.     private $errorBubbling false;
  56.     /**
  57.      * @var mixed
  58.      */
  59.     private $emptyData;
  60.     private $attributes = [];
  61.     /**
  62.      * @var mixed
  63.      */
  64.     private $data;
  65.     /**
  66.      * @var string|null
  67.      */
  68.     private $dataClass;
  69.     private $dataLocked false;
  70.     /**
  71.      * @var FormFactoryInterface|null
  72.      */
  73.     private $formFactory;
  74.     /**
  75.      * @var string|null
  76.      */
  77.     private $action;
  78.     private $method 'POST';
  79.     /**
  80.      * @var RequestHandlerInterface|null
  81.      */
  82.     private $requestHandler;
  83.     private $autoInitialize false;
  84.     private $options;
  85.     /**
  86.      * Creates an empty form configuration.
  87.      *
  88.      * @param string|null $name      The form name
  89.      * @param string|null $dataClass The class of the form's data
  90.      *
  91.      * @throws InvalidArgumentException if the data class is not a valid class or if
  92.      *                                  the name contains invalid characters
  93.      */
  94.     public function __construct(?string $name, ?string $dataClassEventDispatcherInterface $dispatcher, array $options = [])
  95.     {
  96.         self::validateName($name);
  97.         if (null !== $dataClass && !class_exists($dataClass) && !interface_exists($dataClassfalse)) {
  98.             throw new InvalidArgumentException(sprintf('Class "%s" not found. Is the "data_class" form option set correctly?'$dataClass));
  99.         }
  100.         $this->name = (string) $name;
  101.         $this->dataClass $dataClass;
  102.         $this->dispatcher $dispatcher;
  103.         $this->options $options;
  104.     }
  105.     /**
  106.      * {@inheritdoc}
  107.      */
  108.     public function addEventListener($eventName$listener$priority 0)
  109.     {
  110.         if ($this->locked) {
  111.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  112.         }
  113.         $this->dispatcher->addListener($eventName$listener$priority);
  114.         return $this;
  115.     }
  116.     /**
  117.      * {@inheritdoc}
  118.      */
  119.     public function addEventSubscriber(EventSubscriberInterface $subscriber)
  120.     {
  121.         if ($this->locked) {
  122.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  123.         }
  124.         $this->dispatcher->addSubscriber($subscriber);
  125.         return $this;
  126.     }
  127.     /**
  128.      * {@inheritdoc}
  129.      */
  130.     public function addViewTransformer(DataTransformerInterface $viewTransformer$forcePrepend false)
  131.     {
  132.         if ($this->locked) {
  133.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  134.         }
  135.         if ($forcePrepend) {
  136.             array_unshift($this->viewTransformers$viewTransformer);
  137.         } else {
  138.             $this->viewTransformers[] = $viewTransformer;
  139.         }
  140.         return $this;
  141.     }
  142.     /**
  143.      * {@inheritdoc}
  144.      */
  145.     public function resetViewTransformers()
  146.     {
  147.         if ($this->locked) {
  148.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  149.         }
  150.         $this->viewTransformers = [];
  151.         return $this;
  152.     }
  153.     /**
  154.      * {@inheritdoc}
  155.      */
  156.     public function addModelTransformer(DataTransformerInterface $modelTransformer$forceAppend false)
  157.     {
  158.         if ($this->locked) {
  159.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  160.         }
  161.         if ($forceAppend) {
  162.             $this->modelTransformers[] = $modelTransformer;
  163.         } else {
  164.             array_unshift($this->modelTransformers$modelTransformer);
  165.         }
  166.         return $this;
  167.     }
  168.     /**
  169.      * {@inheritdoc}
  170.      */
  171.     public function resetModelTransformers()
  172.     {
  173.         if ($this->locked) {
  174.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  175.         }
  176.         $this->modelTransformers = [];
  177.         return $this;
  178.     }
  179.     /**
  180.      * {@inheritdoc}
  181.      */
  182.     public function getEventDispatcher()
  183.     {
  184.         if ($this->locked && !$this->dispatcher instanceof ImmutableEventDispatcher) {
  185.             $this->dispatcher = new ImmutableEventDispatcher($this->dispatcher);
  186.         }
  187.         return $this->dispatcher;
  188.     }
  189.     /**
  190.      * {@inheritdoc}
  191.      */
  192.     public function getName()
  193.     {
  194.         return $this->name;
  195.     }
  196.     /**
  197.      * {@inheritdoc}
  198.      */
  199.     public function getPropertyPath()
  200.     {
  201.         return $this->propertyPath;
  202.     }
  203.     /**
  204.      * {@inheritdoc}
  205.      */
  206.     public function getMapped()
  207.     {
  208.         return $this->mapped;
  209.     }
  210.     /**
  211.      * {@inheritdoc}
  212.      */
  213.     public function getByReference()
  214.     {
  215.         return $this->byReference;
  216.     }
  217.     /**
  218.      * {@inheritdoc}
  219.      */
  220.     public function getInheritData()
  221.     {
  222.         return $this->inheritData;
  223.     }
  224.     /**
  225.      * {@inheritdoc}
  226.      */
  227.     public function getCompound()
  228.     {
  229.         return $this->compound;
  230.     }
  231.     /**
  232.      * {@inheritdoc}
  233.      */
  234.     public function getType()
  235.     {
  236.         return $this->type;
  237.     }
  238.     /**
  239.      * {@inheritdoc}
  240.      */
  241.     public function getViewTransformers()
  242.     {
  243.         return $this->viewTransformers;
  244.     }
  245.     /**
  246.      * {@inheritdoc}
  247.      */
  248.     public function getModelTransformers()
  249.     {
  250.         return $this->modelTransformers;
  251.     }
  252.     /**
  253.      * {@inheritdoc}
  254.      */
  255.     public function getDataMapper()
  256.     {
  257.         return $this->dataMapper;
  258.     }
  259.     /**
  260.      * {@inheritdoc}
  261.      */
  262.     public function getRequired()
  263.     {
  264.         return $this->required;
  265.     }
  266.     /**
  267.      * {@inheritdoc}
  268.      */
  269.     public function getDisabled()
  270.     {
  271.         return $this->disabled;
  272.     }
  273.     /**
  274.      * {@inheritdoc}
  275.      */
  276.     public function getErrorBubbling()
  277.     {
  278.         return $this->errorBubbling;
  279.     }
  280.     /**
  281.      * {@inheritdoc}
  282.      */
  283.     public function getEmptyData()
  284.     {
  285.         return $this->emptyData;
  286.     }
  287.     /**
  288.      * {@inheritdoc}
  289.      */
  290.     public function getAttributes()
  291.     {
  292.         return $this->attributes;
  293.     }
  294.     /**
  295.      * {@inheritdoc}
  296.      */
  297.     public function hasAttribute($name)
  298.     {
  299.         return \array_key_exists($name$this->attributes);
  300.     }
  301.     /**
  302.      * {@inheritdoc}
  303.      */
  304.     public function getAttribute($name$default null)
  305.     {
  306.         return \array_key_exists($name$this->attributes) ? $this->attributes[$name] : $default;
  307.     }
  308.     /**
  309.      * {@inheritdoc}
  310.      */
  311.     public function getData()
  312.     {
  313.         return $this->data;
  314.     }
  315.     /**
  316.      * {@inheritdoc}
  317.      */
  318.     public function getDataClass()
  319.     {
  320.         return $this->dataClass;
  321.     }
  322.     /**
  323.      * {@inheritdoc}
  324.      */
  325.     public function getDataLocked()
  326.     {
  327.         return $this->dataLocked;
  328.     }
  329.     /**
  330.      * {@inheritdoc}
  331.      */
  332.     public function getFormFactory()
  333.     {
  334.         return $this->formFactory;
  335.     }
  336.     /**
  337.      * {@inheritdoc}
  338.      */
  339.     public function getAction()
  340.     {
  341.         return $this->action;
  342.     }
  343.     /**
  344.      * {@inheritdoc}
  345.      */
  346.     public function getMethod()
  347.     {
  348.         return $this->method;
  349.     }
  350.     /**
  351.      * {@inheritdoc}
  352.      */
  353.     public function getRequestHandler()
  354.     {
  355.         if (null === $this->requestHandler) {
  356.             if (null === self::$nativeRequestHandler) {
  357.                 self::$nativeRequestHandler = new NativeRequestHandler();
  358.             }
  359.             $this->requestHandler self::$nativeRequestHandler;
  360.         }
  361.         return $this->requestHandler;
  362.     }
  363.     /**
  364.      * {@inheritdoc}
  365.      */
  366.     public function getAutoInitialize()
  367.     {
  368.         return $this->autoInitialize;
  369.     }
  370.     /**
  371.      * {@inheritdoc}
  372.      */
  373.     public function getOptions()
  374.     {
  375.         return $this->options;
  376.     }
  377.     /**
  378.      * {@inheritdoc}
  379.      */
  380.     public function hasOption($name)
  381.     {
  382.         return \array_key_exists($name$this->options);
  383.     }
  384.     /**
  385.      * {@inheritdoc}
  386.      */
  387.     public function getOption($name$default null)
  388.     {
  389.         return \array_key_exists($name$this->options) ? $this->options[$name] : $default;
  390.     }
  391.     /**
  392.      * {@inheritdoc}
  393.      */
  394.     public function setAttribute($name$value)
  395.     {
  396.         if ($this->locked) {
  397.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  398.         }
  399.         $this->attributes[$name] = $value;
  400.         return $this;
  401.     }
  402.     /**
  403.      * {@inheritdoc}
  404.      */
  405.     public function setAttributes(array $attributes)
  406.     {
  407.         if ($this->locked) {
  408.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  409.         }
  410.         $this->attributes $attributes;
  411.         return $this;
  412.     }
  413.     /**
  414.      * {@inheritdoc}
  415.      */
  416.     public function setDataMapper(DataMapperInterface $dataMapper null)
  417.     {
  418.         if ($this->locked) {
  419.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  420.         }
  421.         $this->dataMapper $dataMapper;
  422.         return $this;
  423.     }
  424.     /**
  425.      * {@inheritdoc}
  426.      */
  427.     public function setDisabled($disabled)
  428.     {
  429.         if ($this->locked) {
  430.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  431.         }
  432.         $this->disabled = (bool) $disabled;
  433.         return $this;
  434.     }
  435.     /**
  436.      * {@inheritdoc}
  437.      */
  438.     public function setEmptyData($emptyData)
  439.     {
  440.         if ($this->locked) {
  441.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  442.         }
  443.         $this->emptyData $emptyData;
  444.         return $this;
  445.     }
  446.     /**
  447.      * {@inheritdoc}
  448.      */
  449.     public function setErrorBubbling($errorBubbling)
  450.     {
  451.         if ($this->locked) {
  452.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  453.         }
  454.         $this->errorBubbling = (bool) $errorBubbling;
  455.         return $this;
  456.     }
  457.     /**
  458.      * {@inheritdoc}
  459.      */
  460.     public function setRequired($required)
  461.     {
  462.         if ($this->locked) {
  463.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  464.         }
  465.         $this->required = (bool) $required;
  466.         return $this;
  467.     }
  468.     /**
  469.      * {@inheritdoc}
  470.      */
  471.     public function setPropertyPath($propertyPath)
  472.     {
  473.         if ($this->locked) {
  474.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  475.         }
  476.         if (null !== $propertyPath && !$propertyPath instanceof PropertyPathInterface) {
  477.             $propertyPath = new PropertyPath($propertyPath);
  478.         }
  479.         $this->propertyPath $propertyPath;
  480.         return $this;
  481.     }
  482.     /**
  483.      * {@inheritdoc}
  484.      */
  485.     public function setMapped($mapped)
  486.     {
  487.         if ($this->locked) {
  488.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  489.         }
  490.         $this->mapped = (bool) $mapped;
  491.         return $this;
  492.     }
  493.     /**
  494.      * {@inheritdoc}
  495.      */
  496.     public function setByReference($byReference)
  497.     {
  498.         if ($this->locked) {
  499.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  500.         }
  501.         $this->byReference = (bool) $byReference;
  502.         return $this;
  503.     }
  504.     /**
  505.      * {@inheritdoc}
  506.      */
  507.     public function setInheritData($inheritData)
  508.     {
  509.         if ($this->locked) {
  510.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  511.         }
  512.         $this->inheritData = (bool) $inheritData;
  513.         return $this;
  514.     }
  515.     /**
  516.      * {@inheritdoc}
  517.      */
  518.     public function setCompound($compound)
  519.     {
  520.         if ($this->locked) {
  521.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  522.         }
  523.         $this->compound = (bool) $compound;
  524.         return $this;
  525.     }
  526.     /**
  527.      * {@inheritdoc}
  528.      */
  529.     public function setType(ResolvedFormTypeInterface $type)
  530.     {
  531.         if ($this->locked) {
  532.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  533.         }
  534.         $this->type $type;
  535.         return $this;
  536.     }
  537.     /**
  538.      * {@inheritdoc}
  539.      */
  540.     public function setData($data)
  541.     {
  542.         if ($this->locked) {
  543.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  544.         }
  545.         $this->data $data;
  546.         return $this;
  547.     }
  548.     /**
  549.      * {@inheritdoc}
  550.      */
  551.     public function setDataLocked($locked)
  552.     {
  553.         if ($this->locked) {
  554.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  555.         }
  556.         $this->dataLocked = (bool) $locked;
  557.         return $this;
  558.     }
  559.     /**
  560.      * {@inheritdoc}
  561.      */
  562.     public function setFormFactory(FormFactoryInterface $formFactory)
  563.     {
  564.         if ($this->locked) {
  565.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  566.         }
  567.         $this->formFactory $formFactory;
  568.         return $this;
  569.     }
  570.     /**
  571.      * {@inheritdoc}
  572.      */
  573.     public function setAction($action)
  574.     {
  575.         if ($this->locked) {
  576.             throw new BadMethodCallException('The config builder cannot be modified anymore.');
  577.         }
  578.         $this->action = (string) $action;
  579.         return $this;
  580.     }
  581.     /**
  582.      * {@inheritdoc}
  583.      */
  584.     public function setMethod($method)
  585.     {
  586.         if ($this->locked) {
  587.             throw new BadMethodCallException('The config builder cannot be modified anymore.');
  588.         }
  589.         $this->method strtoupper($method);
  590.         return $this;
  591.     }
  592.     /**
  593.      * {@inheritdoc}
  594.      */
  595.     public function setRequestHandler(RequestHandlerInterface $requestHandler)
  596.     {
  597.         if ($this->locked) {
  598.             throw new BadMethodCallException('The config builder cannot be modified anymore.');
  599.         }
  600.         $this->requestHandler $requestHandler;
  601.         return $this;
  602.     }
  603.     /**
  604.      * {@inheritdoc}
  605.      */
  606.     public function setAutoInitialize($initialize)
  607.     {
  608.         if ($this->locked) {
  609.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  610.         }
  611.         $this->autoInitialize = (bool) $initialize;
  612.         return $this;
  613.     }
  614.     /**
  615.      * {@inheritdoc}
  616.      */
  617.     public function getFormConfig()
  618.     {
  619.         if ($this->locked) {
  620.             throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
  621.         }
  622.         // This method should be idempotent, so clone the builder
  623.         $config = clone $this;
  624.         $config->locked true;
  625.         return $config;
  626.     }
  627.     /**
  628.      * Validates whether the given variable is a valid form name.
  629.      *
  630.      * @param string|null $name The tested form name
  631.      *
  632.      * @throws UnexpectedTypeException  if the name is not a string or an integer
  633.      * @throws InvalidArgumentException if the name contains invalid characters
  634.      *
  635.      * @internal since Symfony 4.4
  636.      */
  637.     public static function validateName($name)
  638.     {
  639.         if (null !== $name && !\is_string($name) && !\is_int($name)) {
  640.             throw new UnexpectedTypeException($name'string or null');
  641.         }
  642.         if (!self::isValidName($name)) {
  643.             throw new InvalidArgumentException(sprintf('The name "%s" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").'$name));
  644.         }
  645.     }
  646.     /**
  647.      * Returns whether the given variable contains a valid form name.
  648.      *
  649.      * A name is accepted if it
  650.      *
  651.      *   * is empty
  652.      *   * starts with a letter, digit or underscore
  653.      *   * contains only letters, digits, numbers, underscores ("_"),
  654.      *     hyphens ("-") and colons (":")
  655.      *
  656.      * @final since Symfony 4.4
  657.      */
  658.     public static function isValidName(?string $name): bool
  659.     {
  660.         return '' === $name || null === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D'$name);
  661.     }
  662. }