1
0
Fork 0
mirror of https://github.com/wallabag/wallabag.git synced 2025-09-05 18:41:02 +00:00

twig implementation

This commit is contained in:
Nicolas Lœuillet 2013-08-03 19:26:54 +02:00
parent 2b840e0cfb
commit 4f5b44bd3b
1418 changed files with 108207 additions and 1586 deletions

View file

@ -0,0 +1,4 @@
vendor/
composer.lock
phpunit.xml

View file

@ -0,0 +1,21 @@
<?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\Exception;
/**
* Marker interface for the Options component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ExceptionInterface
{
}

View file

@ -0,0 +1,21 @@
<?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\Exception;
/**
* Exception thrown when an invalid option is passed.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class InvalidOptionsException extends \InvalidArgumentException implements ExceptionInterface
{
}

View file

@ -0,0 +1,21 @@
<?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\Exception;
/**
* Exception thrown when a required option is missing.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class MissingOptionsException extends \InvalidArgumentException implements ExceptionInterface
{
}

View file

@ -0,0 +1,21 @@
<?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\Exception;
/**
* Thrown when an option definition is invalid.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class OptionDefinitionException extends \RuntimeException implements ExceptionInterface
{
}

View file

@ -0,0 +1,19 @@
Copyright (c) 2004-2013 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,513 @@
<?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;
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
/**
* Container for resolving inter-dependent options.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class Options implements \ArrayAccess, \Iterator, \Countable
{
/**
* A list of option values.
* @var array
*/
private $options = array();
/**
* A list of normalizer closures.
* @var array
*/
private $normalizers = array();
/**
* A list of closures for evaluating lazy options.
* @var array
*/
private $lazy = array();
/**
* A list containing the currently locked options.
* @var array
*/
private $lock = array();
/**
* Whether at least one option has already been read.
*
* Once read, the options cannot be changed anymore. This is
* necessary in order to avoid inconsistencies during the resolving
* process. If any option is changed after being read, all evaluated
* lazy options that depend on this option would become invalid.
*
* @var Boolean
*/
private $reading = false;
/**
* Sets the value of a given option.
*
* You can set lazy options by passing a closure with the following
* signature:
*
* <code>
* function (Options $options)
* </code>
*
* This closure will be evaluated once the option is read using
* {@link get()}. The closure has access to the resolved values of
* other options through the passed {@link Options} instance.
*
* @param string $option The name of the option.
* @param mixed $value The value of the option.
*
* @throws OptionDefinitionException If options have already been read.
* Once options are read, the container
* becomes immutable.
*/
public function set($option, $value)
{
// Setting is not possible once an option is read, because then lazy
// options could manipulate the state of the object, leading to
// inconsistent results.
if ($this->reading) {
throw new OptionDefinitionException('Options cannot be set anymore once options have been read.');
}
// Setting is equivalent to overloading while discarding the previous
// option value
unset($this->options[$option]);
unset($this->lazy[$option]);
$this->overload($option, $value);
}
/**
* Sets the normalizer for a given option.
*
* Normalizers should be closures with the following signature:
*
* <code>
* function (Options $options, $value)
* </code>
*
* This closure will be evaluated once the option is read using
* {@link get()}. The closure has access to the resolved values of
* other options through the passed {@link Options} instance.
*
* @param string $option The name of the option.
* @param \Closure $normalizer The normalizer.
*
* @throws OptionDefinitionException If options have already been read.
* Once options are read, the container
* becomes immutable.
*/
public function setNormalizer($option, \Closure $normalizer)
{
if ($this->reading) {
throw new OptionDefinitionException('Normalizers cannot be added anymore once options have been read.');
}
$this->normalizers[$option] = $normalizer;
}
/**
* Replaces the contents of the container with the given options.
*
* This method is a shortcut for {@link clear()} with subsequent
* calls to {@link set()}.
*
* @param array $options The options to set.
*
* @throws OptionDefinitionException If options have already been read.
* Once options are read, the container
* becomes immutable.
*/
public function replace(array $options)
{
if ($this->reading) {
throw new OptionDefinitionException('Options cannot be replaced anymore once options have been read.');
}
$this->options = array();
$this->lazy = array();
$this->normalizers = array();
foreach ($options as $option => $value) {
$this->overload($option, $value);
}
}
/**
* Overloads the value of a given option.
*
* Contrary to {@link set()}, this method keeps the previous default
* value of the option so that you can access it if you pass a closure.
* Passed closures should have the following signature:
*
* <code>
* function (Options $options, $value)
* </code>
*
* The second parameter passed to the closure is the current default
* value of the option.
*
* @param string $option The option name.
* @param mixed $value The option value.
*
* @throws OptionDefinitionException If options have already been read.
* Once options are read, the container
* becomes immutable.
*/
public function overload($option, $value)
{
if ($this->reading) {
throw new OptionDefinitionException('Options cannot be overloaded anymore once options have been read.');
}
// If an option is a closure that should be evaluated lazily, store it
// 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) {
// Initialize the option if no previous value exists
if (!isset($this->options[$option])) {
$this->options[$option] = null;
}
// Ignore previous lazy options if the closure has no second parameter
if (!isset($this->lazy[$option]) || !isset($params[1])) {
$this->lazy[$option] = array();
}
// Store closure for later evaluation
$this->lazy[$option][] = $value;
return;
}
}
// Remove lazy options by default
unset($this->lazy[$option]);
$this->options[$option] = $value;
}
/**
* Returns the value of the given option.
*
* If the option was a lazy option, it is evaluated now.
*
* @param string $option The option name.
*
* @return mixed The option value.
*
* @throws \OutOfBoundsException If the option does not exist.
* @throws OptionDefinitionException If a cyclic dependency is detected
* between two lazy options.
*/
public function get($option)
{
$this->reading = true;
if (!array_key_exists($option, $this->options)) {
throw new \OutOfBoundsException(sprintf('The option "%s" does not exist.', $option));
}
if (isset($this->lazy[$option])) {
$this->resolve($option);
}
if (isset($this->normalizers[$option])) {
$this->normalize($option);
}
return $this->options[$option];
}
/**
* Returns whether the given option exists.
*
* @param string $option The option name.
*
* @return Boolean Whether the option exists.
*/
public function has($option)
{
return array_key_exists($option, $this->options);
}
/**
* Removes the option with the given name.
*
* @param string $option The option name.
*
* @throws OptionDefinitionException If options have already been read.
* Once options are read, the container
* becomes immutable.
*/
public function remove($option)
{
if ($this->reading) {
throw new OptionDefinitionException('Options cannot be removed anymore once options have been read.');
}
unset($this->options[$option]);
unset($this->lazy[$option]);
unset($this->normalizers[$option]);
}
/**
* Removes all options.
*
* @throws OptionDefinitionException If options have already been read.
* Once options are read, the container
* becomes immutable.
*/
public function clear()
{
if ($this->reading) {
throw new OptionDefinitionException('Options cannot be cleared anymore once options have been read.');
}
$this->options = array();
$this->lazy = array();
$this->normalizers = array();
}
/**
* Returns the values of all options.
*
* Lazy options are evaluated at this point.
*
* @return array The option values.
*/
public function all()
{
$this->reading = true;
// Performance-wise this is slightly better than
// while (null !== $option = key($this->lazy))
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->normalizers as $option => $normalizer) {
if (isset($this->normalizers[$option])) {
$this->normalize($option);
}
}
return $this->options;
}
/**
* Equivalent to {@link has()}.
*
* @param string $option The option name.
*
* @return Boolean Whether the option exists.
*
* @see \ArrayAccess::offsetExists()
*/
public function offsetExists($option)
{
return $this->has($option);
}
/**
* Equivalent to {@link get()}.
*
* @param string $option The option name.
*
* @return mixed The option value.
*
* @throws \OutOfBoundsException If the option does not exist.
* @throws OptionDefinitionException If a cyclic dependency is detected
* between two lazy options.
*
* @see \ArrayAccess::offsetGet()
*/
public function offsetGet($option)
{
return $this->get($option);
}
/**
* Equivalent to {@link set()}.
*
* @param string $option The name of the option.
* @param mixed $value The value of the option. May be a closure with a
* signature as defined in DefaultOptions::add().
*
* @throws OptionDefinitionException If options have already been read.
* Once options are read, the container
* becomes immutable.
*
* @see \ArrayAccess::offsetSet()
*/
public function offsetSet($option, $value)
{
$this->set($option, $value);
}
/**
* Equivalent to {@link remove()}.
*
* @param string $option The option name.
*
* @throws OptionDefinitionException If options have already been read.
* Once options are read, the container
* becomes immutable.
*
* @see \ArrayAccess::offsetUnset()
*/
public function offsetUnset($option)
{
$this->remove($option);
}
/**
* {@inheritdoc}
*/
public function current()
{
return $this->get($this->key());
}
/**
* {@inheritdoc}
*/
public function next()
{
next($this->options);
}
/**
* {@inheritdoc}
*/
public function key()
{
return key($this->options);
}
/**
* {@inheritdoc}
*/
public function valid()
{
return null !== $this->key();
}
/**
* {@inheritdoc}
*/
public function rewind()
{
reset($this->options);
}
/**
* {@inheritdoc}
*/
public function count()
{
return count($this->options);
}
/**
* Evaluates the given lazy option.
*
* The evaluated value is written into the options array. The closure for
* evaluating the option is discarded afterwards.
*
* @param string $option The option to evaluate.
*
* @throws OptionDefinitionException If the option has a cyclic dependency
* on another option.
*/
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();
foreach ($this->lock as $option => $locked) {
if ($locked) {
$conflicts[] = $option;
}
}
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', $conflicts)));
}
$this->lock[$option] = true;
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
unset($this->lazy[$option]);
}
/**
* Normalizes the given option.
*
* The evaluated value is written into the options array.
*
* @param string $option The option to normalizer.
*
* @throws OptionDefinitionException If the option has a cyclic dependency
* on another 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();
foreach ($this->lock as $option => $locked) {
if ($locked) {
$conflicts[] = $option;
}
}
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', $conflicts)));
}
/** @var \Closure $normalizer */
$normalizer = $this->normalizers[$option];
$this->lock[$option] = true;
$this->options[$option] = $normalizer($this, array_key_exists($option, $this->options) ? $this->options[$option] : null);
unset($this->lock[$option]);
// The option is now normalized
unset($this->normalizers[$option]);
}
}

