[Form] Moved Options classes to new OptionsParser component
This commit is contained in:
parent
46ffbd5282
commit
b9d053edb2
@ -42,6 +42,7 @@
|
||||
"symfony/http-foundation": "self.version",
|
||||
"symfony/http-kernel": "self.version",
|
||||
"symfony/locale": "self.version",
|
||||
"symfony/options-parser": "self.version",
|
||||
"symfony/process": "self.version",
|
||||
"symfony/routing": "self.version",
|
||||
"symfony/security": "self.version",
|
||||
|
@ -19,7 +19,7 @@ use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface;
|
||||
use Symfony\Bridge\Doctrine\Form\EventListener\MergeDoctrineCollectionListener;
|
||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Options;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
abstract class DoctrineType extends AbstractType
|
||||
{
|
||||
|
@ -14,8 +14,8 @@ namespace Symfony\Bridge\Propel1\Form\Type;
|
||||
use Symfony\Bridge\Propel1\Form\ChoiceList\ModelChoiceList;
|
||||
use Symfony\Bridge\Propel1\Form\DataTransformer\CollectionToArrayTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Options;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
/**
|
||||
* ModelType class.
|
||||
|
@ -1,320 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form;
|
||||
|
||||
use Symfony\Component\Form\Exception\OptionDefinitionException;
|
||||
use Symfony\Component\Form\Exception\InvalidOptionException;
|
||||
|
||||
/**
|
||||
* Helper for specifying and resolving inter-dependent options.
|
||||
*
|
||||
* Options are a common pattern for initializing classes in PHP. Avoiding the
|
||||
* problems related to this approach is however a non-trivial task. Usually,
|
||||
* both classes and subclasses should be able to set default option values.
|
||||
* These default options should be overridden by the options passed to the
|
||||
* constructor. Last but not least, the (default) values of some options may
|
||||
* depend on the values of other options, which themselves may depend on other
|
||||
* options and so on.
|
||||
*
|
||||
* DefaultOptions resolves these problems. It allows you to:
|
||||
*
|
||||
* - Define default option values
|
||||
* - Define options in layers that correspond to your class hierarchy. Each
|
||||
* layer may depend on the default value set in the higher layers.
|
||||
* - Define default values for options that depend on the <em>concrete</em>
|
||||
* values of other options.
|
||||
* - Resolve the concrete option values by passing the options set by the
|
||||
* user.
|
||||
*
|
||||
* You can use it in your classes by implementing the following pattern:
|
||||
*
|
||||
* <code>
|
||||
* class Car
|
||||
* {
|
||||
* protected $options;
|
||||
*
|
||||
* public function __construct(array $options)
|
||||
* {
|
||||
* $defaultOptions = new DefaultOptions();
|
||||
* $this->addDefaultOptions($defaultOptions);
|
||||
*
|
||||
* $this->options = $defaultOptions->resolve($options);
|
||||
* }
|
||||
*
|
||||
* protected function addDefaultOptions(DefaultOptions $options)
|
||||
* {
|
||||
* $options->add(array(
|
||||
* 'make' => 'VW',
|
||||
* 'year' => '1999',
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $car = new Car(array(
|
||||
* 'make' => 'Mercedes',
|
||||
* 'year' => 2005,
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* By calling add(), new default options are added to the container. The method
|
||||
* resolve() accepts an array of options passed by the user that are matched
|
||||
* against the defined options. If any option is not recognized, an exception
|
||||
* is thrown. Finally, resolve() returns the merged default and user options.
|
||||
*
|
||||
* You can now easily add or override options in subclasses:
|
||||
*
|
||||
* <code>
|
||||
* class Renault extends Car
|
||||
* {
|
||||
* protected function addDefaultOptions(DefaultOptions $options)
|
||||
* {
|
||||
* parent::addDefaultOptions($options);
|
||||
*
|
||||
* $options->add(array(
|
||||
* 'make' => 'Renault',
|
||||
* 'gear' => 'auto',
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $renault = new Renault(array(
|
||||
* 'year' => 1997,
|
||||
* 'gear' => 'manual'
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* IMPORTANT: parent::addDefaultOptions() must always be called before adding
|
||||
* new default options!
|
||||
*
|
||||
* In the previous example, it makes sense to restrict the option "gear" to
|
||||
* a set of allowed values:
|
||||
*
|
||||
* <code>
|
||||
* class Renault extends Car
|
||||
* {
|
||||
* protected function addDefaultOptions(DefaultOptions $options)
|
||||
* {
|
||||
* // ... like above ...
|
||||
*
|
||||
* $options->addAllowedValues(array(
|
||||
* 'gear' => array('auto', 'manual'),
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Fails!
|
||||
* $renault = new Renault(array(
|
||||
* 'gear' => 'v6',
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* Now it is impossible to pass a value in the "gear" option that is not
|
||||
* expected.
|
||||
*
|
||||
* Last but not least, you can define options that depend on other options.
|
||||
* For example, depending on the "make" you could preset the country that the
|
||||
* car is registered in.
|
||||
*
|
||||
* <code>
|
||||
* class Car
|
||||
* {
|
||||
* protected function addDefaultOptions(DefaultOptions $options)
|
||||
* {
|
||||
* $options->add(array(
|
||||
* 'make' => 'VW',
|
||||
* 'year' => '1999',
|
||||
* 'country' => function (Options $options) {
|
||||
* if ('VW' === $options['make']) {
|
||||
* return 'DE';
|
||||
* }
|
||||
*
|
||||
* return null;
|
||||
* },
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $car = new Car(array(
|
||||
* 'make' => 'VW', // => "country" is "DE"
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* The closure receives as its first parameter a container of class Options
|
||||
* that contains the <em>concrete</em> options determined upon resolving. The
|
||||
* closure is executed once resolve() is called.
|
||||
*
|
||||
* The closure also receives a second parameter $previousValue that contains the
|
||||
* value defined by the parent layer of the hierarchy. If the option has not
|
||||
* been defined in any parent layer, the second parameter is NULL.
|
||||
*
|
||||
* <code>
|
||||
* class Renault extends Car
|
||||
* {
|
||||
* protected function addDefaultOptions(DefaultOptions $options)
|
||||
* {
|
||||
* $options->add(array(
|
||||
* 'country' => function (Options $options, $previousValue) {
|
||||
* if ('Renault' === $options['make']) {
|
||||
* return 'FR';
|
||||
* }
|
||||
*
|
||||
* // return default value defined in Car
|
||||
* return $previousValue;
|
||||
* },
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $renault = new Renault(array(
|
||||
* 'make' => 'VW', // => "country" is still "DE"
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DefaultOptions
|
||||
{
|
||||
/**
|
||||
* The container resolving the options.
|
||||
* @var Options
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* A list of accepted values for each option.
|
||||
* @var array
|
||||
*/
|
||||
private $allowedValues = array();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->options = new Options();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default options.
|
||||
*
|
||||
* @param array $options A list of option names as keys and option values
|
||||
* as values. The option values may be closures
|
||||
* of the following signatures:
|
||||
*
|
||||
* - function (Options $options)
|
||||
* - function (Options $options, $previousValue)
|
||||
*/
|
||||
public function add(array $options)
|
||||
{
|
||||
foreach ($options as $option => $value) {
|
||||
$this->options[$option] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds 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.
|
||||
*
|
||||
* @throws InvalidOptionException If an option has not been defined for
|
||||
* which an allowed value is set.
|
||||
*/
|
||||
public function addAllowedValues(array $allowedValues)
|
||||
{
|
||||
$this->validateOptionNames(array_keys($allowedValues));
|
||||
|
||||
$this->allowedValues = array_merge_recursive($this->allowedValues, $allowedValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the final option values by merging default options with user
|
||||
* options.
|
||||
*
|
||||
* @param array $userOptions The options passed by the user.
|
||||
*
|
||||
* @return array A list of options and their final values.
|
||||
*
|
||||
* @throws InvalidOptionException If any of the passed options has not
|
||||
* been defined or does not contain an
|
||||
* allowed value.
|
||||
* @throws OptionDefinitionException If a cyclic dependency is detected
|
||||
* between option closures.
|
||||
*/
|
||||
public function resolve(array $userOptions)
|
||||
{
|
||||
// Make sure this method can be called multiple times
|
||||
$options = clone $this->options;
|
||||
|
||||
$this->validateOptionNames(array_keys($userOptions));
|
||||
|
||||
// Override options set by the user
|
||||
foreach ($userOptions as $option => $value) {
|
||||
$options[$option] = $value;
|
||||
}
|
||||
|
||||
// Resolve options
|
||||
$options = iterator_to_array($options);
|
||||
|
||||
// Validate against allowed values
|
||||
$this->validateOptionValues($options);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given option names exist and throws an exception
|
||||
* otherwise.
|
||||
*
|
||||
* @param array $optionNames A list of option names.
|
||||
*
|
||||
* @throws InvalidOptionException If any of the options has not been
|
||||
* defined.
|
||||
*/
|
||||
private function validateOptionNames(array $optionNames)
|
||||
{
|
||||
$knownOptions = $this->options->getNames();
|
||||
$diff = array_diff($optionNames, $knownOptions);
|
||||
|
||||
if (count($diff) > 0) {
|
||||
sort($knownOptions);
|
||||
sort($diff);
|
||||
}
|
||||
|
||||
if (count($diff) > 1) {
|
||||
throw new InvalidOptionException(sprintf('The options "%s" do not exist. Known options are: "%s"', implode('", "', $diff), implode('", "', $knownOptions)));
|
||||
}
|
||||
|
||||
if (count($diff) > 0) {
|
||||
throw new InvalidOptionException(sprintf('The option "%s" does not exist. Known options are: "%s"', current($diff), implode('", "', $knownOptions)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given option values match the allowed values and
|
||||
* throws an exception otherwise.
|
||||
*
|
||||
* @param array $options A list of option values.
|
||||
*
|
||||
* @throws InvalidOptionException 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 (!in_array($options[$option], $allowedValues, true)) {
|
||||
throw new InvalidOptionException(sprintf('The option "%s" has the value "%s", but is expected to be one of "%s"', $option, $options[$option], implode('", "', $allowedValues)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Exception;
|
||||
|
||||
class OptionDefinitionException extends FormException
|
||||
{
|
||||
}
|
@ -12,7 +12,6 @@
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Options;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
@ -27,6 +26,7 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransform
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToBooleanArrayTransformer;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class ChoiceType extends AbstractType
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransfo
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer;
|
||||
use Symfony\Component\Form\Options;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class DateTimeType extends AbstractType
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransfo
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
||||
use Symfony\Component\Form\ReversedTransformer;
|
||||
use Symfony\Component\Form\Options;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class DateType extends AbstractType
|
||||
{
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Deprecated. You should extend FormType instead.
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Options;
|
||||
use Symfony\Component\Form\Util\PropertyPath;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
@ -23,6 +22,7 @@ use Symfony\Component\Form\Extension\Core\EventListener\ValidationListener;
|
||||
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class FormType extends AbstractType
|
||||
{
|
||||
|
@ -20,7 +20,7 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransf
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\Options;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class TimeType extends AbstractType
|
||||
{
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class TimezoneType extends AbstractType
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Form;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\Exception\TypeDefinitionException;
|
||||
use Symfony\Component\OptionsParser\OptionsParser;
|
||||
|
||||
class FormFactory implements FormFactoryInterface
|
||||
{
|
||||
@ -220,7 +221,7 @@ class FormFactory implements FormFactoryInterface
|
||||
$types = array();
|
||||
$optionValues = array();
|
||||
$knownOptions = array();
|
||||
$defaultOptions = new DefaultOptions();
|
||||
$optionsParser = new OptionsParser();
|
||||
|
||||
// Bottom-up determination of the type hierarchy
|
||||
// Start with the actual type and look for the parent type
|
||||
@ -254,14 +255,14 @@ class FormFactory implements FormFactoryInterface
|
||||
// options. Default options of children override default options
|
||||
// of parents.
|
||||
$typeOptions = $type->getDefaultOptions();
|
||||
$defaultOptions->add($typeOptions);
|
||||
$defaultOptions->addAllowedValues($type->getAllowedOptionValues());
|
||||
$optionsParser->setDefaults($typeOptions);
|
||||
$optionsParser->addAllowedValues($type->getAllowedOptionValues());
|
||||
$knownOptions = array_merge($knownOptions, array_keys($typeOptions));
|
||||
|
||||
foreach ($type->getExtensions() as $typeExtension) {
|
||||
$extensionOptions = $typeExtension->getDefaultOptions();
|
||||
$defaultOptions->add($extensionOptions);
|
||||
$defaultOptions->addAllowedValues($typeExtension->getAllowedOptionValues());
|
||||
$optionsParser->setDefaults($extensionOptions);
|
||||
$optionsParser->addAllowedValues($typeExtension->getAllowedOptionValues());
|
||||
$knownOptions = array_merge($knownOptions, array_keys($extensionOptions));
|
||||
}
|
||||
}
|
||||
@ -277,7 +278,7 @@ class FormFactory implements FormFactoryInterface
|
||||
}
|
||||
|
||||
// Resolve options
|
||||
$options = $defaultOptions->resolve($options);
|
||||
$options = $optionsParser->parse($options);
|
||||
|
||||
for ($i = 0, $l = count($types); $i < $l && !$builder; ++$i) {
|
||||
$builder = $types[$i]->createBuilder($name, $this, $options);
|
||||
|
@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Tests;
|
||||
|
||||
use Symfony\Component\Form\DefaultOptions;
|
||||
use Symfony\Component\Form\Options;
|
||||
|
||||
class DefaultOptionsTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $options;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->options = new DefaultOptions();
|
||||
}
|
||||
|
||||
public function testResolve()
|
||||
{
|
||||
$this->options->add(array(
|
||||
'foo' => 'bar',
|
||||
'bam' => function (Options $options) {
|
||||
return 'baz';
|
||||
},
|
||||
));
|
||||
|
||||
$result = array(
|
||||
'foo' => 'fee',
|
||||
'bam' => 'baz',
|
||||
);
|
||||
|
||||
$this->assertEquals($result, $this->options->resolve(array(
|
||||
'foo' => 'fee',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\InvalidOptionException
|
||||
*/
|
||||
public function testResolveFailsIfNonExistingOption()
|
||||
{
|
||||
$this->options->add(array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
|
||||
$this->options->resolve(array(
|
||||
'non_existing' => 'option',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\InvalidOptionException
|
||||
*/
|
||||
public function testResolveFailsIfOptionValueNotAllowed()
|
||||
{
|
||||
$this->options->add(array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
|
||||
$this->options->addAllowedValues(array(
|
||||
'foo' => array('bar', 'baz'),
|
||||
));
|
||||
|
||||
$this->options->resolve(array(
|
||||
'foo' => 'bam',
|
||||
));
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ class DateTypeTest extends LocalizedTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\FormException
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testInvalidWidgetOption()
|
||||
{
|
||||
@ -33,7 +33,7 @@ class DateTypeTest extends LocalizedTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\FormException
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testInvalidInputOption()
|
||||
{
|
||||
|
@ -279,7 +279,7 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\InvalidOptionException
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testCreateNamedBuilderExpectsOptionsToExist()
|
||||
{
|
||||
@ -292,7 +292,7 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\InvalidOptionException
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testCreateNamedBuilderExpectsOptionsToBeInValidRange()
|
||||
{
|
||||
@ -608,7 +608,6 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($parentBuilder, $builder->getParent());
|
||||
}
|
||||
|
||||
|
||||
public function testFormTypeCreatesDefaultValueForEmptyDataOption()
|
||||
{
|
||||
$factory = new FormFactory(array(new \Symfony\Component\Form\Extension\Core\CoreExtension()));
|
||||
|
@ -18,7 +18,8 @@
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/event-dispatcher": "2.1.*",
|
||||
"symfony/locale": "2.1.*"
|
||||
"symfony/locale": "2.1.*",
|
||||
"symfony/options-parser": "2.1.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/validator": "2.1.*",
|
||||
|
@ -9,8 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Exception;
|
||||
namespace Symfony\Component\OptionsParser\Exception;
|
||||
|
||||
class InvalidOptionException extends FormException
|
||||
/**
|
||||
* Marker interface for the Options component.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
@ -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\OptionsParser\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when an invalid option is passed.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class InvalidOptionsException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
@ -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\OptionsParser\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a required option is missing.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class MissingOptionsException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
@ -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\OptionsParser\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when an option definition is invalid.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class OptionDefinitionException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
19
src/Symfony/Component/OptionsParser/LICENSE
Normal file
19
src/Symfony/Component/OptionsParser/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2004-2012 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.
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form;
|
||||
namespace Symfony\Component\OptionsParser;
|
||||
|
||||
use Closure;
|
||||
|
||||
@ -17,8 +17,6 @@ use Closure;
|
||||
* An option that is evaluated lazily using a closure.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @see DefaultOptions
|
||||
*/
|
||||
class LazyOption
|
||||
{
|
@ -9,164 +9,16 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form;
|
||||
namespace Symfony\Component\OptionsParser;
|
||||
|
||||
use ArrayAccess;
|
||||
use Iterator;
|
||||
use OutOfBoundsException;
|
||||
use Symfony\Component\Form\Exception\OptionDefinitionException;
|
||||
use Symfony\Component\OptionsParser\Exception\OptionDefinitionException;
|
||||
|
||||
/**
|
||||
* Container for resolving inter-dependent options.
|
||||
*
|
||||
* Options are a common pattern for resolved classes in PHP. Avoiding the
|
||||
* problems related to this approach is however a non-trivial task. Usually,
|
||||
* both classes and subclasses should be able to set default option values.
|
||||
* These default options should be overridden by the options passed to the
|
||||
* constructor. Last but not least, the (default) values of some options may
|
||||
* depend on the values of other options, which themselves may depend on other
|
||||
* options.
|
||||
*
|
||||
* This class resolves these problems. You can use it in your classes by
|
||||
* implementing the following pattern:
|
||||
*
|
||||
* <code>
|
||||
* class Car
|
||||
* {
|
||||
* protected $options;
|
||||
*
|
||||
* public function __construct(array $options)
|
||||
* {
|
||||
* $_options = new Options();
|
||||
* $this->addDefaultOptions($_options);
|
||||
*
|
||||
* $this->options = $_options->resolve($options);
|
||||
* }
|
||||
*
|
||||
* protected function addDefaultOptions(Options $options)
|
||||
* {
|
||||
* $options->add(array(
|
||||
* 'make' => 'VW',
|
||||
* 'year' => '1999',
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $car = new Car(array(
|
||||
* 'make' => 'Mercedes',
|
||||
* 'year' => 2005,
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* By calling add(), new default options are added to the container. The method
|
||||
* resolve() accepts an array of options passed by the user that are matched
|
||||
* against the allowed options. If any option is not recognized, an exception
|
||||
* is thrown. Finally, resolve() returns the merged default and user options.
|
||||
*
|
||||
* You can now easily add or override options in subclasses:
|
||||
*
|
||||
* <code>
|
||||
* class Renault extends Car
|
||||
* {
|
||||
* protected function addDefaultOptions(Options $options)
|
||||
* {
|
||||
* parent::addDefaultOptions($options);
|
||||
*
|
||||
* $options->add(array(
|
||||
* 'make' => 'Renault',
|
||||
* 'gear' => 'auto',
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $renault = new Renault(array(
|
||||
* 'year' => 1997,
|
||||
* 'gear' => 'manual'
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* IMPORTANT: parent::addDefaultOptions() must always be called before adding
|
||||
* new options!
|
||||
*
|
||||
* In the previous example, it makes sense to restrict the option "gear" to
|
||||
* a set of allowed values:
|
||||
*
|
||||
* <code>
|
||||
* class Renault extends Car
|
||||
* {
|
||||
* protected function addDefaultOptions(Options $options)
|
||||
* {
|
||||
* // ... like above ...
|
||||
*
|
||||
* $options->addAllowedValues(array(
|
||||
* 'gear' => array('auto', 'manual'),
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Fails!
|
||||
* $renault = new Renault(array(
|
||||
* 'gear' => 'v6',
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* Now it is impossible to pass a value in the "gear" option that is not
|
||||
* expected.
|
||||
*
|
||||
* Last but not least, you can define options that depend on other options.
|
||||
* For example, depending on the "make" you could preset the country that the
|
||||
* car is registered in.
|
||||
*
|
||||
* <code>
|
||||
* class Car
|
||||
* {
|
||||
* protected function addDefaultOptions(Options $options)
|
||||
* {
|
||||
* $options->add(array(
|
||||
* 'make' => 'VW',
|
||||
* 'year' => '1999',
|
||||
* 'country' => function (Options $options) {
|
||||
* if ('VW' === $options['make']) {
|
||||
* return 'DE';
|
||||
* }
|
||||
*
|
||||
* return null;
|
||||
* },
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $car = new Car(array(
|
||||
* 'make' => 'VW', // => "country" is "DE"
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* When overriding an option with a closure in subclasses, you can make use of
|
||||
* the second parameter $parentValue in which the value defined by the parent
|
||||
* class is stored.
|
||||
*
|
||||
* <code>
|
||||
* class Renault extends Car
|
||||
* {
|
||||
* protected function addDefaultOptions(Options $options)
|
||||
* {
|
||||
* $options->add(array(
|
||||
* 'country' => function (Options $options, $parentValue) {
|
||||
* if ('Renault' === $options['make']) {
|
||||
* return 'FR';
|
||||
* }
|
||||
*
|
||||
* return $parentValue;
|
||||
* },
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $renault = new Renault(array(
|
||||
* 'make' => 'VW', // => "country" is still "DE"
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class Options implements ArrayAccess, Iterator
|
||||
@ -312,16 +164,6 @@ class Options implements ArrayAccess, Iterator
|
||||
unset($this->lock[$option]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all defined options.
|
||||
*
|
||||
* @return array An array of option names.
|
||||
*/
|
||||
public function getNames()
|
||||
{
|
||||
return array_keys($this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Iterator::current()
|
||||
*/
|
249
src/Symfony/Component/OptionsParser/OptionsParser.php
Normal file
249
src/Symfony/Component/OptionsParser/OptionsParser.php
Normal file
@ -0,0 +1,249 @@
|
||||
<?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\OptionsParser;
|
||||
|
||||
use Symfony\Component\OptionsParser\Exception\OptionDefinitionException;
|
||||
use Symfony\Component\OptionsParser\Exception\InvalidOptionsException;
|
||||
use Symfony\Component\OptionsParser\Exception\MissingOptionsException;
|
||||
|
||||
/**
|
||||
* Helper for merging default and concrete option values.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class OptionsParser
|
||||
{
|
||||
/**
|
||||
* The default option values.
|
||||
* @var Options
|
||||
*/
|
||||
private $defaultOptions;
|
||||
|
||||
/**
|
||||
* The options known by the parser.
|
||||
* @var array
|
||||
*/
|
||||
private $knownOptions = array();
|
||||
|
||||
/**
|
||||
* The options required to be passed to parse().
|
||||
* @var array
|
||||
*/
|
||||
private $requiredOptions = array();
|
||||
|
||||
/**
|
||||
* A list of accepted values for each option.
|
||||
* @var array
|
||||
*/
|
||||
private $allowedValues = array();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->defaultOptions = new Options();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default option values.
|
||||
*
|
||||
* @param array $options A list of option names as keys and default values
|
||||
* as values. The option values may be closures
|
||||
* of the following signatures:
|
||||
*
|
||||
* - function (Options $options)
|
||||
* - function (Options $options, $previousValue)
|
||||
*/
|
||||
public function setDefaults(array $defaultValues)
|
||||
{
|
||||
foreach ($defaultValues as $option => $value) {
|
||||
$this->defaultOptions[$option] = $value;
|
||||
$this->knownOptions[$option] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets optional options.
|
||||
*
|
||||
* This method is identical to `setDefaults`, only that no default values
|
||||
* are configured for the options. If these options are not passed to
|
||||
* parse(), 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.
|
||||
*
|
||||
* @param array $optionNames A list of option names.
|
||||
*
|
||||
* @throws OptionDefinitionException When trying to pass default values.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets required options.
|
||||
*
|
||||
* If these options are not passed to parse(), an exception will be thrown.
|
||||
*
|
||||
* @param array $optionNames A list of option names.
|
||||
*
|
||||
* @throws OptionDefinitionException When trying to pass default values.
|
||||
*/
|
||||
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;
|
||||
$this->requiredOptions[$option] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @throws InvalidOptionsException If an option has not been defined for
|
||||
* which an allowed value is set.
|
||||
*/
|
||||
public function setAllowedValues(array $allowedValues)
|
||||
{
|
||||
$this->validateOptionNames(array_keys($allowedValues));
|
||||
|
||||
$this->allowedValues = array_replace($this->allowedValues, $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.
|
||||
*
|
||||
* @throws InvalidOptionsException If an option has not been defined for
|
||||
* which an allowed value is set.
|
||||
*/
|
||||
public function addAllowedValues(array $allowedValues)
|
||||
{
|
||||
$this->validateOptionNames(array_keys($allowedValues));
|
||||
|
||||
$this->allowedValues = array_merge_recursive($this->allowedValues, $allowedValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 InvalidOptionsException If any of the passed options has not
|
||||
* been defined or does not contain an
|
||||
* allowed value.
|
||||
* @throws MissingOptionsException If a required option is missing.
|
||||
* @throws OptionDefinitionException If a cyclic dependency is detected
|
||||
* between two lazy options.
|
||||
*/
|
||||
public function parse(array $options)
|
||||
{
|
||||
$this->validateOptionNames(array_keys($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[$option] = $value;
|
||||
}
|
||||
|
||||
// Resolve options
|
||||
$combinedOptions = iterator_to_array($combinedOptions);
|
||||
|
||||
// Validate against allowed values
|
||||
$this->validateOptionValues($combinedOptions);
|
||||
|
||||
return $combinedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given option names exist and throws an exception
|
||||
* otherwise.
|
||||
*
|
||||
* @param array $optionNames A list of option names.
|
||||
*
|
||||
* @throws InvalidOptionsException If any of the options has not been
|
||||
* defined.
|
||||
*/
|
||||
private function validateOptionNames(array $optionNames)
|
||||
{
|
||||
ksort($this->knownOptions);
|
||||
|
||||
$knownOptions = array_keys($this->knownOptions);
|
||||
$diff = array_diff($optionNames, $knownOptions);
|
||||
|
||||
sort($diff);
|
||||
|
||||
if (count($diff) > 1) {
|
||||
throw new InvalidOptionsException(sprintf('The options "%s" do not exist. Known options are: "%s"', implode('", "', $diff), implode('", "', $knownOptions)));
|
||||
}
|
||||
|
||||
if (count($diff) > 0) {
|
||||
throw new InvalidOptionsException(sprintf('The option "%s" does not exist. Known options are: "%s"', current($diff), implode('", "', $knownOptions)));
|
||||
}
|
||||
|
||||
ksort($this->requiredOptions);
|
||||
|
||||
$requiredOptions = array_keys($this->requiredOptions);
|
||||
$diff = array_diff($requiredOptions, $optionNames);
|
||||
|
||||
sort($diff);
|
||||
|
||||
if (count($diff) > 1) {
|
||||
throw new MissingOptionsException(sprintf('The options "%s" are missing.', implode('", "', $diff)));
|
||||
}
|
||||
|
||||
if (count($diff) > 0) {
|
||||
throw new MissingOptionsException(sprintf('The option "%s" is missing.', current($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 (!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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
103
src/Symfony/Component/OptionsParser/README.md
Normal file
103
src/Symfony/Component/OptionsParser/README.md
Normal file
@ -0,0 +1,103 @@
|
||||
OptionsParser Component
|
||||
======================
|
||||
|
||||
OptionsParser helps to configure objects with option arrays.
|
||||
|
||||
It supports default values on different levels of your class hierarchy,
|
||||
required options and lazy options where the default value depends on the
|
||||
concrete value of a different 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.
|
||||
|
||||
use Symfony\Component\OptionsParser\OptionsParser;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class Person
|
||||
{
|
||||
protected $options;
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$parser = new OptionsParser();
|
||||
$this->setOptions($parser);
|
||||
|
||||
$this->options = $parser->parse($options);
|
||||
}
|
||||
|
||||
protected function setOptions(OptionsParser $parser)
|
||||
{
|
||||
$parser->setRequired(array(
|
||||
'firstName',
|
||||
'lastName',
|
||||
'age',
|
||||
));
|
||||
|
||||
$parser->setDefaults(array(
|
||||
'age' => null,
|
||||
'gender' => function (Options $options) {
|
||||
if (self::isKnownMaleName($options['firstName'])) {
|
||||
return 'male';
|
||||
}
|
||||
|
||||
return 'female';
|
||||
},
|
||||
));
|
||||
|
||||
$parser->setAllowedValues(array(
|
||||
'gender' => array('male', 'female'),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
We can now easily instantiate a Person object:
|
||||
|
||||
// 'gender' is implicitely 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 `setOptions`
|
||||
method:
|
||||
|
||||
use Symfony\Component\OptionsParser\OptionsParser;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class Employee extends Person
|
||||
{
|
||||
protected function setOptions(OptionsParser $parser)
|
||||
{
|
||||
parent::setOptions($parser);
|
||||
|
||||
$parser->setRequired(array(
|
||||
'birthDate',
|
||||
));
|
||||
|
||||
$parser->setDefaults(array(
|
||||
// $previousValue contains the default value configured in the
|
||||
// parent class
|
||||
'age' => function (Options $options, $previousValue) {
|
||||
return self::configureAgeFromBirthDate($options['birthDate']);
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
You can run the unit tests with the following command:
|
||||
|
||||
phpunit -c src/Symfony/Component/OptionsParser/
|
271
src/Symfony/Component/OptionsParser/Tests/OptionParserTest.php
Normal file
271
src/Symfony/Component/OptionsParser/Tests/OptionParserTest.php
Normal file
@ -0,0 +1,271 @@
|
||||
<?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\OptionsParser\Tests;
|
||||
|
||||
use Symfony\Component\OptionsParser\OptionsParser;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class OptionsParserTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $options;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->parser = new OptionsParser();
|
||||
}
|
||||
|
||||
public function testParse()
|
||||
{
|
||||
$this->parser->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'two' => '20',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => '20',
|
||||
), $this->parser->parse($options));
|
||||
}
|
||||
|
||||
public function testParseLazy()
|
||||
{
|
||||
$this->parser->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => function (Options $options) {
|
||||
return '20';
|
||||
},
|
||||
));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => '20',
|
||||
), $this->parser->parse(array()));
|
||||
}
|
||||
|
||||
public function testParseLazyDependencyOnOptional()
|
||||
{
|
||||
$this->parser->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => function (Options $options) {
|
||||
return $options['one'] . '2';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '10',
|
||||
'two' => '102',
|
||||
), $this->parser->parse($options));
|
||||
}
|
||||
|
||||
public function testParseLazyDependencyOnMissingOptionalWithoutDefault()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->parser->setOptional(array(
|
||||
'one',
|
||||
));
|
||||
|
||||
$this->parser->setDefaults(array(
|
||||
'two' => function (Options $options) use ($test) {
|
||||
$test->assertFalse(isset($options['one']));
|
||||
|
||||
return '2';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'two' => '2',
|
||||
), $this->parser->parse($options));
|
||||
}
|
||||
|
||||
public function testParseLazyDependencyOnOptionalWithoutDefault()
|
||||
{
|
||||
$test = $this;
|
||||
|
||||
$this->parser->setOptional(array(
|
||||
'one',
|
||||
));
|
||||
|
||||
$this->parser->setDefaults(array(
|
||||
'two' => function (Options $options) use ($test) {
|
||||
$test->assertTrue(isset($options['one']));
|
||||
|
||||
return $options['one'] . '2';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '10',
|
||||
'two' => '102',
|
||||
), $this->parser->parse($options));
|
||||
}
|
||||
|
||||
public function testParseLazyDependencyOnRequired()
|
||||
{
|
||||
$this->parser->setRequired(array(
|
||||
'one',
|
||||
));
|
||||
$this->parser->setDefaults(array(
|
||||
'two' => function (Options $options) {
|
||||
return $options['one'] . '2';
|
||||
},
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '10',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '10',
|
||||
'two' => '102',
|
||||
), $this->parser->parse($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testParseFailsIfNonExistingOption()
|
||||
{
|
||||
$this->parser->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->parser->setRequired(array(
|
||||
'two',
|
||||
));
|
||||
|
||||
$this->parser->setOptional(array(
|
||||
'three',
|
||||
));
|
||||
|
||||
$this->parser->parse(array(
|
||||
'foo' => 'bar',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\MissingOptionsException
|
||||
*/
|
||||
public function testParseFailsIfMissingRequiredOption()
|
||||
{
|
||||
$this->parser->setRequired(array(
|
||||
'one',
|
||||
));
|
||||
|
||||
$this->parser->setDefaults(array(
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$this->parser->parse(array(
|
||||
'two' => '20',
|
||||
));
|
||||
}
|
||||
|
||||
public function testParseSucceedsIfOptionValueAllowed()
|
||||
{
|
||||
$this->parser->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->parser->setAllowedValues(array(
|
||||
'one' => array('1', 'one'),
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => 'one',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => 'one',
|
||||
), $this->parser->parse($options));
|
||||
}
|
||||
|
||||
public function testParseSucceedsIfOptionValueAllowed2()
|
||||
{
|
||||
$this->parser->setDefaults(array(
|
||||
'one' => '1',
|
||||
'two' => '2',
|
||||
));
|
||||
|
||||
$this->parser->addAllowedValues(array(
|
||||
'one' => array('1'),
|
||||
'two' => array('2'),
|
||||
));
|
||||
$this->parser->addAllowedValues(array(
|
||||
'one' => array('one'),
|
||||
'two' => array('two'),
|
||||
));
|
||||
|
||||
$options = array(
|
||||
'one' => '1',
|
||||
'two' => 'two',
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'one' => '1',
|
||||
'two' => 'two',
|
||||
), $this->parser->parse($options));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testParseFailsIfOptionValueNotAllowed()
|
||||
{
|
||||
$this->parser->setDefaults(array(
|
||||
'one' => '1',
|
||||
));
|
||||
|
||||
$this->parser->setAllowedValues(array(
|
||||
'one' => array('1', 'one'),
|
||||
));
|
||||
|
||||
$this->parser->parse(array(
|
||||
'one' => '2',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\OptionDefinitionException
|
||||
*/
|
||||
public function testSetRequiredFailsIfDefaultIsPassed()
|
||||
{
|
||||
$this->parser->setRequired(array(
|
||||
'one' => '1',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\OptionDefinitionException
|
||||
*/
|
||||
public function testSetOptionalFailsIfDefaultIsPassed()
|
||||
{
|
||||
$this->parser->setOptional(array(
|
||||
'one' => '1',
|
||||
));
|
||||
}
|
||||
}
|
@ -9,9 +9,9 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Tests;
|
||||
namespace Symfony\Component\OptionsParser\Tests;
|
||||
|
||||
use Symfony\Component\Form\Options;
|
||||
use Symfony\Component\OptionsParser\Options;
|
||||
|
||||
class OptionsTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
@ -50,7 +50,7 @@ class OptionsTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\OptionDefinitionException
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\OptionDefinitionException
|
||||
*/
|
||||
public function testSetNotSupportedAfterGet()
|
||||
{
|
||||
@ -60,7 +60,7 @@ class OptionsTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\OptionDefinitionException
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\OptionDefinitionException
|
||||
*/
|
||||
public function testUnsetNotSupportedAfterGet()
|
||||
{
|
||||
@ -151,7 +151,7 @@ class OptionsTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\OptionDefinitionException
|
||||
* @expectedException Symfony\Component\OptionsParser\Exception\OptionDefinitionException
|
||||
*/
|
||||
public function testLazyOptionDisallowCyclicDependencies()
|
||||
{
|
18
src/Symfony/Component/OptionsParser/Tests/bootstrap.php
Normal file
18
src/Symfony/Component/OptionsParser/Tests/bootstrap.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
spl_autoload_register(function ($class) {
|
||||
if (0 === strpos(ltrim($class, '/'), 'Symfony\Component\OptionsParser')) {
|
||||
if (file_exists($file = __DIR__.'/../'.substr(str_replace('\\', '/', $class), strlen('Symfony\Component\OptionsParser')).'.php')) {
|
||||
require_once $file;
|
||||
}
|
||||
}
|
||||
});
|
30
src/Symfony/Component/OptionsParser/composer.json
Normal file
30
src/Symfony/Component/OptionsParser/composer.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "symfony/options-parser",
|
||||
"type": "library",
|
||||
"description": "Symfony OptionsParser Component",
|
||||
"keywords": [],
|
||||
"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.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Symfony\\Component\\OptionsParser": "" }
|
||||
},
|
||||
"target-dir": "Symfony/Component/OptionsParser",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1-dev"
|
||||
}
|
||||
}
|
||||
}
|
29
src/Symfony/Component/OptionsParser/phpunit.xml.dist
Normal file
29
src/Symfony/Component/OptionsParser/phpunit.xml.dist
Normal 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="Tests/bootstrap.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Symfony OptionsParser Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Resources</directory>
|
||||
<directory>./Tests</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
Reference in New Issue
Block a user