diff --git a/src/Symfony/Component/OptionsResolver/LazyOption.php b/src/Symfony/Component/OptionsResolver/LazyOption.php deleted file mode 100644 index fbe700a6b05e..000000000000 --- a/src/Symfony/Component/OptionsResolver/LazyOption.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\OptionsResolver; - -/** - * An option that is evaluated lazily using a closure. - * - * @author Bernhard Schussek - */ -class LazyOption -{ - /** - * The underlying closure. - * @var \Closure - */ - private $closure; - - /** - * The previous default value of the option. - * @var mixed - */ - private $previousValue; - - /** - * Creates a new lazy option. - * - * @param \Closure $closure The closure used for initializing the - * option value. - * @param mixed $previousValue The previous value of the option. This - * value is passed to the closure when it is - * evaluated. - * - * @see evaluate() - */ - public function __construct(\Closure $closure, $previousValue = null) - { - $this->closure = $closure; - $this->previousValue = $previousValue; - } - - /** - * Evaluates the underlying closure and returns its result. - * - * The given Options instance is passed to the closure as first argument. - * The previous default value set in the constructor is passed as second - * argument. - * - * @param Options $options The container with all concrete options. - * - * @return mixed The result of the closure. - */ - public function evaluate(Options $options) - { - $previousValue = $this->previousValue; - $closure = $this->closure; - - if ($previousValue instanceof self) { - $previousValue = $this->previousValue->evaluate($options); - } - - // Performs a bit better than __invoke() and call_user_func() - return $closure($options, $previousValue); - } -} diff --git a/src/Symfony/Component/OptionsResolver/Options.php b/src/Symfony/Component/OptionsResolver/Options.php index 1fb6057c3080..e4713ef8232f 100644 --- a/src/Symfony/Component/OptionsResolver/Options.php +++ b/src/Symfony/Component/OptionsResolver/Options.php @@ -21,7 +21,7 @@ class Options implements \ArrayAccess, \Iterator, \Countable { /** - * A list of option values and LazyOption instances. + * A list of option values. * @var array */ private $options = array(); @@ -33,19 +33,13 @@ class Options implements \ArrayAccess, \Iterator, \Countable private $normalizers = array(); /** - * A list storing the names of all options that need to be normalized. - * @var array - */ - private $normalization = array(); - - /** - * A list storing the names of all LazyOption instances as keys. + * A list of closures for evaluating lazy options. * @var array */ private $lazy = array(); /** - * A list of Boolean locks for each LazyOption. + * A list containing the currently locked options. * @var array */ private $lock = array(); @@ -95,6 +89,7 @@ public function set($option, $value) // Setting is equivalent to overloading while discarding the previous // option value unset($this->options[$option]); + unset($this->lazy[$option]); $this->overload($option, $value); } @@ -126,9 +121,6 @@ public function setNormalizer($option, \Closure $normalizer) } $this->normalizers[$option] = $normalizer; - - // Each option for which a normalizer exists needs to be normalized - $this->normalization[$option] = true; } /** @@ -150,6 +142,7 @@ public function replace(array $options) } $this->options = array(); + $this->lazy = array(); foreach ($options as $option => $value) { $this->overload($option, $value); @@ -184,25 +177,30 @@ public function overload($option, $value) } // If an option is a closure that should be evaluated lazily, store it - // inside a LazyOption instance. + // in the "lazy" property. if ($value instanceof \Closure) { $reflClosure = new \ReflectionFunction($value); $params = $reflClosure->getParameters(); if (isset($params[0]) && null !== ($class = $params[0]->getClass()) && __CLASS__ === $class->name) { - $currentValue = isset($params[1]) && isset($this->options[$option]) ? $this->options[$option] : null; - $value = new LazyOption($value, $currentValue); + // Initialize the option if no previous value exists + if (!isset($this->options[$option])) { + $this->options[$option] = null; + } - // Store which options are lazy for more efficient resolving - $this->lazy[$option] = true; + // Ignore previous lazy options if the closure has no second parameter + if (!isset($this->lazy[$option]) || !isset($params[1])) { + $this->lazy[$option] = array(); + } - $this->options[$option] = $value; + // Store closure for later evaluation + $this->lazy[$option][] = $value; return; } } - // Reset lazy flag and locks by default + // Remove lazy options by default unset($this->lazy[$option]); $this->options[$option] = $value; @@ -233,7 +231,7 @@ public function get($option) $this->resolve($option); } - if (isset($this->normalization[$option])) { + if (isset($this->normalizers[$option])) { $this->normalize($option); } @@ -269,6 +267,7 @@ public function remove($option) unset($this->options[$option]); unset($this->lazy[$option]); + unset($this->normalizers[$option]); } /** @@ -286,6 +285,7 @@ public function clear() $this->options = array(); $this->lazy = array(); + $this->normalizers = array(); } /** @@ -301,14 +301,16 @@ public function all() // Performance-wise this is slightly better than // while (null !== $option = key($this->lazy)) - foreach ($this->lazy as $option => $isLazy) { + foreach ($this->lazy as $option => $closures) { + // Double check, in case the option has already been resolved + // by cascade in the previous cycles if (isset($this->lazy[$option])) { $this->resolve($option); } } - foreach ($this->normalization as $option => $normalize) { - if (isset($this->normalization[$option])) { + foreach ($this->normalizers as $option => $normalizer) { + if (isset($this->normalizers[$option])) { $this->normalize($option); } } @@ -443,6 +445,10 @@ public function count() */ private function resolve($option) { + // The code duplication with normalize() exists for performance + // reasons, in order to save a method call. + // Remember that this method is potentially called a couple of thousand + // times and needs to be as efficient as possible. if (isset($this->lock[$option])) { $conflicts = array(); @@ -456,7 +462,9 @@ private function resolve($option) } $this->lock[$option] = true; - $this->options[$option] = $this->options[$option]->evaluate($this); + foreach ($this->lazy[$option] as $closure) { + $this->options[$option] = $closure($this, $this->options[$option]); + } unset($this->lock[$option]); // The option now isn't lazy anymore @@ -475,6 +483,10 @@ private function resolve($option) */ private function normalize($option) { + // The code duplication with resolve() exists for performance + // reasons, in order to save a method call. + // Remember that this method is potentially called a couple of thousand + // times and needs to be as efficient as possible. if (isset($this->lock[$option])) { $conflicts = array(); @@ -495,6 +507,6 @@ private function normalize($option) unset($this->lock[$option]); // The option is now normalized - unset($this->normalization[$option]); + unset($this->normalizers[$option]); } } diff --git a/src/Symfony/Component/OptionsResolver/Tests/LazyOptionTest.php b/src/Symfony/Component/OptionsResolver/Tests/LazyOptionTest.php deleted file mode 100644 index 48b15cec80a5..000000000000 --- a/src/Symfony/Component/OptionsResolver/Tests/LazyOptionTest.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\OptionsResolver\Tests; - -use Symfony\Component\OptionsResolver\LazyOption; -use Symfony\Component\OptionsResolver\Options; - -/** - * @author Bernhard Schussek - */ -class LazyOptionTest extends \PHPUnit_Framework_TestCase -{ - public function testDontCacheEvaluatedPreviousValue() - { - $previousValue = new LazyOption(function (Options $options) { - return $options['foo']; - }); - - $lazyOption = new LazyOption(function (Options $options, $previousValue) { - return $previousValue; - }, $previousValue); - - // If provided with two different option sets, two different results - // should be returned - $options1 = new Options(); - $options1['foo'] = 'bar'; - - $this->assertSame('bar', $lazyOption->evaluate($options1)); - - $options2 = new Options(); - $options2['foo'] = 'boo'; - - $this->assertSame('boo', $lazyOption->evaluate($options2)); - } -}