Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 67cddf1

Browse filesBrowse files
committed
Add debug:form type option
1 parent 30e3b6d commit 67cddf1
Copy full SHA for 67cddf1

File tree

9 files changed

+205
-12
lines changed
Filter options

9 files changed

+205
-12
lines changed

‎src/Symfony/Component/Form/Command/DebugCommand.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Command/DebugCommand.php
+43-1Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form\Command;
1313

1414
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Exception\InvalidArgumentException;
1516
use Symfony\Component\Console\Input\InputArgument;
1617
use Symfony\Component\Console\Input\InputInterface;
1718
use Symfony\Component\Console\Input\InputOption;
@@ -48,6 +49,7 @@ protected function configure()
4849
$this
4950
->setDefinition(array(
5051
new InputArgument('class', InputArgument::REQUIRED, 'The form type class'),
52+
new InputArgument('option', InputArgument::OPTIONAL, 'The form type option'),
5153
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt or json)', 'txt'),
5254
))
5355
->setDescription('Displays form type information')
@@ -75,7 +77,30 @@ protected function execute(InputInterface $input, OutputInterface $output)
7577
$class = $this->getFqcnTypeClass($input, $io, $class);
7678
}
7779

78-
$object = $this->formRegistry->getType($class);
80+
$resolvedType = $this->formRegistry->getType($class);
81+
82+
if ($option = $input->getArgument('option')) {
83+
$object = $resolvedType->getOptionsResolver();
84+
if (!$object->isDefined($option)) {
85+
$message = sprintf('The option "%s" is not defined in the "%s" form type.', $option, get_class($resolvedType->getInnerType()));
86+
87+
if ($alternatives = $this->findAlternatives($option, $object->getDefinedOptions())) {
88+
if (1 == count($alternatives)) {
89+
$message .= "\n\nDid you mean this?\n ";
90+
} else {
91+
$message .= "\n\nDid you mean one of these?\n ";
92+
}
93+
$message .= implode("\n ", $alternatives);
94+
}
95+
96+
throw new InvalidArgumentException($message);
97+
}
98+
99+
$options['type'] = $resolvedType->getInnerType();
100+
$options['option'] = $option;
101+
} else {
102+
$object = $resolvedType;
103+
}
79104

80105
$helper = new DescriptorHelper();
81106
$options['format'] = $input->getOption('format');
@@ -103,4 +128,21 @@ private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, $shor
103128

104129
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]);
105130
}
131+
132+
private function findAlternatives($name, array $collection)
133+
{
134+
$alternatives = array();
135+
foreach ($collection as $item) {
136+
$lev = levenshtein($name, $item);
137+
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
138+
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
139+
}
140+
}
141+
142+
$threshold = 1e3;
143+
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
144+
ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE);
145+
146+
return array_keys($alternatives);
147+
}
106148
}

‎src/Symfony/Component/Form/Console/Descriptor/Descriptor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form\Console\Descriptor;
1313

1414
use Symfony\Component\Console\Descriptor\DescriptorInterface;
15+
use Symfony\Component\Console\Exception\InvalidArgumentException;
1516
use Symfony\Component\Console\Output\OutputInterface;
1617
use Symfony\Component\Console\Style\SymfonyStyle;
1718
use Symfony\Component\Form\ResolvedFormTypeInterface;
@@ -49,13 +50,18 @@ public function describe(OutputInterface $output, $object, array $options = arra
4950
case $object instanceof ResolvedFormTypeInterface:
5051
$this->describeResolvedFormType($object, $options);
5152
break;
53+
case $object instanceof OptionsResolver:
54+
$this->describeOption($object, $options);
55+
break;
5256
default:
5357
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
5458
}
5559
}
5660

5761
abstract protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedFormType, array $options = array());
5862

63+
abstract protected function describeOption(OptionsResolver $optionsResolver, array $options = array());
64+
5965
protected function collectOptions(ResolvedFormTypeInterface $type)
6066
{
6167
$this->parents = array();
@@ -92,6 +98,27 @@ protected function collectOptions(ResolvedFormTypeInterface $type)
9298
$this->extensions = array_keys($this->extensions);
9399
}
94100

