[Form] Improved ChoiceType performance by caching ChoiceList objects

This commit is contained in:
Bernhard Schussek 2012-07-12 12:34:07 +02:00
parent 3b400aef78
commit 8298d8c260
4 changed files with 169 additions and 6 deletions

View File

@ -31,6 +31,12 @@ use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ChoiceType extends AbstractType
{
/**
* Caches created choice lists.
* @var array
*/
private $choiceListCache = array();
/**
* {@inheritdoc}
*/
@ -123,12 +129,20 @@ class ChoiceType extends AbstractType
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$choiceList = function (Options $options) {
return new SimpleChoiceList(
// Harden against NULL values (like in EntityType and ModelType)
null !== $options['choices'] ? $options['choices'] : array(),
$options['preferred_choices']
);
$choiceListCache =& $this->choiceListCache;
$choiceList = function (Options $options) use (&$choiceListCache) {
// Harden against NULL values (like in EntityType and ModelType)
$choices = null !== $options['choices'] ? $options['choices'] : array();
// Reuse existing choice lists in order to increase performance
$hash = md5(json_encode(array($choices, $options['preferred_choices'])));
if (!isset($choiceListCache[$hash])) {
$choiceListCache[$hash] = new SimpleChoiceList($choices, $options['preferred_choices']);
}
return $choiceListCache[$hash];
};
$emptyData = function (Options $options) {

View File

@ -0,0 +1,36 @@
<?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\Core\Type;
use Symfony\Component\Form\Tests\FormPerformanceTestCase;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ChoiceTypePerformanceTest extends FormPerformanceTestCase
{
/**
* This test case is realistic in collection forms where each
* row contains the same choice field.
*/
public function testSameChoiceFieldCreatedMultipleTimes()
{
$this->setMaxRunningTime(1);
$choices = range(1, 300);
for ($i = 0; $i < 100; ++$i) {
$this->factory->create('choice', rand(1, 400), array(
'choices' => $choices,
));
}
}
}

View File

@ -0,0 +1,42 @@
<?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;
use Symfony\Component\Form\FormFactory;
use Symfony\Component\Form\Extension\Core\CoreExtension;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormIntegrationTestCase extends \PHPUnit_Framework_TestCase
{
/**
* @var \Symfony\Component\Form\FormFactoryInterface
*/
protected $factory;
protected function setUp()
{
if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) {
$this->markTestSkipped('The "EventDispatcher" component is not available');
}
$this->factory = new FormFactory($this->getExtensions());
}
protected function getExtensions()
{
return array(
new CoreExtension(),
);
}
}

View File

@ -0,0 +1,71 @@
<?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;
/**
* Base class for performance tests.
*
* Copied from Doctrine 2's OrmPerformanceTestCase.
*
* @author robo
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormPerformanceTestCase extends FormIntegrationTestCase
{
/**
* @var integer
*/
protected $maxRunningTime = 0;
/**
*/
protected function runTest()
{
$s = microtime(true);
parent::runTest();
$time = microtime(true) - $s;
if ($this->maxRunningTime != 0 && $time > $this->maxRunningTime) {
$this->fail(
sprintf(
'expected running time: <= %s but was: %s',
$this->maxRunningTime,
$time
)
);
}
}
/**
* @param integer $maxRunningTime
* @throws InvalidArgumentException
* @since Method available since Release 2.3.0
*/
public function setMaxRunningTime($maxRunningTime)
{
if (is_integer($maxRunningTime) && $maxRunningTime >= 0) {
$this->maxRunningTime = $maxRunningTime;
} else {
throw new \InvalidArgumentException;
}
}
/**
* @return integer
* @since Method available since Release 2.3.0
*/
public function getMaxRunningTime()
{
return $this->maxRunningTime;
}
}