[OptionsResolver] Removed LazyOption class and improved performance a tiny bit
This commit is contained in:
parent
50652a9717
commit
27ab56d8ee
@ -1,73 +0,0 @@
|
||||
<?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\OptionsResolver;
|
||||
|
||||
/**
|
||||
* An option that is evaluated lazily using a closure.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
|
||||
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 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
// 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 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
}
|
||||
|
||||
$this->normalizers[$option] = $normalizer;
|
||||
|
||||
// Each option for which a normalizer exists needs to be normalized
|
||||
$this->normalization[$option] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -150,6 +142,7 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
}
|
||||
|
||||
$this->options = array();
|
||||
$this->lazy = array();
|
||||
|
||||
foreach ($options as $option => $value) {
|
||||
$this->overload($option, $value);
|
||||
@ -184,25 +177,30 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
}
|
||||
|
||||
// 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 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
$this->resolve($option);
|
||||
}
|
||||
|
||||
if (isset($this->normalization[$option])) {
|
||||
if (isset($this->normalizers[$option])) {
|
||||
$this->normalize($option);
|
||||
}
|
||||
|
||||
@ -269,6 +267,7 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
|
||||
unset($this->options[$option]);
|
||||
unset($this->lazy[$option]);
|
||||
unset($this->normalizers[$option]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -286,6 +285,7 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
|
||||
$this->options = array();
|
||||
$this->lazy = array();
|
||||
$this->normalizers = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,14 +301,16 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
|
||||
// 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 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
*/
|
||||
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 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
}
|
||||
|
||||
$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 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
*/
|
||||
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 @@ class Options implements \ArrayAccess, \Iterator, \Countable
|
||||
unset($this->lock[$option]);
|
||||
|
||||
// The option is now normalized
|
||||
unset($this->normalization[$option]);
|
||||
unset($this->normalizers[$option]);
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
<?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\OptionsResolver\Tests;
|
||||
|
||||
use Symfony\Component\OptionsResolver\LazyOption;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user