101+
protected function getOptionDefinition(OptionsResolver $optionsResolver, $option)
102+
{
103+
$refObject = new \ReflectionObject($optionsResolver);
104+
foreach (array('defaults', 'lazy', 'allowedTypes', 'allowedValues', 'normalizers') as $name) {
105+
$property = $refObject->getProperty($name);
106+
$property->setAccessible(true);
107+
$value = $property->getValue($optionsResolver);
108+
if (array_key_exists($option, $value)) {
109+
$definition[$name] = $value[$option];
110+
}
111+
}
112+
$definition['required'] = $optionsResolver->isRequired($option);
113+
114+
if (isset($definition['lazy'])) {
115+
$definition['defaults'] = 1 === count($definition['lazy']) ? $definition['lazy'][0] : $definition['lazy'];
116+
unset($definition['lazy']);
117+
}
118+
119+
return $definition;
120+
}
121+
95122
private function getParentOptionsResolver(ResolvedFormTypeInterface $type)
96123
{
97124
$this->parents[$class = get_class($type->getInnerType())] = array();

‎src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form\Console\Descriptor;
1313

1414
use Symfony\Component\Form\ResolvedFormTypeInterface;
15+
use Symfony\Component\OptionsResolver\OptionsResolver;
1516

1617
/**
1718
* @author Yonel Ceruto <yonelceruto@gmail.com>
@@ -44,6 +45,26 @@ protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedF
4445
$this->writeData($data, $options);
4546
}
4647

48+
protected function describeOption(OptionsResolver $optionsResolver, array $options = array())
49+
{
50+
$definition = $this->getOptionDefinition($optionsResolver, $options['option']);
51+
52+
$map = array(
53+
'required' => 'required',
54+
'default' => 'defaults',
55+
'allowed_types' => 'allowedTypes',
56+
'allowed_values' => 'allowedValues',
57+
);
58+
foreach ($map as $uname => $name) {
59+
if (array_key_exists($name, $definition)) {
60+
$data[$uname] = $definition[$name];
61+
}
62+
}
63+
$data['has_normalizer'] = isset($definition['normalizers']);
64+
65+
$this->writeData($data, $options);
66+
}
67+
4768
private function writeData(array $data, array $options)
4869
{
4970
$flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0;

‎src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

1414
use Symfony\Component\Console\Helper\TableSeparator;
1515
use Symfony\Component\Form\ResolvedFormTypeInterface;
16+
use Symfony\Component\OptionsResolver\OptionsResolver;
17+
use Symfony\Component\VarDumper\Cloner\VarCloner;
18+
use Symfony\Component\VarDumper\Dumper\CliDumper;
1619

1720
/**
1821
* @author Yonel Ceruto <yonelceruto@gmail.com>
@@ -74,6 +77,33 @@ protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedF
7477
}
7578
}
7679

80+
protected function describeOption(OptionsResolver $optionsResolver, array $options = array())
81+
{
82+
$cloner = new VarCloner();
83+
$dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
84+
$dumper->setColors($this->output->isDecorated());
85+
$dump = function ($value) use ($dumper, $cloner) {
86+
return rtrim($dumper->dump($cloner->cloneVar($value), true));
87+
};
88+
89+
$definition = $this->getOptionDefinition($optionsResolver, $options['option']);
90+
91+
$this->output->title(sprintf('%s (%s)', get_class($options['type']), $options['option']));
92+
$tableSeparator = new TableSeparator();
93+
$rows = array(
94+
array('<info>Required</info>', $dump($definition['required'])),
95+
$tableSeparator,
96+
array('<info>Default</info>', array_key_exists('defaults', $definition) ? $dump($definition['defaults']) : '-'),
97+
$tableSeparator,
98+
array('<info>Allowed Types</info>', isset($definition['allowedTypes']) ? $dump($definition['allowedTypes']) : '-'),
99+
$tableSeparator,
100+
array('<info>Allowed Values</info>', isset($definition['allowedValues']) ? $dump($definition['allowedValues']) : '-'),
101+
$tableSeparator,
102+
array('<info>Normalizer</info>', isset($definition['normalizers']) ? $dump($definition['normalizers']) : '-'),
103+
);
104+
$this->output->table(array(), $rows);
105+
}
106+
77107
private function normalizeAndSortOptionsColumns(array $options)
78108
{
79109
foreach ($options as $group => &$opts) {

‎src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\Form\Extension\Core\Type\FormType;
1919
use Symfony\Component\Form\FormRegistryInterface;
2020
use Symfony\Component\Form\ResolvedFormTypeInterface;
21+
use Symfony\Component\OptionsResolver\OptionsResolver;
2122

2223
class DebugCommandTest extends TestCase
2324
{
@@ -30,6 +31,15 @@ public function testDebugSingleFormType()
3031
$this->assertContains('Symfony\Component\Form\Extension\Core\Type\FormType (Block prefix: "form")', $tester->getDisplay());
3132
}
3233

34+
public function testDebugFormTypeOption()
35+
{
36+
$tester = $this->createCommandTester();
37+
$ret = $tester->execute(array('class' => 'FormType', 'option' => 'method'), array('decorated' => false));
38+
39+
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
40+
$this->assertContains('Symfony\Component\Form\Extension\Core\Type\FormType (method)', $tester->getDisplay());
41+
}
42+
3343
/**
3444
* @expectedException \InvalidArgumentException
3545
*/
@@ -59,6 +69,13 @@ private function createCommandTester()
5969
->method('getTypeExtensions')
6070
->willReturn(array())
6171
;
72+
$optionsResolver = new OptionsResolver();
73+
$optionsResolver->setDefault('method', 'POST');
74+
$resolvedFormType
75+
->expects($this->any())
76+
->method('getOptionsResolver')
77+
->willReturn($optionsResolver)
78+
;
6279

6380
$formRegistry = $this->getMockBuilder(FormRegistryInterface::class)->getMock();
6481
$formRegistry

‎src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php
+27-3Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension;
2121
use Symfony\Component\Form\ResolvedFormType;
2222
use Symfony\Component\Form\ResolvedFormTypeInterface;
23+
use Symfony\Component\OptionsResolver\OptionsResolver;
2324
use Symfony\Component\Security\Csrf\CsrfTokenManager;
2425

2526
abstract class AbstractDescriptorTest extends TestCase
@@ -44,16 +45,39 @@ public function getDescribeResolvedFormTypeTestData()
4445
);
4546
$parent = new ResolvedFormType(new FormType(), $typeExtensions);
4647

