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 d704551

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

File tree

8 files changed

+218
-1
lines changed
Filter options

8 files changed

+218
-1
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
+37Lines changed: 37 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,25 @@ 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+
try {
106+
$definition[$name] = $this->getPropertyValue($optionsResolver, $refObject->getProperty($name), $option);
107+
} catch (InvalidArgumentException $e) {
108+
}
109+
}
110+
$definition['required'] = $optionsResolver->isRequired($option);
111+
112+
if (isset($definition['lazy'])) {
113+
$definition['defaults'] = 1 === count($definition['lazy']) ? $definition['lazy'][0] : $definition['lazy'];
114+
unset($definition['lazy']);
115+
}
116+
117+
return $definition;
118+
}
119+
95120
private function getParentOptionsResolver(ResolvedFormTypeInterface $type)
96121
{
97122
$this->parents[$class = get_class($type->getInnerType())] = array();
@@ -119,4 +144,16 @@ private function collectTypeExtensionsOptions(ResolvedFormTypeInterface $type, O
119144
$this->extensions[get_class($extension)] = array_diff($optionsResolver->getDefinedOptions(), $inheritedOptions);
120145
}
121146
}
147+
148+
private function getPropertyValue(OptionsResolver $optionsResolver, \ReflectionProperty $property, $option)
149+
{
150+
$property->setAccessible(true);
151+
152+
$value = $property->getValue($optionsResolver);
153+
if (array_key_exists($option, $value)) {
154+
return $value[$option];
155+
}
156+
157+
throw new InvalidArgumentException('Unknown option for this property value.');
158+
}
122159
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
+15Lines changed: 15 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,20 @@ 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+
$data = array(
52+
'required' => $definition['required'],
53+
'default' => array_key_exists('defaults', $definition) ? $definition['defaults'] : '',
54+
'allowed_types' => isset($definition['allowedTypes']) ? $definition['allowedTypes'] : null,
55+
'allowed_values' => isset($definition['allowedValues']) ? $definition['allowedValues'] : null,
56+
'has_normalizer' => isset($definition['normalizers']),
57+
);
58+
59+
$this->writeData($data, $options);
60+
}
61+
4762
private function writeData(array $data, array $options)
4863
{
4964
$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
+52Lines changed: 52 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>
@@ -21,6 +24,11 @@
2124
*/
2225
class TextDescriptor extends Descriptor
2326
{
27+
/** @var CliDumper */
28+
private $dumper;
29+
/** @var VarCloner */
30+
private $cloner;
31+
2432
protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedFormType, array $options = array())
2533
{
2634
$this->collectOptions($resolvedFormType);
@@ -74,6 +82,29 @@ protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedF
7482
}
7583
}
7684

85+
protected function describeOption(OptionsResolver $optionsResolver, array $options = array())
86+
{
87+
$this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
88+
$this->cloner = new VarCloner();
89+
90+
$definition = $this->getOptionDefinition($optionsResolver, $options['option']);
91+
92+
$this->output->title(sprintf('%s (%s)', get_class($options['type']), $options['option']));
93+
$tableSeparator = new TableSeparator();
94+
$rows = array(
95+
array('<info>Required</info>', $this->dumpValue($definition['required'], true)),
96+
$tableSeparator,
97+
array('<info>Default</info>', array_key_exists('defaults', $definition) ? $this->dumpValue($definition['defaults']) : '-'),
98+
$tableSeparator,
99+
array('<info>Allowed Types</info>', isset($definition['allowedTypes']) ? $this->dumpValue($definition['allowedTypes'], true) : '-'),
100+
$tableSeparator,
101+
array('<info>Allowed Values</info>', isset($definition['allowedValues']) ? $this->dumpValue($definition['allowedValues'], true) : '-'),
102+
$tableSeparator,
103+
array('<info>Normalizer</info>', isset($definition['normalizers']) ? $this->dumpValue($definition['normalizers']) : '-'),
104+
);
105+
$this->output->table(array(), $rows);
106+
}
107+
77108
private function normalizeAndSortOptionsColumns(array $options)
78109
{
79110
foreach ($options as $group => &$opts) {
@@ -105,4 +136,25 @@ private function normalizeAndSortOptionsColumns(array $options)
105136

106137
return $options;
107138
}
139+
140+
private function dumpValue($value, $light = false)
141+
{
142+
if (is_string($value) && $light) {
143+
return $value;
144+
}
145+
if (is_bool($value) && $light) {
146+
return $value ? 'Yes' : 'No';
147+
}
148+
if (is_array($value)) {
149+
foreach ($value as $k => $v) {
150+
$value[$k] = $this->dumpValue($v, $light);
151+
}
152+
153+
if ($light) {
154+
return implode(",\n", $value);
155+
}
156+
}
157+
158+
return rtrim($this->dumper->dump($this->cloner->cloneVar($value), true));
159+
}
108160
}

‎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
+23Lines changed: 23 additions & 0 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
@@ -47,6 +48,28 @@ public function getDescribeResolvedFormTypeTestData()
4748
yield array(new ResolvedFormType(new ChoiceType(), array(), $parent), array(), 'resolved_form_type_1');
4849
}
4950

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+
70+
yield array($resolvedType->getOptionsResolver(), $options, 'form_type_option_1');
71+
}
72+
5073
abstract protected function getDescriptor();
5174

5275
abstract protected function getFormat();
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"required": false,
3+
"default": null,
4+
"allowed_types": [
5+
"null",
6+
"callable",
7+
"string",
8+
"Symfony\\Component\\PropertyAccess\\PropertyPath"
9+
],
10+
"allowed_values": null,
11+
"has_normalizer": false
12+
}
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
Symfony\Component\Form\Extension\Core\Type\ChoiceType (choice_value)
3+
====================================================================
4+
5+
---------------- -----------------------------------------------
6+
Required No
7+
---------------- -----------------------------------------------
8+
Default null
9+
---------------- -----------------------------------------------
10+
 Allowed Types null, 
11+
 callable, 
12+
 string, 
13+
 Symfony\Component\PropertyAccess\PropertyPath
14+
---------------- -----------------------------------------------
15+
Allowed Values -
16+
---------------- -----------------------------------------------
17+
Normalizer -
18+
---------------- -----------------------------------------------
19+

0 commit comments

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