From 4f5b44bd3bd490309eb2ba7b44df4769816ba729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20L=C5=93uillet?= Date: Sat, 3 Aug 2013 19:26:54 +0200 Subject: twig implementation --- .../Symfony/Component/OptionsResolver/.gitignore | 4 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidOptionsException.php | 21 + .../Exception/MissingOptionsException.php | 21 + .../Exception/OptionDefinitionException.php | 21 + .../Symfony/Component/OptionsResolver/LICENSE | 19 + .../Symfony/Component/OptionsResolver/Options.php | 513 ++++++++++++++++ .../Component/OptionsResolver/OptionsResolver.php | 346 +++++++++++ .../OptionsResolver/OptionsResolverInterface.php | 210 +++++++ .../Symfony/Component/OptionsResolver/README.md | 107 ++++ .../OptionsResolver/Tests/OptionsResolverTest.php | 681 +++++++++++++++++++++ .../OptionsResolver/Tests/OptionsTest.php | 529 ++++++++++++++++ .../Component/OptionsResolver/composer.json | 31 + .../Component/OptionsResolver/phpunit.xml.dist | 29 + 14 files changed, 2553 insertions(+) create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Options.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json create mode 100644 vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist (limited to 'vendor/symfony/options-resolver/Symfony') diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore new file mode 100644 index 00000000..44de97a3 --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml + diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php new file mode 100644 index 00000000..4224f4e3 --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php new file mode 100644 index 00000000..2e7ea1bc --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +class InvalidOptionsException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php new file mode 100644 index 00000000..8544dfb2 --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/MissingOptionsException.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +class MissingOptionsException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php new file mode 100644 index 00000000..11617fe1 --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Exception/OptionDefinitionException.php @@ -0,0 +1,21 @@ + + * + * 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 + */ +class OptionDefinitionException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE new file mode 100644 index 00000000..88a57f8d --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/LICENSE @@ -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. diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Options.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Options.php new file mode 100644 index 00000000..5b958af4 --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Options.php @@ -0,0 +1,513 @@ + + * + * 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 + */ +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: + * + * + * function (Options $options) + * + * + * 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: + * + * + * function (Options $options, $value) + * + * + * 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: + * + * + * function (Options $options, $value) + * + * + * 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]); + } +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php new file mode 100644 index 00000000..d6554baa --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -0,0 +1,346 @@ + + * + * 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 + * @author Tobias Schultze + */ +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) + )); + } + } +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php new file mode 100644 index 00000000..8474c4bc --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/OptionsResolverInterface.php @@ -0,0 +1,210 @@ + + * + * 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 + */ +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: + * + * + * function (Options $options) + * function (Options $options, $value) + * + * + * 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: + * + * + * function (Options $options, $value) + * + * + * 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()); +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md new file mode 100644 index 00000000..29cea102 --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/README.md @@ -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 diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php new file mode 100644 index 00000000..d50cd3fc --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -0,0 +1,681 @@ + + * + * 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()); + } +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php new file mode 100644 index 00000000..e24a7647 --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/Tests/OptionsTest.php @@ -0,0 +1,529 @@ + + * + * 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()); + } +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json new file mode 100644 index 00000000..f13d246e --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/composer.json @@ -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" + } + } +} diff --git a/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist new file mode 100644 index 00000000..ad24d17b --- /dev/null +++ b/vendor/symfony/options-resolver/Symfony/Component/OptionsResolver/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + + + + -- cgit v1.2.3