47-
yield array(new ResolvedFormType(new ChoiceType(), array(), $parent), array(), 'resolved_form_type_1');
48+
yield array(new ResolvedFormType(new ChoiceType(), array(), $parent), array('decorated' => false), 'resolved_form_type_1');
49+
}
50+
51+
/** @dataProvider getDescribeOptionTestData */
52+
public function testDescribeOption(OptionsResolver $optionsResolver, array $options, $fixtureName)
53+
{
54+
$expectedDescription = $this->getExpectedDescription($fixtureName);
55+
$describedObject = $this->getObjectDescription($optionsResolver, $options);
56+
57+
if ('json' === $this->getFormat()) {
58+
$this->assertEquals(json_encode(json_decode($expectedDescription), JSON_PRETTY_PRINT), json_encode(json_decode($describedObject), JSON_PRETTY_PRINT));
59+
} else {
60+
$this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $describedObject)));
61+
}
62+
}
63+
64+
public function getDescribeOptionTestData()
65+
{
66+
$resolvedType = new ResolvedFormType(new ChoiceType(), array(), new ResolvedFormType(new FormType()));
67+
$options['type'] = $resolvedType->getInnerType();
68+
$options['option'] = 'choice_value';
69+
$options['decorated'] = false;
70+
71+
yield array($resolvedType->getOptionsResolver(), $options, 'form_type_option_1');
4872
}
4973

5074
abstract protected function getDescriptor();
5175

5276
abstract protected function getFormat();
5377

54-
private function getObjectDescription($object, array $options = array())
78+
private function getObjectDescription($object, array $options)
5579
{
56-
$output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true);
80+
$output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, $options['decorated']);
5781
$io = new SymfonyStyle(new ArrayInput(array()), $output);
5882

5983
$this->getDescriptor()->describe($io, $object, $options);
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"required": false,
3+
"default": null,
4+
"allowed_types": [
5+
"null",
6+
"callable",
7+
"string",
8+
"Symfony\\Component\\PropertyAccess\\PropertyPath"
9+
],
10+
"has_normalizer": false
11+
}
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
Symfony\Component\Form\Extension\Core\Type\ChoiceType (choice_value)
3+
====================================================================
4+
5+
---------------- ---------------------------------------------------
6+
Required false
7+
---------------- ---------------------------------------------------
8+
Default null
9+
---------------- ---------------------------------------------------
10+
Allowed Types [
11+
"null",
12+
"callable",
13+
"string",
14+
"Symfony\Component\PropertyAccess\PropertyPath"
15+
]
16+
---------------- ---------------------------------------------------
17+
Allowed Values -
18+
---------------- ---------------------------------------------------
19+
Normalizer -
20+
---------------- ---------------------------------------------------
21+

‎src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt
+8-8Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11

2-
Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")
3-
==============================================================================
2+
Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")
3+
==============================================================================
44

55
--------------------------- -------------------- ------------------------- -----------------------
6-
 Options   Overridden options   Parent options   Extension options 
6+
Options Overridden options Parent options Extension options
77
--------------------------- -------------------- ------------------------- -----------------------
8-
choice_attr FormType FormType FormTypeCsrfExtension
8+
choice_attr FormType FormType FormTypeCsrfExtension
99
choice_label -------------------- ------------------------- -----------------------
1010
choice_loader compound action csrf_field_name
1111
choice_name data_class attr csrf_message
@@ -28,13 +28,13 @@
2828
upload_max_size_message
2929
--------------------------- -------------------- ------------------------- -----------------------
3030

31-
Parent types
32-
------------
31+
Parent types
32+
------------
3333

3434
* Symfony\Component\Form\Extension\Core\Type\FormType
3535

36-
Type extensions
37-
---------------
36+
Type extensions
37+
---------------
3838

3939
* Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension
4040

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.