diff --git a/src/Symfony/Component/Form/Command/DebugCommand.php b/src/Symfony/Component/Form/Command/DebugCommand.php
index d8e1f5a0789b..68f3bc96f100 100644
--- a/src/Symfony/Component/Form/Command/DebugCommand.php
+++ b/src/Symfony/Component/Form/Command/DebugCommand.php
@@ -19,7 +19,9 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Form\Console\Helper\DescriptorHelper;
+use Symfony\Component\Form\Extension\Core\CoreExtension;
use Symfony\Component\Form\FormRegistryInterface;
+use Symfony\Component\Form\FormTypeInterface;
/**
* A console command for retrieving information about form types.
@@ -55,6 +57,7 @@ protected function configure()
$this
->setDefinition(array(
new InputArgument('class', InputArgument::OPTIONAL, 'The form type class'),
+ new InputArgument('option', InputArgument::OPTIONAL, 'The form type option'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt or json)', 'txt'),
))
->setDescription('Displays form type information')
@@ -70,6 +73,10 @@ protected function configure()
The command lists all defined options that contains the given form type, as well as their parents and type extensions.
+ php %command.full_name% ChoiceType choice_value
+
+The command displays the definition of the given option name.
+
php %command.full_name% --format=json
The command lists everything in a machine readable json format.
@@ -87,14 +94,42 @@ protected function execute(InputInterface $input, OutputInterface $output)
if (null === $class = $input->getArgument('class')) {
$object = null;
- $options['types'] = $this->types;
+ $options['core_types'] = $this->getCoreTypes();
+ $options['service_types'] = array_values(array_diff($this->types, $options['core_types']));
$options['extensions'] = $this->extensions;
$options['guessers'] = $this->guessers;
+ foreach ($options as $k => $list) {
+ sort($options[$k]);
+ }
} else {
if (!class_exists($class)) {
$class = $this->getFqcnTypeClass($input, $io, $class);
}
- $object = $this->formRegistry->getType($class);
+ $resolvedType = $this->formRegistry->getType($class);
+
+ if ($option = $input->getArgument('option')) {
+ $object = $resolvedType->getOptionsResolver();
+
+ if (!$object->isDefined($option)) {
+ $message = sprintf('Option "%s" is not defined in "%s".', $option, get_class($resolvedType->getInnerType()));
+
+ if ($alternatives = $this->findAlternatives($option, $object->getDefinedOptions())) {
+ if (1 == count($alternatives)) {
+ $message .= "\n\nDid you mean this?\n ";
+ } else {
+ $message .= "\n\nDid you mean one of these?\n ";
+ }
+ $message .= implode("\n ", $alternatives);
+ }
+
+ throw new InvalidArgumentException($message);
+ }
+
+ $options['type'] = $resolvedType->getInnerType();
+ $options['option'] = $option;
+ } else {
+ $object = $resolvedType;
+ }
}
$helper = new DescriptorHelper();
@@ -105,6 +140,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, $shortClassName)
{
$classes = array();
+ sort($this->namespaces);
foreach ($this->namespaces as $namespace) {
if (class_exists($fqcn = $namespace.'\\'.$shortClassName)) {
$classes[] = $fqcn;
@@ -112,7 +148,19 @@ private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, $shor
}
if (0 === $count = count($classes)) {
- throw new InvalidArgumentException(sprintf("Could not find type \"%s\" into the following namespaces:\n %s", $shortClassName, implode("\n ", $this->namespaces)));
+ $message = sprintf("Could not find type \"%s\" into the following namespaces:\n %s", $shortClassName, implode("\n ", $this->namespaces));
+
+ $allTypes = array_merge($this->getCoreTypes(), $this->types);
+ if ($alternatives = $this->findAlternatives($shortClassName, $allTypes)) {
+ if (1 == count($alternatives)) {
+ $message .= "\n\nDid you mean this?\n ";
+ } else {
+ $message .= "\n\nDid you mean one of these?\n ";
+ }
+ $message .= implode("\n ", $alternatives);
+ }
+
+ throw new InvalidArgumentException($message);
}
if (1 === $count) {
return $classes[0];
@@ -121,6 +169,35 @@ private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, $shor
throw new InvalidArgumentException(sprintf("The type \"%s\" is ambiguous.\n\nDid you mean one of these?\n %s", $shortClassName, implode("\n ", $classes)));
}
- return $io->choice(sprintf("The type \"%s\" is ambiguous.\n\n Select one of the following form types to display its information:", $shortClassName), $classes, $classes[0]);
+ return $io->choice(sprintf("The type \"%s\" is ambiguous.\n\nSelect one of the following form types to display its information:", $shortClassName), $classes, $classes[0]);
+ }
+
+ private function getCoreTypes()
+ {
+ $coreExtension = new CoreExtension();
+ $loadTypesRefMethod = (new \ReflectionObject($coreExtension))->getMethod('loadTypes');
+ $loadTypesRefMethod->setAccessible(true);
+ $coreTypes = $loadTypesRefMethod->invoke($coreExtension);
+ $coreTypes = array_map(function (FormTypeInterface $type) { return get_class($type); }, $coreTypes);
+ sort($coreTypes);
+
+ return $coreTypes;
+ }
+
+ private function findAlternatives($name, array $collection)
+ {
+ $alternatives = array();
+ foreach ($collection as $item) {
+ $lev = levenshtein($name, $item);
+ if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
+ $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
+ }
+ }
+
+ $threshold = 1e3;
+ $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
+ ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE);
+
+ return array_keys($alternatives);
}
}
diff --git a/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php b/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
index c72a19d7993f..6cccd8ead235 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
@@ -14,12 +14,12 @@
use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Style\StyleInterface;
+use Symfony\Component\Console\Style\OutputStyle;
use Symfony\Component\Console\Style\SymfonyStyle;
-use Symfony\Component\Form\Extension\Core\CoreExtension;
-use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\Form\ResolvedFormTypeInterface;
use Symfony\Component\Form\Util\OptionsResolverWrapper;
+use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
+use Symfony\Component\OptionsResolver\Exception\NoConfigurationException;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
@@ -29,7 +29,7 @@
*/
abstract class Descriptor implements DescriptorInterface
{
- /** @var StyleInterface */
+ /** @var OutputStyle */
protected $output;
protected $type;
protected $ownOptions = array();
@@ -45,7 +45,7 @@ abstract class Descriptor implements DescriptorInterface
*/
public function describe(OutputInterface $output, $object, array $options = array())
{
- $this->output = $output instanceof StyleInterface ? $output : new SymfonyStyle(new ArrayInput(array()), $output);
+ $this->output = $output instanceof OutputStyle ? $output : new SymfonyStyle(new ArrayInput(array()), $output);
switch (true) {
case null === $object:
@@ -54,28 +54,19 @@ public function describe(OutputInterface $output, $object, array $options = arra
case $object instanceof ResolvedFormTypeInterface:
$this->describeResolvedFormType($object, $options);
break;
+ case $object instanceof OptionsResolver:
+ $this->describeOption($object, $options);
+ break;
default:
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
}
}
- abstract protected function describeDefaults(array $options = array());
+ abstract protected function describeDefaults(array $options);
abstract protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedFormType, array $options = array());
- protected function getCoreTypes()
- {
- $coreExtension = new CoreExtension();
- $coreExtensionRefObject = new \ReflectionObject($coreExtension);
- $loadTypesRefMethod = $coreExtensionRefObject->getMethod('loadTypes');
- $loadTypesRefMethod->setAccessible(true);
- $coreTypes = $loadTypesRefMethod->invoke($coreExtension);
-
- $coreTypes = array_map(function (FormTypeInterface $type) { return get_class($type); }, $coreTypes);
- sort($coreTypes);
-
- return $coreTypes;
- }
+ abstract protected function describeOption(OptionsResolver $optionsResolver, array $options);
protected function collectOptions(ResolvedFormTypeInterface $type)
{
@@ -113,6 +104,31 @@ protected function collectOptions(ResolvedFormTypeInterface $type)
$this->extensions = array_keys($this->extensions);
}
+ protected function getOptionDefinition(OptionsResolver $optionsResolver, $option)
+ {
+ $definition = array('required' => $optionsResolver->isRequired($option));
+
+ $introspector = new OptionsResolverIntrospector($optionsResolver);
+
+ $map = array(
+ 'default' => 'getDefault',
+ 'lazy' => 'getLazyClosures',
+ 'allowedTypes' => 'getAllowedTypes',
+ 'allowedValues' => 'getAllowedValues',
+ 'normalizer' => 'getNormalizer',
+ );
+
+ foreach ($map as $key => $method) {
+ try {
+ $definition[$key] = $introspector->{$method}($option);
+ } catch (NoConfigurationException $e) {
+ // noop
+ }
+ }
+
+ return $definition;
+ }
+
private function getParentOptionsResolver(ResolvedFormTypeInterface $type)
{
$this->parents[$class = get_class($type->getInnerType())] = array();
diff --git a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
index 7616d616f144..9d02aba3c12a 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Console\Descriptor;
use Symfony\Component\Form\ResolvedFormTypeInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* @author Yonel Ceruto
@@ -20,10 +21,10 @@
*/
class JsonDescriptor extends Descriptor
{
- protected function describeDefaults(array $options = array())
+ protected function describeDefaults(array $options)
{
- $data['builtin_form_types'] = $this->getCoreTypes();
- $data['service_form_types'] = array_values(array_diff($options['types'], $data['builtin_form_types']));
+ $data['builtin_form_types'] = $options['core_types'];
+ $data['service_form_types'] = $options['service_types'];
$data['type_extensions'] = $options['extensions'];
$data['type_guessers'] = $options['guessers'];
@@ -54,6 +55,30 @@ protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedF
$this->writeData($data, $options);
}
+ protected function describeOption(OptionsResolver $optionsResolver, array $options)
+ {
+ $definition = $this->getOptionDefinition($optionsResolver, $options['option']);
+
+ $map = array(
+ 'required' => 'required',
+ 'default' => 'default',
+ 'allowed_types' => 'allowedTypes',
+ 'allowed_values' => 'allowedValues',
+ );
+ foreach ($map as $label => $name) {
+ if (array_key_exists($name, $definition)) {
+ $data[$label] = $definition[$name];
+
+ if ('default' === $name) {
+ $data['is_lazy'] = isset($definition['lazy']);
+ }
+ }
+ }
+ $data['has_normalizer'] = isset($definition['normalizer']);
+
+ $this->writeData($data, $options);
+ }
+
private function writeData(array $data, array $options)
{
$flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0;
diff --git a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
index d5072f1e9a63..6e17e9b859e0 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
@@ -13,6 +13,10 @@
use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Form\ResolvedFormTypeInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\VarDumper\Caster\Caster;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
/**
* @author Yonel Ceruto
@@ -21,18 +25,16 @@
*/
class TextDescriptor extends Descriptor
{
- protected function describeDefaults(array $options = array())
+ protected function describeDefaults(array $options)
{
- $coreTypes = $this->getCoreTypes();
-
$this->output->section('Built-in form types (Symfony\Component\Form\Extension\Core\Type)');
- $shortClassNames = array_map(function ($fqcn) { return array_slice(explode('\\', $fqcn), -1)[0]; }, $coreTypes);
+ $shortClassNames = array_map(function ($fqcn) { return array_slice(explode('\\', $fqcn), -1)[0]; }, $options['core_types']);
for ($i = 0; $i * 5 < count($shortClassNames); ++$i) {
$this->output->writeln(' '.implode(', ', array_slice($shortClassNames, $i * 5, 5)));
}
$this->output->section('Service form types');
- $this->output->listing(array_diff($options['types'], $coreTypes));
+ $this->output->listing($options['service_types']);
$this->output->section('Type extensions');
$this->output->listing($options['extensions']);
@@ -94,6 +96,34 @@ protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedF
}
}
+ protected function describeOption(OptionsResolver $optionsResolver, array $options)
+ {
+ $definition = $this->getOptionDefinition($optionsResolver, $options['option']);
+
+ $dump = $this->getDumpFunction();
+ $map = array(
+ 'Required' => 'required',
+ 'Default' => 'default',
+ 'Allowed types' => 'allowedTypes',
+ 'Allowed values' => 'allowedValues',
+ 'Normalizer' => 'normalizer',
+ );
+ $rows = array();
+ foreach ($map as $label => $name) {
+ $value = array_key_exists($name, $definition) ? $dump($definition[$name]) : '-';
+ if ('default' === $name && isset($definition['lazy'])) {
+ $value = "Value: $value\n\nClosure(s): ".$dump($definition['lazy']);
+ }
+
+ $rows[] = array("$label", $value);
+ $rows[] = new TableSeparator();
+ }
+ array_pop($rows);
+
+ $this->output->title(sprintf('%s (%s)', get_class($options['type']), $options['option']));
+ $this->output->table(array(), $rows);
+ }
+
private function normalizeAndSortOptionsColumns(array $options)
{
foreach ($options as $group => &$opts) {
@@ -125,4 +155,24 @@ private function normalizeAndSortOptionsColumns(array $options)
return $options;
}
+
+ private function getDumpFunction()
+ {
+ $cloner = new VarCloner();
+ $cloner->addCasters(array('Closure' => function ($c, $a) {
+ $prefix = Caster::PREFIX_VIRTUAL;
+
+ return array(
+ $prefix.'parameters' => isset($a[$prefix.'parameters']) ? count($a[$prefix.'parameters']->value) : 0,
+ $prefix.'file' => $a[$prefix.'file'],
+ $prefix.'line' => $a[$prefix.'line'],
+ );
+ }));
+ $dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
+ $dumper->setColors($this->output->isDecorated());
+
+ return function ($value) use ($dumper, $cloner) {
+ return rtrim($dumper->dump($cloner->cloneVar($value)->withRefHandles(false), true));
+ };
+ }
}
diff --git a/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
index d24af0a4b1bc..f69222c345ed 100644
--- a/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
+++ b/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
@@ -39,6 +39,15 @@ public function testDebugSingleFormType()
$this->assertContains('Symfony\Component\Form\Extension\Core\Type\FormType (Block prefix: "form")', $tester->getDisplay());
}
+ public function testDebugFormTypeOption()
+ {
+ $tester = $this->createCommandTester();
+ $ret = $tester->execute(array('class' => 'FormType', 'option' => 'method'), array('decorated' => false));
+
+ $this->assertEquals(0, $ret, 'Returns 0 in case of success');
+ $this->assertContains('Symfony\Component\Form\Extension\Core\Type\FormType (method)', $tester->getDisplay());
+ }
+
/**
* @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
* @expectedExceptionMessage Could not find type "NonExistentType"
@@ -90,7 +99,7 @@ public function testDebugAmbiguousFormTypeInteractive()
The type "AmbiguousType" is ambiguous.
- Select one of the following form types to display its information: [%A\A\AmbiguousType]:
+Select one of the following form types to display its information: [%A\A\AmbiguousType]:
[0] %A\A\AmbiguousType
[1] %A\B\AmbiguousType
%A
diff --git a/src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php
index 3a5efcefeb68..8849266527d8 100644
--- a/src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php
+++ b/src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php
@@ -15,11 +15,15 @@
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension;
+use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\ResolvedFormType;
use Symfony\Component\Form\ResolvedFormTypeInterface;
+use Symfony\Component\OptionsResolver\Options;
+use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Csrf\CsrfTokenManager;
abstract class AbstractDescriptorTest extends TestCase
@@ -27,8 +31,8 @@ abstract class AbstractDescriptorTest extends TestCase
/** @dataProvider getDescribeDefaultsTestData */
public function testDescribeDefaults($object, array $options, $fixtureName)
{
- $expectedDescription = $this->getExpectedDescription($fixtureName);
$describedObject = $this->getObjectDescription($object, $options);
+ $expectedDescription = $this->getExpectedDescription($fixtureName);
if ('json' === $this->getFormat()) {
$this->assertEquals(json_encode(json_decode($expectedDescription), JSON_PRETTY_PRINT), json_encode(json_decode($describedObject), JSON_PRETTY_PRINT));
@@ -40,8 +44,8 @@ public function testDescribeDefaults($object, array $options, $fixtureName)
/** @dataProvider getDescribeResolvedFormTypeTestData */
public function testDescribeResolvedFormType(ResolvedFormTypeInterface $type, array $options, $fixtureName)
{
- $expectedDescription = $this->getExpectedDescription($fixtureName);
$describedObject = $this->getObjectDescription($type, $options);
+ $expectedDescription = $this->getExpectedDescription($fixtureName);
if ('json' === $this->getFormat()) {
$this->assertEquals(json_encode(json_decode($expectedDescription), JSON_PRETTY_PRINT), json_encode(json_decode($describedObject), JSON_PRETTY_PRINT));
@@ -50,32 +54,64 @@ public function testDescribeResolvedFormType(ResolvedFormTypeInterface $type, ar
}
}
+ /** @dataProvider getDescribeOptionTestData */
+ public function testDescribeOption(OptionsResolver $optionsResolver, array $options, $fixtureName)
+ {
+ $describedObject = $this->getObjectDescription($optionsResolver, $options);
+ $expectedDescription = $this->getExpectedDescription($fixtureName);
+
+ if ('json' === $this->getFormat()) {
+ $this->assertEquals(json_encode(json_decode($expectedDescription), JSON_PRETTY_PRINT), json_encode(json_decode($describedObject), JSON_PRETTY_PRINT));
+ } else {
+ $this->assertStringMatchesFormat(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $describedObject)));
+ }
+ }
+
public function getDescribeDefaultsTestData()
{
- $options['types'] = array('Symfony\Bridge\Doctrine\Form\Type\EntityType');
+ $options['core_types'] = array('Symfony\Component\Form\Extension\Core\Type\FormType');
+ $options['service_types'] = array('Symfony\Bridge\Doctrine\Form\Type\EntityType');
$options['extensions'] = array('Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension');
$options['guessers'] = array('Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser');
+ $options['decorated'] = false;
yield array(null, $options, 'defaults_1');
}
public function getDescribeResolvedFormTypeTestData()
{
- $typeExtensions = array(
- new FormTypeCsrfExtension(new CsrfTokenManager()),
- );
+ $typeExtensions = array(new FormTypeCsrfExtension(new CsrfTokenManager()));
$parent = new ResolvedFormType(new FormType(), $typeExtensions);
- yield array(new ResolvedFormType(new ChoiceType(), array(), $parent), array(), 'resolved_form_type_1');
+ yield array(new ResolvedFormType(new ChoiceType(), array(), $parent), array('decorated' => false), 'resolved_form_type_1');
+ }
+
+ public function getDescribeOptionTestData()
+ {
+ $parent = new ResolvedFormType(new FormType());
+ $options['decorated'] = false;
+
+ $resolvedType = new ResolvedFormType(new ChoiceType(), array(), $parent);
+ $options['type'] = $resolvedType->getInnerType();
+ $options['option'] = 'choice_translation_domain';
+ yield array($resolvedType->getOptionsResolver(), $options, 'default_option_with_normalizer');
+
+ $resolvedType = new ResolvedFormType(new FooType(), array(), $parent);
+ $options['type'] = $resolvedType->getInnerType();
+ $options['option'] = 'foo';
+ yield array($resolvedType->getOptionsResolver(), $options, 'required_option_with_allowed_values');
+
+ $options['option'] = 'empty_data';
+ yield array($resolvedType->getOptionsResolver(), $options, 'overridden_option_with_default_closures');
}
abstract protected function getDescriptor();
abstract protected function getFormat();
- private function getObjectDescription($object, array $options = array())
+ private function getObjectDescription($object, array $options)
{
- $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true);
+ $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, $options['decorated']);
$io = new SymfonyStyle(new ArrayInput(array()), $output);
$this->getDescriptor()->describe($io, $object, $options);
@@ -93,3 +129,23 @@ private function getFixtureFilename($name)
return sprintf('%s/../../Fixtures/Descriptor/%s.%s', __DIR__, $name, $this->getFormat());
}
}
+
+class FooType extends AbstractType
+{
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setRequired('foo');
+ $resolver->setDefault('empty_data', function (Options $options, $value) {
+ $foo = $options['foo'];
+
+ return function (FormInterface $form) use ($foo) {
+ return $form->getConfig()->getCompound() ? array($foo) : $foo;
+ };
+ });
+ $resolver->setAllowedTypes('foo', 'string');
+ $resolver->setAllowedValues('foo', array('bar', 'baz'));
+ $resolver->setNormalizer('foo', function (Options $options, $value) {
+ return (string) $value;
+ });
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.json
new file mode 100644
index 000000000000..0ac903a95475
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.json
@@ -0,0 +1,11 @@
+{
+ "required": false,
+ "default": true,
+ "is_lazy": false,
+ "allowed_types": [
+ "null",
+ "bool",
+ "string"
+ ],
+ "has_normalizer": true
+}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt
new file mode 100644
index 000000000000..a579a90e53b5
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt
@@ -0,0 +1,24 @@
+
+Symfony\Component\Form\Extension\Core\Type\ChoiceType (choice_translation_domain)
+=================================================================================
+
+ ---------------- --------------------%s
+ Required false %s
+ ---------------- --------------------%s
+ Default true %s
+ ---------------- --------------------%s
+ Allowed types [ %s
+ "null", %s
+ "bool", %s
+ "string" %s
+ ] %s
+ ---------------- --------------------%s
+ Allowed values - %s
+ ---------------- --------------------%s
+ Normalizer Closure { %s
+ parameters: 2, %s
+ file: "%s%eExtension%eCore%eType%eChoiceType.php",
+ line: "%s to %s" %s
+ } %s
+ ---------------- --------------------%s
+
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.json
index 99858a2f997e..7629e80431eb 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.json
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.json
@@ -1,39 +1,6 @@
{
"builtin_form_types": [
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\BirthdayType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\ButtonType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\CheckboxType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\ChoiceType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\CollectionType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\ColorType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\CountryType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\CurrencyType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\DateIntervalType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\DateTimeType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\DateType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\EmailType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\FileType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\FormType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\HiddenType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\IntegerType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\LanguageType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\LocaleType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\MoneyType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\NumberType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\PasswordType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\PercentType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\RadioType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\RangeType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\RepeatedType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\ResetType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\SearchType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\SubmitType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\TelType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\TextType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\TextareaType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\TimeType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\TimezoneType",
- "Symfony\\Component\\Form\\Extension\\Core\\Type\\UrlType"
+ "Symfony\\Component\\Form\\Extension\\Core\\Type\\FormType"
],
"service_form_types": [
"Symfony\\Bridge\\Doctrine\\Form\\Type\\EntityType"
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.txt
index 52a579ac43e6..9b3338ec7bd3 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.txt
@@ -1,27 +1,21 @@
-[33mBuilt-in form types (Symfony\Component\Form\Extension\Core\Type)[39m
-[33m----------------------------------------------------------------[39m
+Built-in form types (Symfony\Component\Form\Extension\Core\Type)
+----------------------------------------------------------------
- BirthdayType, ButtonType, CheckboxType, ChoiceType, CollectionType
- ColorType, CountryType, CurrencyType, DateIntervalType, DateTimeType
- DateType, EmailType, FileType, FormType, HiddenType
- IntegerType, LanguageType, LocaleType, MoneyType, NumberType
- PasswordType, PercentType, RadioType, RangeType, RepeatedType
- ResetType, SearchType, SubmitType, TelType, TextType
- TextareaType, TimeType, TimezoneType, UrlType
+ FormType
-[33mService form types[39m
-[33m------------------[39m
+Service form types
+------------------
* Symfony\Bridge\Doctrine\Form\Type\EntityType
-[33mType extensions[39m
-[33m---------------[39m
+Type extensions
+---------------
* Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension
-[33mType guessers[39m
-[33m-------------[39m
+Type guessers
+-------------
* Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.json
new file mode 100644
index 000000000000..c41e377acd3d
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.json
@@ -0,0 +1,6 @@
+{
+ "required": false,
+ "default": null,
+ "is_lazy": true,
+ "has_normalizer": false
+}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt
new file mode 100644
index 000000000000..662d982979bc
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt
@@ -0,0 +1,29 @@
+
+Symfony\Component\Form\Tests\Console\Descriptor\FooType (empty_data)
+====================================================================
+
+ ---------------- ----------------------%s
+ Required false %s
+ ---------------- ----------------------%s
+ Default Value: null %s
+ %s
+ Closure(s): [ %s
+ Closure { %s
+ parameters: 1, %s
+ file: "%s%eExtension%eCore%eType%eFormType.php",
+ line: "%s to %s" %s
+ }, %s
+ Closure { %s
+ parameters: 2, %s
+ file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php",
+ line: "%s to %s" %s
+ } %s
+ ] %s
+ ---------------- ----------------------%s
+ Allowed types - %s
+ ---------------- ----------------------%s
+ Allowed values - %s
+ ---------------- ----------------------%s
+ Normalizer - %s
+ ---------------- ----------------------%s
+
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.json
new file mode 100644
index 000000000000..126933c6b048
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.json
@@ -0,0 +1,11 @@
+{
+ "required": true,
+ "allowed_types": [
+ "string"
+ ],
+ "allowed_values": [
+ "bar",
+ "baz"
+ ],
+ "has_normalizer": true
+}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt
new file mode 100644
index 000000000000..049b692f1dc7
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt
@@ -0,0 +1,25 @@
+
+Symfony\Component\Form\Tests\Console\Descriptor\FooType (foo)
+=============================================================
+
+ ---------------- --------------------%s
+ Required true %s
+ ---------------- --------------------%s
+ Default - %s
+ ---------------- --------------------%s
+ Allowed types [ %s
+ "string" %s
+ ] %s
+ ---------------- --------------------%s
+ Allowed values [ %s
+ "bar", %s
+ "baz" %s
+ ] %s
+ ---------------- --------------------%s
+ Normalizer Closure { %s
+ parameters: 2, %s
+ file: "%s%eTests%eConsole%eDescriptor%eAbstractDescriptorTest.php",
+ line: "%s to %s" %s
+ } %s
+ ---------------- --------------------%s
+
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
index 5f839b85ac6b..2b34ad960d67 100644
--- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
@@ -1,11 +1,11 @@
-[33mSymfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")[39m
-[33m==============================================================================[39m
+Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")
+==============================================================================
--------------------------- -------------------- ------------------------- -----------------------
- [32m Options [39m [32m Overridden options [39m [32m Parent options [39m [32m Extension options [39m
+ Options Overridden options Parent options Extension options
--------------------------- -------------------- ------------------------- -----------------------
- choice_attr [32mFormType[39m [32mFormType[39m [32mFormTypeCsrfExtension[39m
+ choice_attr FormType FormType FormTypeCsrfExtension
choice_label -------------------- ------------------------- -----------------------
choice_loader compound action csrf_field_name
choice_name data_class attr csrf_message
@@ -28,13 +28,13 @@
upload_max_size_message
--------------------------- -------------------- ------------------------- -----------------------
-[33mParent types[39m
-[33m------------[39m
+Parent types
+------------
* Symfony\Component\Form\Extension\Core\Type\FormType
-[33mType extensions[39m
-[33m---------------[39m
+Type extensions
+---------------
* Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension
diff --git a/src/Symfony/Component/OptionsResolver/CHANGELOG.md b/src/Symfony/Component/OptionsResolver/CHANGELOG.md
index 5f6d15b2c7dd..d19116f37c28 100644
--- a/src/Symfony/Component/OptionsResolver/CHANGELOG.md
+++ b/src/Symfony/Component/OptionsResolver/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+3.4.0
+-----
+
+ * added `OptionsResolverIntrospector` to inspect options definitions inside an `OptionsResolver` instance
+
2.6.0
-----
diff --git a/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php
new file mode 100644
index 000000000000..60317243e9c4
--- /dev/null
+++ b/src/Symfony/Component/OptionsResolver/Debug/OptionsResolverIntrospector.php
@@ -0,0 +1,102 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Debug;
+
+use Symfony\Component\OptionsResolver\Exception\NoConfigurationException;
+use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+/**
+ * @author Maxime Steinhausser
+ *
+ * @final
+ */
+class OptionsResolverIntrospector
+{
+ private $get;
+
+ public function __construct(OptionsResolver $optionsResolver)
+ {
+ $this->get = \Closure::bind(function ($property, $option, $message) {
+ /** @var OptionsResolver $this */
+ if (!$this->isDefined($option)) {
+ throw new UndefinedOptionsException(sprintf('The option "%s" does not exist.', $option));
+ }
+
+ if (!array_key_exists($option, $this->{$property})) {
+ throw new NoConfigurationException($message);
+ }
+
+ return $this->{$property}[$option];
+ }, $optionsResolver, $optionsResolver);
+ }
+
+ /**
+ * @param string $option
+ *
+ * @return mixed
+ *
+ * @throws NoConfigurationException on no configured value
+ */
+ public function getDefault($option)
+ {
+ return call_user_func($this->get, 'defaults', $option, sprintf('No default value was set for the "%s" option.', $option));
+ }
+
+ /**
+ * @param string $option
+ *
+ * @return \Closure[]
+ *
+ * @throws NoConfigurationException on no configured closures
+ */
+ public function getLazyClosures($option)
+ {
+ return call_user_func($this->get, 'lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option));
+ }
+
+ /**
+ * @param string $option
+ *
+ * @return string[]
+ *
+ * @throws NoConfigurationException on no configured types
+ */
+ public function getAllowedTypes($option)
+ {
+ return call_user_func($this->get, 'allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option));
+ }
+
+ /**
+ * @param string $option
+ *
+ * @return mixed[]
+ *
+ * @throws NoConfigurationException on no configured values
+ */
+ public function getAllowedValues($option)
+ {
+ return call_user_func($this->get, 'allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option));
+ }
+
+ /**
+ * @param string $option
+ *
+ * @return \Closure
+ *
+ * @throws NoConfigurationException on no configured normalizer
+ */
+ public function getNormalizer($option)
+ {
+ return call_user_func($this->get, 'normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option));
+ }
+}
diff --git a/src/Symfony/Component/OptionsResolver/Exception/NoConfigurationException.php b/src/Symfony/Component/OptionsResolver/Exception/NoConfigurationException.php
new file mode 100644
index 000000000000..6693ec14df89
--- /dev/null
+++ b/src/Symfony/Component/OptionsResolver/Exception/NoConfigurationException.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Exception;
+
+use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
+
+/**
+ * Thrown when trying to introspect an option definition property
+ * for which no value was configured inside the OptionsResolver instance.
+ *
+ * @see OptionsResolverIntrospector
+ *
+ * @author Maxime Steinhausser
+ */
+class NoConfigurationException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
new file mode 100644
index 000000000000..7c4753ab5f6b
--- /dev/null
+++ b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
@@ -0,0 +1,203 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\OptionsResolver\Tests\Debug;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
+use Symfony\Component\OptionsResolver\Options;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class OptionsResolverIntrospectorTest extends TestCase
+{
+ public function testGetDefault()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefault($option = 'foo', 'bar');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getDefault($option));
+ }
+
+ public function testGetDefaultNull()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefault($option = 'foo', null);
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertNull($debug->getDefault($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No default value was set for the "foo" option.
+ */
+ public function testGetDefaultThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getDefault($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetDefaultThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getDefault('foo'));
+ }
+
+ public function testGetLazyClosures()
+ {
+ $resolver = new OptionsResolver();
+ $closures = array();
+ $resolver->setDefault($option = 'foo', $closures[] = function (Options $options) {});
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($closures, $debug->getLazyClosures($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No lazy closures were set for the "foo" option.
+ */
+ public function testGetLazyClosuresThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getLazyClosures($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetLazyClosuresThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getLazyClosures('foo'));
+ }
+
+ public function testGetAllowedTypes()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+ $resolver->setAllowedTypes($option = 'foo', $allowedTypes = array('string', 'bool'));
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($allowedTypes, $debug->getAllowedTypes($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No allowed types were set for the "foo" option.
+ */
+ public function testGetAllowedTypesThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getAllowedTypes($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetAllowedTypesThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getAllowedTypes('foo'));
+ }
+
+ public function testGetAllowedValues()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+ $resolver->setAllowedValues($option = 'foo', $allowedValues = array('bar', 'baz'));
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($allowedValues, $debug->getAllowedValues($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No allowed values were set for the "foo" option.
+ */
+ public function testGetAllowedValuesThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getAllowedValues($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetAllowedValuesThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getAllowedValues('foo'));
+ }
+
+ public function testGetNormalizer()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+ $resolver->setNormalizer($option = 'foo', $normalizer = function () {});
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame($normalizer, $debug->getNormalizer($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException
+ * @expectedExceptionMessage No normalizer was set for the "foo" option.
+ */
+ public function testGetNormalizerThrowsOnNoConfiguredValue()
+ {
+ $resolver = new OptionsResolver();
+ $resolver->setDefined($option = 'foo');
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getNormalizer($option));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
+ * @expectedExceptionMessage The option "foo" does not exist.
+ */
+ public function testGetNormalizerThrowsOnNotDefinedOption()
+ {
+ $resolver = new OptionsResolver();
+
+ $debug = new OptionsResolverIntrospector($resolver);
+ $this->assertSame('bar', $debug->getNormalizer('foo'));
+ }
+}