vendor/symfony/symfony/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php line 242

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\Extension\DataCollector;
  11. use Symfony\Component\Form\FormInterface;
  12. use Symfony\Component\Form\FormView;
  13. use Symfony\Component\HttpFoundation\Request;
  14. use Symfony\Component\HttpFoundation\Response;
  15. use Symfony\Component\HttpKernel\DataCollector\DataCollector;
  16. use Symfony\Component\Validator\ConstraintViolationInterface;
  17. use Symfony\Component\VarDumper\Caster\Caster;
  18. use Symfony\Component\VarDumper\Caster\ClassStub;
  19. use Symfony\Component\VarDumper\Caster\StubCaster;
  20. use Symfony\Component\VarDumper\Cloner\Stub;
  21. /**
  22.  * Data collector for {@link FormInterface} instances.
  23.  *
  24.  * @author Robert Schönthal <robert.schoenthal@gmail.com>
  25.  * @author Bernhard Schussek <bschussek@gmail.com>
  26.  */
  27. class FormDataCollector extends DataCollector implements FormDataCollectorInterface
  28. {
  29.     private $dataExtractor;
  30.     /**
  31.      * Stores the collected data per {@link FormInterface} instance.
  32.      *
  33.      * Uses the hashes of the forms as keys. This is preferable over using
  34.      * {@link \SplObjectStorage}, because in this way no references are kept
  35.      * to the {@link FormInterface} instances.
  36.      *
  37.      * @var array
  38.      */
  39.     private $dataByForm;
  40.     /**
  41.      * Stores the collected data per {@link FormView} instance.
  42.      *
  43.      * Uses the hashes of the views as keys. This is preferable over using
  44.      * {@link \SplObjectStorage}, because in this way no references are kept
  45.      * to the {@link FormView} instances.
  46.      *
  47.      * @var array
  48.      */
  49.     private $dataByView;
  50.     /**
  51.      * Connects {@link FormView} with {@link FormInterface} instances.
  52.      *
  53.      * Uses the hashes of the views as keys and the hashes of the forms as
  54.      * values. This is preferable over storing the objects directly, because
  55.      * this way they can safely be discarded by the GC.
  56.      *
  57.      * @var array
  58.      */
  59.     private $formsByView;
  60.     private $hasVarDumper;
  61.     public function __construct(FormDataExtractorInterface $dataExtractor)
  62.     {
  63.         $this->dataExtractor $dataExtractor;
  64.         $this->hasVarDumper class_exists(ClassStub::class);
  65.         $this->reset();
  66.     }
  67.     /**
  68.      * Does nothing. The data is collected during the form event listeners.
  69.      */
  70.     public function collect(Request $requestResponse $response, \Exception $exception null)
  71.     {
  72.     }
  73.     public function reset()
  74.     {
  75.         $this->data = [
  76.             'forms' => [],
  77.             'forms_by_hash' => [],
  78.             'nb_errors' => 0,
  79.         ];
  80.     }
  81.     /**
  82.      * {@inheritdoc}
  83.      */
  84.     public function associateFormWithView(FormInterface $formFormView $view)
  85.     {
  86.         $this->formsByView[spl_object_hash($view)] = spl_object_hash($form);
  87.     }
  88.     /**
  89.      * {@inheritdoc}
  90.      */
  91.     public function collectConfiguration(FormInterface $form)
  92.     {
  93.         $hash spl_object_hash($form);
  94.         if (!isset($this->dataByForm[$hash])) {
  95.             $this->dataByForm[$hash] = [];
  96.         }
  97.         $this->dataByForm[$hash] = array_replace(
  98.             $this->dataByForm[$hash],
  99.             $this->dataExtractor->extractConfiguration($form)
  100.         );
  101.         foreach ($form as $child) {
  102.             $this->collectConfiguration($child);
  103.         }
  104.     }
  105.     /**
  106.      * {@inheritdoc}
  107.      */
  108.     public function collectDefaultData(FormInterface $form)
  109.     {
  110.         $hash spl_object_hash($form);
  111.         if (!isset($this->dataByForm[$hash])) {
  112.             // field was created by form event
  113.             $this->collectConfiguration($form);
  114.         }
  115.         $this->dataByForm[$hash] = array_replace(
  116.             $this->dataByForm[$hash],
  117.             $this->dataExtractor->extractDefaultData($form)
  118.         );
  119.         foreach ($form as $child) {
  120.             $this->collectDefaultData($child);
  121.         }
  122.     }
  123.     /**
  124.      * {@inheritdoc}
  125.      */
  126.     public function collectSubmittedData(FormInterface $form)
  127.     {
  128.         $hash spl_object_hash($form);
  129.         if (!isset($this->dataByForm[$hash])) {
  130.             // field was created by form event
  131.             $this->collectConfiguration($form);
  132.             $this->collectDefaultData($form);
  133.         }
  134.         $this->dataByForm[$hash] = array_replace(
  135.             $this->dataByForm[$hash],
  136.             $this->dataExtractor->extractSubmittedData($form)
  137.         );
  138.         // Count errors
  139.         if (isset($this->dataByForm[$hash]['errors'])) {
  140.             $this->data['nb_errors'] += \count($this->dataByForm[$hash]['errors']);
  141.         }
  142.         foreach ($form as $child) {
  143.             $this->collectSubmittedData($child);
  144.             // Expand current form if there are children with errors
  145.             if (empty($this->dataByForm[$hash]['has_children_error'])) {
  146.                 $childData $this->dataByForm[spl_object_hash($child)];
  147.                 $this->dataByForm[$hash]['has_children_error'] = !empty($childData['has_children_error']) || !empty($childData['errors']);
  148.             }
  149.         }
  150.     }
  151.     /**
  152.      * {@inheritdoc}
  153.      */
  154.     public function collectViewVariables(FormView $view)
  155.     {
  156.         $hash spl_object_hash($view);
  157.         if (!isset($this->dataByView[$hash])) {
  158.             $this->dataByView[$hash] = [];
  159.         }
  160.         $this->dataByView[$hash] = array_replace(
  161.             $this->dataByView[$hash],
  162.             $this->dataExtractor->extractViewVariables($view)
  163.         );
  164.         foreach ($view->children as $child) {
  165.             $this->collectViewVariables($child);
  166.         }
  167.     }
  168.     /**
  169.      * {@inheritdoc}
  170.      */
  171.     public function buildPreliminaryFormTree(FormInterface $form)
  172.     {
  173.         $this->data['forms'][$form->getName()] = &$this->recursiveBuildPreliminaryFormTree($form$this->data['forms_by_hash']);
  174.     }
  175.     /**
  176.      * {@inheritdoc}
  177.      */
  178.     public function buildFinalFormTree(FormInterface $formFormView $view)
  179.     {
  180.         $this->data['forms'][$form->getName()] = &$this->recursiveBuildFinalFormTree($form$view$this->data['forms_by_hash']);
  181.     }
  182.     /**
  183.      * {@inheritdoc}
  184.      */
  185.     public function getName()
  186.     {
  187.         return 'form';
  188.     }
  189.     /**
  190.      * {@inheritdoc}
  191.      */
  192.     public function getData()
  193.     {
  194.         return $this->data;
  195.     }
  196.     public function serialize()
  197.     {
  198.         if ($this->hasVarDumper) {
  199.             foreach ($this->data['forms_by_hash'] as &$form) {
  200.                 if (isset($form['type_class']) && !$form['type_class'] instanceof ClassStub) {
  201.                     $form['type_class'] = new ClassStub($form['type_class']);
  202.                 }
  203.             }
  204.         }
  205.         return serialize($this->cloneVar($this->data));
  206.     }
  207.     /**
  208.      * {@inheritdoc}
  209.      */
  210.     protected function getCasters()
  211.     {
  212.         return parent::getCasters() + [
  213.             \Exception::class => function (\Exception $e, array $aStub $s) {
  214.                 foreach (["\0Exception\0previous""\0Exception\0trace"] as $k) {
  215.                     if (isset($a[$k])) {
  216.                         unset($a[$k]);
  217.                         ++$s->cut;
  218.                     }
  219.                 }
  220.                 return $a;
  221.             },
  222.             FormInterface::class => function (FormInterface $f, array $a) {
  223.                 return [
  224.                     Caster::PREFIX_VIRTUAL.'name' => $f->getName(),
  225.                     Caster::PREFIX_VIRTUAL.'type_class' => new ClassStub(\get_class($f->getConfig()->getType()->getInnerType())),
  226.                 ];
  227.             },
  228.             FormView::class => [StubCaster::class, 'cutInternals'],
  229.             ConstraintViolationInterface::class => function (ConstraintViolationInterface $v, array $a) {
  230.                 return [
  231.                     Caster::PREFIX_VIRTUAL.'root' => $v->getRoot(),
  232.                     Caster::PREFIX_VIRTUAL.'path' => $v->getPropertyPath(),
  233.                     Caster::PREFIX_VIRTUAL.'value' => $v->getInvalidValue(),
  234.                 ];
  235.             },
  236.         ];
  237.     }
  238.     private function &recursiveBuildPreliminaryFormTree(FormInterface $form, array &$outputByHash)
  239.     {
  240.         $hash spl_object_hash($form);
  241.         $output = &$outputByHash[$hash];
  242.         $output = isset($this->dataByForm[$hash])
  243.             ? $this->dataByForm[$hash]
  244.             : [];
  245.         $output['children'] = [];
  246.         foreach ($form as $name => $child) {
  247.             $output['children'][$name] = &$this->recursiveBuildPreliminaryFormTree($child$outputByHash);
  248.         }
  249.         return $output;
  250.     }
  251.     private function &recursiveBuildFinalFormTree(FormInterface $form nullFormView $view, array &$outputByHash)
  252.     {
  253.         $viewHash spl_object_hash($view);
  254.         $formHash null;
  255.         if (null !== $form) {
  256.             $formHash spl_object_hash($form);
  257.         } elseif (isset($this->formsByView[$viewHash])) {
  258.             // The FormInterface instance of the CSRF token is never contained in
  259.             // the FormInterface tree of the form, so we need to get the
  260.             // corresponding FormInterface instance for its view in a different way
  261.             $formHash $this->formsByView[$viewHash];
  262.         }
  263.         if (null !== $formHash) {
  264.             $output = &$outputByHash[$formHash];
  265.         }
  266.         $output = isset($this->dataByView[$viewHash])
  267.             ? $this->dataByView[$viewHash]
  268.             : [];
  269.         if (null !== $formHash) {
  270.             $output array_replace(
  271.                 $output,
  272.                 isset($this->dataByForm[$formHash])
  273.                     ? $this->dataByForm[$formHash]
  274.                     : []
  275.             );
  276.         }
  277.         $output['children'] = [];
  278.         foreach ($view->children as $name => $childView) {
  279.             // The CSRF token, for example, is never added to the form tree.
  280.             // It is only present in the view.
  281.             $childForm null !== $form && $form->has($name)
  282.                 ? $form->get($name)
  283.                 : null;
  284.             $output['children'][$name] = &$this->recursiveBuildFinalFormTree($childForm$childView$outputByHash);
  285.         }
  286.         return $output;
  287.     }
  288. }