19
19
use Symfony \Component \Console \Output \OutputInterface ;
20
20
use Symfony \Component \Console \Style \SymfonyStyle ;
21
21
use Symfony \Component \Form \Console \Helper \DescriptorHelper ;
22
+ use Symfony \Component \Form \Extension \Core \CoreExtension ;
22
23
use Symfony \Component \Form \FormRegistryInterface ;
24
+ use Symfony \Component \Form \FormTypeInterface ;
23
25
24
26
/**
25
27
* A console command for retrieving information about form types.
@@ -55,6 +57,7 @@ protected function configure()
55
57
$ this
56
58
->setDefinition (array (
57
59
new InputArgument ('class ' , InputArgument::OPTIONAL , 'The form type class ' ),
60
+ new InputArgument ('option ' , InputArgument::OPTIONAL , 'The form type option ' ),
58
61
new InputOption ('format ' , null , InputOption::VALUE_REQUIRED , 'The output format (txt or json) ' , 'txt ' ),
59
62
))
60
63
->setDescription ('Displays form type information ' )
@@ -70,6 +73,10 @@ protected function configure()
70
73
71
74
The command lists all defined options that contains the given form type, as well as their parents and type extensions.
72
75
76
+ <info>php %command.full_name% ChoiceType choice_value</info>
77
+
78
+ The command displays the definition of the given option name.
79
+
73
80
<info>php %command.full_name% --format=json</info>
74
81
75
82
The command lists everything in a machine readable json format.
@@ -87,14 +94,42 @@ protected function execute(InputInterface $input, OutputInterface $output)
87
94
88
95
if (null === $ class = $ input ->getArgument ('class ' )) {
89
96
$ object = null ;
90
- $ options ['types ' ] = $ this ->types ;
97
+ $ options ['core_types ' ] = $ this ->getCoreTypes ();
98
+ $ options ['service_types ' ] = array_values (array_diff ($ this ->types , $ options ['core_types ' ]));
91
99
$ options ['extensions ' ] = $ this ->extensions ;
92
100
$ options ['guessers ' ] = $ this ->guessers ;
101
+ foreach ($ options as $ k => $ list ) {
102
+ sort ($ options [$ k ]);
103
+ }
93
104
} else {
94
105
if (!class_exists ($ class )) {
95
106
$ class = $ this ->getFqcnTypeClass ($ input , $ io , $ class );
96
107
}
97
- $ object = $ this ->formRegistry ->getType ($ class );
108
+ $ resolvedType = $ this ->formRegistry ->getType ($ class );
109
+
110
+ if ($ option = $ input ->getArgument ('option ' )) {
111
+ $ object = $ resolvedType ->getOptionsResolver ();
112
+
113
+ if (!$ object ->isDefined ($ option )) {
114
+ $ message = sprintf ('Option "%s" is not defined in "%s". ' , $ option , get_class ($ resolvedType ->getInnerType ()));
115
+
116
+ if ($ alternatives = $ this ->findAlternatives ($ option , $ object ->getDefinedOptions ())) {
117
+ if (1 == count ($ alternatives )) {
118
+ $ message .= "\n\nDid you mean this? \n " ;
119
+ } else {
120
+ $ message .= "\n\nDid you mean one of these? \n " ;
121
+ }
122
+ $ message .= implode ("\n " , $ alternatives );
123
+ }
124
+
125
+ throw new InvalidArgumentException ($ message );
126
+ }
127
+
128
+ $ options ['type ' ] = $ resolvedType ->getInnerType ();
129
+ $ options ['option ' ] = $ option ;
130
+ } else {
131
+ $ object = $ resolvedType ;
132
+ }
98
133
}
99
134
100
135
$ helper = new DescriptorHelper ();
@@ -105,14 +140,27 @@ protected function execute(InputInterface $input, OutputInterface $output)
105
140
private function getFqcnTypeClass (InputInterface $ input , SymfonyStyle $ io , $ shortClassName )
106
141
{
107
142
$ classes = array ();
143
+ sort ($ this ->namespaces );
108
144
foreach ($ this ->namespaces as $ namespace ) {
109
145
if (class_exists ($ fqcn = $ namespace .'\\' .$ shortClassName )) {
110
146
$ classes [] = $ fqcn ;
111
147
}
112
148
}
113
149
114
150
if (0 === $ count = count ($ classes )) {
115
- throw new InvalidArgumentException (sprintf ("Could not find type \"%s \" into the following namespaces: \n %s " , $ shortClassName , implode ("\n " , $ this ->namespaces )));
151
+ $ message = sprintf ("Could not find type \"%s \" into the following namespaces: \n %s " , $ shortClassName , implode ("\n " , $ this ->namespaces ));
152
+
153
+ $ allTypes = array_merge ($ this ->getCoreTypes (), $ this ->types );
154
+ if ($ alternatives = $ this ->findAlternatives ($ shortClassName , $ allTypes )) {
155
+ if (1 == count ($ alternatives )) {
156
+ $ message .= "\n\nDid you mean this? \n " ;
157
+ } else {
158
+ $ message .= "\n\nDid you mean one of these? \n " ;
159
+ }
160
+ $ message .= implode ("\n " , $ alternatives );
161
+ }
162
+
163
+ throw new InvalidArgumentException ($ message );
116
164
}
117
165
if (1 === $ count ) {
118
166
return $ classes [0 ];
@@ -121,6 +169,35 @@ private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, $shor
121
169
throw new InvalidArgumentException (sprintf ("The type \"%s \" is ambiguous. \n\nDid you mean one of these? \n %s " , $ shortClassName , implode ("\n " , $ classes )));
122
170
}
123
171
124
- 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 ]);
172
+ 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 ]);
173
+ }
174
+
175
+ private function getCoreTypes ()
176
+ {
177
+ $ coreExtension = new CoreExtension ();
178
+ $ loadTypesRefMethod = (new \ReflectionObject ($ coreExtension ))->getMethod ('loadTypes ' );
179
+ $ loadTypesRefMethod ->setAccessible (true );
180
+ $ coreTypes = $ loadTypesRefMethod ->invoke ($ coreExtension );
181
+ $ coreTypes = array_map (function (FormTypeInterface $ type ) { return get_class ($ type ); }, $ coreTypes );
182
+ sort ($ coreTypes );
183
+
184
+ return $ coreTypes ;
185
+ }
186
+
187
+ private function findAlternatives ($ name , array $ collection )
188
+ {
189
+ $ alternatives = array ();
190
+ foreach ($ collection as $ item ) {
191
+ $ lev = levenshtein ($ name , $ item );
192
+ if ($ lev <= strlen ($ name ) / 3 || false !== strpos ($ item , $ name )) {
193
+ $ alternatives [$ item ] = isset ($ alternatives [$ item ]) ? $ alternatives [$ item ] - $ lev : $ lev ;
194
+ }
195
+ }
196
+
197
+ $ threshold = 1e3 ;
198
+ $ alternatives = array_filter ($ alternatives , function ($ lev ) use ($ threshold ) { return $ lev < 2 * $ threshold ; });
199
+ ksort ($ alternatives , SORT_NATURAL | SORT_FLAG_CASE );
200
+
201
+ return array_keys ($ alternatives );
125
202
}
126
203
}
0 commit comments