View file

@ -0,0 +1,346 @@
<?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;
use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
/**
* Helper for merging default and concrete option values.
*
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*/
class OptionsResolver implements OptionsResolverInterface
{
/**
* The default option values.
* @var Options
*/
private $defaultOptions;
/**
* The options known by the resolver.
* @var array
*/
private $knownOptions = array();
/**
* The options without defaults that are required to be passed to resolve().
* @var array
*/
private $requiredOptions = array();
/**
* A list of accepted values for each option.
* @var array
*/
private $allowedValues = array();
/**
* A list of accepted types for each option.
* @var array
*/
private $allowedTypes = array();
/**
* Creates a new instance.
*/
public function __construct()
{
$this->defaultOptions = new Options();
}
/**
* Clones the resolver.
*/
public function __clone()
{
$this->defaultOptions = clone $this->defaultOptions;
}
/**
* {@inheritdoc}
*/
public function setDefaults(array $defaultValues)
{
foreach ($defaultValues as $option => $value) {
$this->defaultOptions->overload($option, $value);
$this->knownOptions[$option] = true;
unset($this->requiredOptions[$option]);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function replaceDefaults(array $defaultValues)
{
foreach ($defaultValues as $option => $value) {
$this->defaultOptions->set($option, $value);
$this->knownOptions[$option] = true;
unset($this->requiredOptions[$option]);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function setOptional(array $optionNames)
{
foreach ($optionNames as $key => $option) {
if (!is_int($key)) {
throw new OptionDefinitionException('You should not pass default values to setOptional()');
}
$this->knownOptions[$option] = true;
}
return $this;
}
/**
* {@inheritdoc}
*/
public function setRequired(array $optionNames)
{
foreach ($optionNames as $key => $option) {
if (!is_int($key)) {
throw new OptionDefinitionException('You should not pass default values to setRequired()');
}
$this->knownOptions[$option] = true;
// set as required if no default has been set already
if (!isset($this->defaultOptions[$option])) {
$this->requiredOptions[$option] = true;
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function setAllowedValues(array $allowedValues)
{
$this->validateOptionsExistence($allowedValues);
$this->allowedValues = array_replace($this->allowedValues, $allowedValues);
return $this;
}
/**
* {@inheritdoc}
*/
public function addAllowedValues(array $allowedValues)
{
$this->validateOptionsExistence($allowedValues);
$this->allowedValues = array_merge_recursive($this->allowedValues, $allowedValues);
return $this;
}
/**
* {@inheritdoc}
*/
public function setAllowedTypes(array $allowedTypes)
{
$this->validateOptionsExistence($allowedTypes);
$this->allowedTypes = array_replace($this->allowedTypes, $allowedTypes);
return $this;
}
/**
* {@inheritdoc}
*/
public function addAllowedTypes(array $allowedTypes)
{
$this->validateOptionsExistence($allowedTypes);
$this->allowedTypes = array_merge_recursive($this->allowedTypes, $allowedTypes);
return $this;
}
/**
* {@inheritdoc}
*/
public function setNormalizers(array $normalizers)
{
$this->validateOptionsExistence($normalizers);
foreach ($normalizers as $option => $normalizer) {
$this->defaultOptions->setNormalizer($option, $normalizer);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function isKnown($option)
{
return isset($this->knownOptions[$option]);
}
/**
* {@inheritdoc}
*/
public function isRequired($option)
{
return isset($this->requiredOptions[$option]);
}
/**
* {@inheritdoc}
*/
public function resolve(array $options = array())
{
$this->validateOptionsExistence($options);
$this->validateOptionsCompleteness($options);
// Make sure this method can be called multiple times
$combinedOptions = clone $this->defaultOptions;
// Override options set by the user
foreach ($options as $option => $value) {
$combinedOptions->set($option, $value);
}
// Resolve options
$resolvedOptions = $combinedOptions->all();
$this->validateOptionValues($resolvedOptions);
$this->validateOptionTypes($resolvedOptions);
return $resolvedOptions;
}
/**
* Validates that the given option names exist and throws an exception
* otherwise.
*
* @param array $options An list of option names as keys.
*
* @throws InvalidOptionsException If any of the options has not been defined.
*/
private function validateOptionsExistence(array $options)
{
$diff = array_diff_key($options, $this->knownOptions);
if (count($diff) > 0) {
ksort($this->knownOptions);
ksort($diff);
throw new InvalidOptionsException(sprintf(
(count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Known options are: "%s"',
implode('", "', array_keys($diff)),
implode('", "', array_keys($this->knownOptions))
));
}
}
/**
* Validates that all required options are given and throws an exception
* otherwise.
*
* @param array $options An list of option names as keys.
*
* @throws MissingOptionsException If a required option is missing.
*/
private function validateOptionsCompleteness(array $options)
{
$diff = array_diff_key($this->requiredOptions, $options);
if (count($diff) > 0) {
ksort($diff);
throw new MissingOptionsException(sprintf(
count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.',
implode('", "', array_keys($diff))
));
}
}
/**
* Validates that the given option values match the allowed values and
* throws an exception otherwise.
*
* @param array $options A list of option values.
*
* @throws InvalidOptionsException If any of the values does not match the
* allowed values of the option.
*/
private function validateOptionValues(array $options)
{
foreach ($this->allowedValues as $option => $allowedValues) {
if (isset($options[$option]) && !in_array($options[$option], $allowedValues, true)) {
throw new InvalidOptionsException(sprintf('The option "%s" has the value "%s", but is expected to be one of "%s"', $option, $options[$option], implode('", "', $allowedValues)));
}
}
}
/**
* Validates that the given options match the allowed types and
* throws an exception otherwise.
*
* @param array $options A list of options.
*
* @throws InvalidOptionsException If any of the types does not match the
* allowed types of the option.
*/
private function validateOptionTypes(array $options)
{
foreach ($this->allowedTypes as $option => $allowedTypes) {
if (!array_key_exists($option, $options)) {
continue;
}
$value = $options[$option];
$allowedTypes = (array) $allowedTypes;
foreach ($allowedTypes as $type) {
$isFunction = 'is_'.$type;
if (function_exists($isFunction) && $isFunction($value)) {
continue 2;
} elseif ($value instanceof $type) {
continue 2;
}
}
$printableValue = is_object($value)
? get_class($value)
: (is_array($value)
? 'Array'
: (string) $value);
throw new InvalidOptionsException(sprintf(
'The option "%s" with value "%s" is expected to be of type "%s"',
$option,
$printableValue,
implode('", "', $allowedTypes)
));
}
}
}

View file

@ -0,0 +1,210 @@
<?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;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface OptionsResolverInterface
{
/**
* Sets default option values.
*
* The options can either be values of any types or closures that
* evaluate the option value lazily. These closures must have one
* of the following signatures:
*
* <code>
* function (Options $options)
* function (Options $options, $value)
* </code>
*
* The second parameter passed to the closure is the previously
* set default value, in case you are overwriting an existing
* default value.
*
* The closures should return the lazily created option value.
*
* @param array $defaultValues A list of option names as keys and default
* values or closures as values.
*
* @return OptionsResolverInterface The resolver instance.
*/
public function setDefaults(array $defaultValues);
/**
* Replaces default option values.
*
* Old defaults are erased, which means that closures passed here cannot
* access the previous default value. This may be useful to improve
* performance if the previous default value is calculated by an expensive
* closure.
*
* @param array $defaultValues A list of option names as keys and default
* values or closures as values.
*
* @return OptionsResolverInterface The resolver instance.
*/
public function replaceDefaults(array $defaultValues);
/**
* Sets optional options.
*
* This method declares valid option names without setting default values for them.
* If these options are not passed to {@link resolve()} and no default has been set
* for them, they will be missing in the final options array. This can be helpful
* if you want to determine whether an option has been set or not because otherwise
* {@link resolve()} would trigger an exception for unknown options.
*
* @param array $optionNames A list of option names.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws Exception\OptionDefinitionException When trying to pass default values.
*/
public function setOptional(array $optionNames);
/**
* Sets required options.
*
* If these options are not passed to {@link resolve()} and no default has been set for
* them, an exception will be thrown.
*
* @param array $optionNames A list of option names.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws Exception\OptionDefinitionException When trying to pass default values.
*/
public function setRequired(array $optionNames);
/**
* Sets allowed values for a list of options.
*
* @param array $allowedValues A list of option names as keys and arrays
* with values acceptable for that option as
* values.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws Exception\InvalidOptionsException If an option has not been defined
* (see {@link isKnown()}) for which
* an allowed value is set.
*/
public function setAllowedValues(array $allowedValues);
/**
* Adds allowed values for a list of options.
*
* The values are merged with the allowed values defined previously.
*
* @param array $allowedValues A list of option names as keys and arrays
* with values acceptable for that option as
* values.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws Exception\InvalidOptionsException If an option has not been defined
* (see {@link isKnown()}) for which
* an allowed value is set.
*/
public function addAllowedValues(array $allowedValues);
/**
* Sets allowed types for a list of options.
*
* @param array $allowedTypes A list of option names as keys and type
* names passed as string or array as values.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws Exception\InvalidOptionsException If an option has not been defined for
* which an allowed type is set.
*/
public function setAllowedTypes(array $allowedTypes);
/**
* Adds allowed types for a list of options.
*
* The types are merged with the allowed types defined previously.
*
* @param array $allowedTypes A list of option names as keys and type
* names passed as string or array as values.
*
* @return OptionsResolverInterface The resolver instance.
*
* @throws Exception\InvalidOptionsException If an option has not been defined for
* which an allowed type is set.
*/
public function addAllowedTypes(array $allowedTypes);
/**
* Sets normalizers that are applied on resolved options.
*
* The normalizers should be closures with the following signature:
*
* <code>
* function (Options $options, $value)
* </code>
*
* The second parameter passed to the closure is the value of
* the option.
*
* The closure should return the normalized value.
*
* @param array $normalizers An array of closures.
*
* @return OptionsResolverInterface The resolver instance.
*/
public function setNormalizers(array $normalizers);
/**
* Returns whether an option is known.
*
* An option is known if it has been passed to either {@link setDefaults()},
* {@link setRequired()} or {@link setOptional()} before.
*
* @param string $option The name of the option.
*
* @return Boolean Whether the option is known.
*/
public function isKnown($option);
/**
* Returns whether an option is required.
*
* An option is required if it has been passed to {@link setRequired()},
* but not to {@link setDefaults()}. That is, the option has been declared
* as required and no default value has been set.
*
* @param string $option The name of the option.
*
* @return Boolean Whether the option is required.
*/
public function isRequired($option);
/**
* Returns the combination of the default and the passed options.
*
* @param array $options The custom option values.
*
* @return array A list of options and their values.
*
* @throws Exception\InvalidOptionsException If any of the passed options has not
* been defined or does not contain an
* allowed value.
* @throws Exception\MissingOptionsException If a required option is missing.
* @throws Exception\OptionDefinitionException If a cyclic dependency is detected
* between two lazy options.
*/
public function resolve(array $options = array());
}

View file

@ -0,0 +1,107 @@
OptionsResolver Component
=========================
OptionsResolver helps at configuring objects with option arrays.
It supports default values on different levels of your class hierarchy,
option constraints (required vs. optional, allowed values) and lazy options
whose default value depends on the value of another option.
The following example demonstrates a Person class with two required options
"firstName" and "lastName" and two optional options "age" and "gender", where
the default value of "gender" is derived from the passed first name, if
possible, and may only be one of "male" and "female".
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\Options;
class Person
{
protected $options;
public function __construct(array $options = array())
{
$resolver = new OptionsResolver();
$this->setDefaultOptions($resolver);
$this->options = $resolver->resolve($options);
}
protected function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setRequired(array(
'firstName',
'lastName',
));
$resolver->setDefaults(array(
'age' => null,
'gender' => function (Options $options) {
if (self::isKnownMaleName($options['firstName'])) {
return 'male';
}
return 'female';
},
));
$resolver->setAllowedValues(array(
'gender' => array('male', 'female'),
));
}
}
We can now easily instantiate a Person object:
// 'gender' is implicitly set to 'female'
$person = new Person(array(
'firstName' => 'Jane',
'lastName' => 'Doe',
));
We can also override the default values of the optional options:
$person = new Person(array(
'firstName' => 'Abdullah',
'lastName' => 'Mogashi',
'gender' => 'male',
'age' => 30,
));
Options can be added or changed in subclasses by overriding the `setDefaultOptions`
method:
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\Options;
class Employee extends Person
{
protected function setDefaultOptions(OptionsResolverInterface $resolver)
{
parent::setDefaultOptions($resolver);
$resolver->setRequired(array(
'birthDate',
));
$resolver->setDefaults(array(
// $previousValue contains the default value configured in the
// parent class
'age' => function (Options $options, $previousValue) {
return self::calculateAge($options['birthDate']);
}
));
}
}
Resources
---------
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/OptionsResolver/
$ composer.phar install --dev
$ phpunit

View file

@ -0,0 +1,681 @@
<?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\OptionsResolver;
use Symfony\Component\OptionsResolver\Options;
class OptionsResolverTest extends \PHPUnit_Framework_TestCase
{
/**
* @var OptionsResolver
*/
private $resolver;
protected function setUp()
{
$this->resolver = new OptionsResolver();
}
public function testResolve()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$options = array(
'two' => '20',
);
$this->assertEquals(array(
'one' => '1',
'two' => '20',
), $this->resolver->resolve($options));
}
public function testResolveLazy()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => function (Options $options) {
return '20';
},
));
$this->assertEquals(array(
'one' => '1',
'two' => '20',
), $this->resolver->resolve(array()));
}
public function testResolveLazyDependencyOnOptional()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => function (Options $options) {
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnMissingOptionalWithoutDefault()
{
$test = $this;
$this->resolver->setOptional(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertFalse(isset($options['one']));
return '2';
},
));
$options = array(
);
$this->assertEquals(array(
'two' => '2',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnOptionalWithoutDefault()
{
$test = $this;
$this->resolver->setOptional(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertTrue(isset($options['one']));
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyDependencyOnRequired()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => function (Options $options) {
return $options['one'].'2';
},
));
$options = array(
'one' => '10',
);
$this->assertEquals(array(
'one' => '10',
'two' => '102',
), $this->resolver->resolve($options));
}
public function testResolveLazyReplaceDefaults()
{
$test = $this;
$this->resolver->setDefaults(array(
'one' => function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->fail('Previous closure should not be executed');
},
));
$this->resolver->replaceDefaults(array(
'one' => function (Options $options, $previousValue) {
return '1';
},
));
$this->assertEquals(array(
'one' => '1',
), $this->resolver->resolve(array()));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfNonExistingOption()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setRequired(array(
'two',
));
$this->resolver->setOptional(array(
'three',
));
$this->resolver->resolve(array(
'foo' => 'bar',
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
*/
public function testResolveFailsIfMissingRequiredOption()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setDefaults(array(
'two' => '2',
));
$this->resolver->resolve(array(
'two' => '20',
));
}
public function testResolveSucceedsIfOptionValueAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
));
$options = array(
'one' => 'one',
);
$this->assertEquals(array(
'one' => 'one',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionValueAllowed2()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedValues(array(
'one' => '1',
'two' => '2',
));
$this->resolver->addAllowedValues(array(
'one' => 'one',
'two' => 'two',
));
$options = array(
'one' => '1',
'two' => 'two',
);
$this->assertEquals(array(
'one' => '1',
'two' => 'two',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionalWithAllowedValuesNotSet()
{
$this->resolver->setRequired(array(
'one',
));
$this->resolver->setOptional(array(
'two',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
'two' => array('2', 'two'),
));
$options = array(
'one' => '1',
);
$this->assertEquals(array(
'one' => '1',
), $this->resolver->resolve($options));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionValueNotAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedValues(array(
'one' => array('1', 'one'),
));
$this->resolver->resolve(array(
'one' => '2',
));
}
public function testResolveSucceedsIfOptionTypeAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
));
$options = array(
'one' => 'one',
);
$this->assertEquals(array(
'one' => 'one',
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassArray()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => array('string', 'bool'),
));
$options = array(
'one' => true,
);
$this->assertEquals(array(
'one' => true,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassObject()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'object',
));
$object = new \stdClass();
$options = array(
'one' => $object,
);
$this->assertEquals(array(
'one' => $object,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedPassClass()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => '\stdClass',
));
$object = new \stdClass();
$options = array(
'one' => $object,
);
$this->assertEquals(array(
'one' => $object,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionTypeAllowedAddTypes()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'bool',
));
$this->resolver->addAllowedTypes(array(
'one' => 'float',
'two' => 'integer',
));
$options = array(
'one' => 1.23,
'two' => false,
);
$this->assertEquals(array(
'one' => 1.23,
'two' => false,
), $this->resolver->resolve($options));
}
public function testResolveSucceedsIfOptionalWithTypeAndWithoutValue()
{
$this->resolver->setOptional(array(
'one',
'two',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'int',
));
$options = array(
'two' => 1,
);
$this->assertEquals(array(
'two' => 1,
), $this->resolver->resolve($options));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowed()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => array('string', 'bool'),
));
$this->resolver->resolve(array(
'one' => 1.23,
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowedMultipleOptions()
{
$this->resolver->setDefaults(array(
'one' => '1',
'two' => '2',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
'two' => 'bool',
));
$this->resolver->resolve(array(
'one' => 'foo',
'two' => 1.23,
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
*/
public function testResolveFailsIfOptionTypeNotAllowedAddTypes()
{
$this->resolver->setDefaults(array(
'one' => '1',
));
$this->resolver->setAllowedTypes(array(
'one' => 'string',
));
$this->resolver->addAllowedTypes(array(
'one' => 'bool',
));
$this->resolver->resolve(array(
'one' => 1.23,
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testSetRequiredFailsIfDefaultIsPassed()
{
$this->resolver->setRequired(array(
'one' => '1',
));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testSetOptionalFailsIfDefaultIsPassed()
{
$this->resolver->setOptional(array(
'one' => '1',
));
}
public function testFluidInterface()
{
$this->resolver->setDefaults(array('one' => '1'))
->replaceDefaults(array('one' => '2'))
->setAllowedValues(array('one' => array('1', '2')))
->addAllowedValues(array('one' => array('3')))
->setRequired(array('two'))
->setOptional(array('three'));
$options = array(
'two' => '2',
);
$this->assertEquals(array(
'one' => '2',
'two' => '2',
), $this->resolver->resolve($options));
}
public function testKnownIfDefaultWasSet()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testKnownIfRequired()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setRequired(array(
'foo',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testKnownIfOptional()
{
$this->assertFalse($this->resolver->isKnown('foo'));
$this->resolver->setOptional(array(
'foo',
));
$this->assertTrue($this->resolver->isKnown('foo'));
}
public function testRequiredIfRequired()
{
$this->assertFalse($this->resolver->isRequired('foo'));
$this->resolver->setRequired(array(
'foo',
));
$this->assertTrue($this->resolver->isRequired('foo'));
}
public function testNotRequiredIfRequiredAndDefaultValue()
{
$this->assertFalse($this->resolver->isRequired('foo'));
$this->resolver->setRequired(array(
'foo',
));
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->assertFalse($this->resolver->isRequired('foo'));
}
public function testNormalizersTransformFinalOptions()
{
$this->resolver->setDefaults(array(
'foo' => 'bar',
'bam' => 'baz',
));
$this->resolver->setNormalizers(array(
'foo' => function (Options $options, $value) {
return $options['bam'].'['.$value.']';
},
));
$expected = array(
'foo' => 'baz[bar]',
'bam' => 'baz',
);
$this->assertEquals($expected, $this->resolver->resolve(array()));
$expected = array(
'foo' => 'boo[custom]',
'bam' => 'boo',
);
$this->assertEquals($expected, $this->resolver->resolve(array(
'foo' => 'custom',
'bam' => 'boo',
)));
}
public function testResolveWithoutOptionSucceedsIfRequiredAndDefaultValue()
{
$this->resolver->setRequired(array(
'foo',
));
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->assertEquals(array(
'foo' => 'bar'
), $this->resolver->resolve(array()));
}
public function testResolveWithoutOptionSucceedsIfDefaultValueAndRequired()
{
$this->resolver->setDefaults(array(
'foo' => 'bar',
));
$this->resolver->setRequired(array(
'foo',
));
$this->assertEquals(array(
'foo' => 'bar'
), $this->resolver->resolve(array()));
}
public function testResolveSucceedsIfOptionRequiredAndValueAllowed()
{
$this->resolver->setRequired(array(
'one', 'two',
));
$this->resolver->setAllowedValues(array(
'two' => array('2'),
));
$options = array(
'one' => '1',
'two' => '2'
);
$this->assertEquals($options, $this->resolver->resolve($options));
}
public function testClone()
{
$this->resolver->setDefaults(array('one' => '1'));
$clone = clone $this->resolver;
// Changes after cloning don't affect each other
$this->resolver->setDefaults(array('two' => '2'));
$clone->setDefaults(array('three' => '3'));
$this->assertEquals(array(
'one' => '1',
'two' => '2',
), $this->resolver->resolve());
$this->assertEquals(array(
'one' => '1',
'three' => '3',
), $clone->resolve());
}
}

View file

@ -0,0 +1,529 @@
<?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\Options;
class OptionsTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Options
*/
private $options;
protected function setUp()
{
$this->options = new Options();
}
public function testArrayAccess()
{
$this->assertFalse(isset($this->options['foo']));
$this->assertFalse(isset($this->options['bar']));
$this->options['foo'] = 0;
$this->options['bar'] = 1;
$this->assertTrue(isset($this->options['foo']));
$this->assertTrue(isset($this->options['bar']));
unset($this->options['bar']);
$this->assertTrue(isset($this->options['foo']));
$this->assertFalse(isset($this->options['bar']));
$this->assertEquals(0, $this->options['foo']);
}
public function testCountable()
{
$this->options->set('foo', 0);
$this->options->set('bar', 1);
$this->assertCount(2, $this->options);
}
/**
* @expectedException \OutOfBoundsException
*/
public function testGetNonExisting()
{
$this->options->get('foo');
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testSetNotSupportedAfterGet()
{
$this->options->set('foo', 'bar');
$this->options->get('foo');
$this->options->set('foo', 'baz');
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testRemoveNotSupportedAfterGet()
{
$this->options->set('foo', 'bar');
$this->options->get('foo');
$this->options->remove('foo');
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testSetNormalizerNotSupportedAfterGet()
{
$this->options->set('foo', 'bar');
$this->options->get('foo');
$this->options->setNormalizer('foo', function () {});
}
public function testSetLazyOption()
{
$test = $this;
$this->options->set('foo', function (Options $options) use ($test) {
return 'dynamic';
});
$this->assertEquals('dynamic', $this->options->get('foo'));
}
public function testSetDiscardsPreviousValue()
{
$test = $this;
// defined by superclass
$this->options->set('foo', 'bar');
// defined by subclass
$this->options->set('foo', function (Options $options, $previousValue) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertNull($previousValue);
return 'dynamic';
});
$this->assertEquals('dynamic', $this->options->get('foo'));
}
public function testOverloadKeepsPreviousValue()
{
$test = $this;
// defined by superclass
$this->options->set('foo', 'bar');
// defined by subclass
$this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $previousValue);
return 'dynamic';
});
$this->assertEquals('dynamic', $this->options->get('foo'));
}
public function testPreviousValueIsEvaluatedIfLazy()
{
$test = $this;
// defined by superclass
$this->options->set('foo', function (Options $options) {
return 'bar';
});
// defined by subclass
$this->options->overload('foo', function (Options $options, $previousValue) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $previousValue);
return 'dynamic';
});
$this->assertEquals('dynamic', $this->options->get('foo'));
}
public function testPreviousValueIsNotEvaluatedIfNoSecondArgument()
{
$test = $this;
// defined by superclass
$this->options->set('foo', function (Options $options) use ($test) {
$test->fail('Should not be called');
});
// defined by subclass, no $previousValue argument defined!
$this->options->overload('foo', function (Options $options) use ($test) {
return 'dynamic';
});
$this->assertEquals('dynamic', $this->options->get('foo'));
}
public function testLazyOptionCanAccessOtherOptions()
{
$test = $this;
$this->options->set('foo', 'bar');
$this->options->set('bam', function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'dynamic';
});
$this->assertEquals('bar', $this->options->get('foo'));
$this->assertEquals('dynamic', $this->options->get('bam'));
}
public function testLazyOptionCanAccessOtherLazyOptions()
{
$test = $this;
$this->options->set('foo', function (Options $options) {
return 'bar';
});
$this->options->set('bam', function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'dynamic';
});
$this->assertEquals('bar', $this->options->get('foo'));
$this->assertEquals('dynamic', $this->options->get('bam'));
}
public function testNormalizer()
{
$this->options->set('foo', 'bar');
$this->options->setNormalizer('foo', function () {
return 'normalized';
});
$this->assertEquals('normalized', $this->options->get('foo'));
}
public function testNormalizerReceivesUnnormalizedValue()
{
$this->options->set('foo', 'bar');
$this->options->setNormalizer('foo', function (Options $options, $value) {
return 'normalized['.$value.']';
});
$this->assertEquals('normalized[bar]', $this->options->get('foo'));
}
public function testNormalizerCanAccessOtherOptions()
{
$test = $this;
$this->options->set('foo', 'bar');
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'normalized';
});
$this->assertEquals('bar', $this->options->get('foo'));
$this->assertEquals('normalized', $this->options->get('bam'));
}
public function testNormalizerCanAccessOtherLazyOptions()
{
$test = $this;
$this->options->set('foo', function (Options $options) {
return 'bar';
});
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) use ($test) {
/* @var \PHPUnit_Framework_TestCase $test */
$test->assertEquals('bar', $options->get('foo'));
return 'normalized';
});
$this->assertEquals('bar', $this->options->get('foo'));
$this->assertEquals('normalized', $this->options->get('bam'));
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependencies()
{
$this->options->set('foo', function (Options $options) {
$options->get('bam');
});
$this->options->set('bam', function (Options $options) {
$options->get('foo');
});
$this->options->get('foo');
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependenciesBetweenNormalizers()
{
$this->options->set('foo', 'bar');
$this->options->set('bam', 'baz');
$this->options->setNormalizer('foo', function (Options $options) {
$options->get('bam');
});
$this->options->setNormalizer('bam', function (Options $options) {
$options->get('foo');
});
$this->options->get('foo');
}
/**
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testFailForCyclicDependenciesBetweenNormalizerAndLazyOption()
{
$this->options->set('foo', function (Options $options) {
$options->get('bam');
});
$this->options->set('bam', 'baz');
$this->options->setNormalizer('bam', function (Options $options) {
$options->get('foo');
});
$this->options->get('foo');
}
public function testAllInvokesEachLazyOptionOnlyOnce()
{
$test = $this;
$i = 1;
$this->options->set('foo', function (Options $options) use ($test, &$i) {
$test->assertSame(1, $i);
++$i;
// Implicitly invoke lazy option for "bam"
$options->get('bam');
});
$this->options->set('bam', function (Options $options) use ($test, &$i) {
$test->assertSame(2, $i);
++$i;
});
$this->options->all();
}
public function testAllInvokesEachNormalizerOnlyOnce()
{
$test = $this;
$i = 1;
$this->options->set('foo', 'bar');
$this->options->set('bam', 'baz');
$this->options->setNormalizer('foo', function (Options $options) use ($test, &$i) {
$test->assertSame(1, $i);
++$i;
// Implicitly invoke normalizer for "bam"
$options->get('bam');
});
$this->options->setNormalizer('bam', function (Options $options) use ($test, &$i) {
$test->assertSame(2, $i);
++$i;
});
$this->options->all();
}
public function testReplaceClearsAndSets()
{
$this->options->set('one', '1');
$this->options->replace(array(
'two' => '2',
'three' => function (Options $options) {
return '2' === $options['two'] ? '3' : 'foo';
}
));
$this->assertEquals(array(
'two' => '2',
'three' => '3',
), $this->options->all());
}
public function testClearRemovesAllOptions()
{
$this->options->set('one', 1);
$this->options->set('two', 2);
$this->options->clear();
$this->assertEmpty($this->options->all());
}
/**
* @covers Symfony\Component\OptionsResolver\Options::replace
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testCannotReplaceAfterOptionWasRead()
{
$this->options->set('one', 1);
$this->options->all();
$this->options->replace(array(
'two' => '2',
));
}
/**
* @covers Symfony\Component\OptionsResolver\Options::overload
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testCannotOverloadAfterOptionWasRead()
{
$this->options->set('one', 1);
$this->options->all();
$this->options->overload('one', 2);
}
/**
* @covers Symfony\Component\OptionsResolver\Options::clear
* @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
*/
public function testCannotClearAfterOptionWasRead()
{
$this->options->set('one', 1);
$this->options->all();
$this->options->clear();
}
public function testOverloadCannotBeEvaluatedLazilyWithoutExpectedClosureParams()
{
$this->options->set('foo', 'bar');
$this->options->overload('foo', function () {
return 'test';
});
$this->assertNotEquals('test', $this->options->get('foo'));
$this->assertTrue(is_callable($this->options->get('foo')));
}
public function testOverloadCannotBeEvaluatedLazilyWithoutFirstParamTypeHint()
{
$this->options->set('foo', 'bar');
$this->options->overload('foo', function ($object) {
return 'test';
});
$this->assertNotEquals('test', $this->options->get('foo'));
$this->assertTrue(is_callable($this->options->get('foo')));
}
public function testOptionsIteration()
{
$this->options->set('foo', 'bar');
$this->options->set('foo1', 'bar1');
$expectedResult = array('foo' => 'bar', 'foo1' => 'bar1');
$this->assertEquals($expectedResult, iterator_to_array($this->options, true));
}
public function testHasWithNullValue()
{
$this->options->set('foo', null);
$this->assertTrue($this->options->has('foo'));
}
public function testRemoveOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->remove('foo2');
$this->assertEquals(array('foo1' => ''), $this->options->all());
}
public function testReplaceOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->replace(array('foo1' => 'new'));
$this->assertEquals(array('foo1' => 'new'), $this->options->all());
}
public function testClearOptionAndNormalizer()
{
$this->options->set('foo1', 'bar');
$this->options->setNormalizer('foo1', function (Options $options) {
return '';
});
$this->options->set('foo2', 'bar');
$this->options->setNormalizer('foo2', function (Options $options) {
return '';
});
$this->options->clear();
$this->assertEmpty($this->options->all());
}
public function testNormalizerWithoutCorrespondingOption()
{
$test = $this;
$this->options->setNormalizer('foo', function (Options $options, $previousValue) use ($test) {
$test->assertNull($previousValue);
return '';
});
$this->assertEquals(array('foo' => ''), $this->options->all());
}
}

View file

@ -0,0 +1,31 @@
{
"name": "symfony/options-resolver",
"type": "library",
"description": "Symfony OptionsResolver Component",
"keywords": ["options", "config", "configuration"],
"homepage": "http://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"require": {
"php": ">=5.3.3"
},
"autoload": {
"psr-0": { "Symfony\\Component\\OptionsResolver\\": "" }
},
"target-dir": "Symfony/Component/OptionsResolver",
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
}
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="Symfony OptionsResolver Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
</exclude>
</whitelist>
</filter>
</phpunit>