Skip to content

Commit

Permalink
[Form] Fixed FormValidator::findClickedButton() not to be called expo…
Browse files Browse the repository at this point in the history
…nentially
  • Loading branch information
webmozart committed Sep 10, 2013
1 parent a672bba commit b65a515
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
Expand Up @@ -22,6 +22,11 @@
*/
class FormValidator extends ConstraintValidator
{
/**
* @var \SplObjectStorage
*/
private static $clickedButtons;

/**
* @var ServerParams
*/
Expand All @@ -47,6 +52,16 @@ public function validate($form, Constraint $constraint)
return;
}

if (null === static::$clickedButtons) {
static::$clickedButtons = new \SplObjectStorage();
}

// If the form was previously validated, remove it from the cache in
// case the clicked button has changed
if (static::$clickedButtons->contains($form)) {
static::$clickedButtons->detach($form);
}

/* @var FormInterface $form */
$config = $form->getConfig();

Expand Down Expand Up @@ -172,7 +187,17 @@ private static function allowDataWalking(FormInterface $form)
*/
private static function getValidationGroups(FormInterface $form)
{
$button = self::findClickedButton($form->getRoot());
$root = $form->getRoot();

// Determine the clicked button of the complete form tree
if (!static::$clickedButtons->contains($root)) {
// Only call findClickedButton() once to prevent an exponential
// runtime
// https://github.com/symfony/symfony/issues/8317
static::$clickedButtons->attach($root, self::findClickedButton($root));
}

$button = static::$clickedButtons->offsetGet($root);

if (null !== $button) {
$groups = $button->getConfig()->getOption('validation_groups');
Expand Down
@@ -0,0 +1,54 @@
<?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\Tests\Extension\Validator\Constraints;

use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Test\FormPerformanceTestCase;
use Symfony\Component\Validator\Validation;

/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormValidatorPerformanceTest extends FormPerformanceTestCase
{
protected function getExtensions()
{
return array(
new ValidatorExtension(Validation::createValidator()),
);
}

/**
* findClickedButton() used to have an exponential number of calls
*
* @group benchmark
*/
public function testValidationPerformance()
{
$this->setMaxRunningTime(1);

$builder = $this->factory->createBuilder('form');

for ($i = 0; $i < 100; ++$i) {
$builder->add($i, 'form');

$builder->get($i)
->add('a')
->add('b')
->add('c');
}

$form = $builder->getForm();

$form->submit(null);
}
}

0 comments on commit b65a515

Please sign in to comment.