Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dd3276c

Browse files
committed
feature #23694 [Form] Add debug:form command (yceruto)
This PR was merged into the 3.4 branch. Discussion ---------- [Form] Add debug:form command | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #23688 | License | MIT | Doc PR | - ![debug-form](https://user-images.githubusercontent.com/2028198/29007125-c3508cd6-7aca-11e7-91e2-c2b509847db5.png) A short class name (e.g. `DateType`) can be passed as `class` argument too (the command will try to resolve its FQCN if it's in known form type namespaces). Commits ------- 4f040d7 Add debug:form command
2 parents b3d31e9 + 4f040d7 commit dd3276c

File tree

18 files changed

+917
-5
lines changed

18 files changed

+917
-5
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ public function load(array $configs, ContainerBuilder $container)
220220
if (!class_exists('Symfony\Component\Validator\Validation')) {
221221
throw new LogicException('The Validator component is required to use the Form component.');
222222
}
223+
} else {
224+
$container->removeDefinition('Symfony\Component\Form\Command\DebugCommand');
223225
}
224226

225227
$this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader);

src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,11 @@
9696
<service id="Symfony\Bundle\FrameworkBundle\Command\YamlLintCommand">
9797
<tag name="console.command" command="lint:yaml" />
9898
</service>
99+
100+
<service id="Symfony\Component\Form\Command\DebugCommand">
101+
<argument type="service" id="form.registry" />
102+
<argument type="collection" /> <!-- All form types namespaces are stored here by FormPass -->
103+
<tag name="console.command" command="debug:form" />
104+
</service>
99105
</services>
100106
</container>

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"symfony/dom-crawler": "~2.8|~3.0|~4.0",
4141
"symfony/polyfill-intl-icu": "~1.0",
4242
"symfony/security": "~2.8|~3.0|~4.0",
43-
"symfony/form": "~3.3|~4.0",
43+
"symfony/form": "~3.4|~4.0",
4444
"symfony/expression-language": "~2.8|~3.0|~4.0",
4545
"symfony/process": "~2.8|~3.0|~4.0",
4646
"symfony/security-core": "~3.2|~4.0",
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Command;
13+
14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Input\InputArgument;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Input\InputOption;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
use Symfony\Component\Console\Style\SymfonyStyle;
20+
use Symfony\Component\Form\Console\Helper\DescriptorHelper;
21+
use Symfony\Component\Form\FormRegistryInterface;
22+
23+
/**
24+
* A console command for retrieving information about form types.
25+
*
26+
* @author Yonel Ceruto <[email protected]>
27+
*/
28+
class DebugCommand extends Command
29+
{
30+
protected static $defaultName = 'debug:form';
31+
32+
private $formRegistry;
33+
private $namespaces;
34+
35+
public function __construct(FormRegistryInterface $formRegistry, array $namespaces = array('Symfony\Component\Form\Extension\Core\Type'))
36+
{
37+
parent::__construct();
38+
39+
$this->formRegistry = $formRegistry;
40+
$this->namespaces = $namespaces;
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
protected function configure()
47+
{
48+
$this
49+
->setDefinition(array(
50+
new InputArgument('class', InputArgument::REQUIRED, 'The form type class'),
51+
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt or json)', 'txt'),
52+
))
53+
->setDescription('Displays form type information')
54+
;
55+
}
56+
57+
/**
58+
* {@inheritdoc}
59+
*/
60+
protected function execute(InputInterface $input, OutputInterface $output)
61+
{
62+
$io = new SymfonyStyle($input, $output);
63+
64+
if (!class_exists($class = $input->getArgument('class'))) {
65+
$class = $this->getFqcnTypeClass($input, $io, $class);
66+
}
67+
68+
$object = $this->formRegistry->getType($class);
69+
70+
$helper = new DescriptorHelper();
71+
$options['format'] = $input->getOption('format');
72+
$helper->describe($io, $object, $options);
73+
}
74+
75+
private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, $shortClassName)
76+
{
77+
$classes = array();
78+
foreach ($this->namespaces as $namespace) {
79+
if (class_exists($fqcn = $namespace.'\\'.$shortClassName)) {
80+
$classes[] = $fqcn;
81+
}
82+
}
83+
84+
if (0 === $count = count($classes)) {
85+
throw new \InvalidArgumentException(sprintf("Could not find type \"%s\" into the following namespaces:\n %s", $shortClassName, implode("\n ", $this->namespaces)));
86+
}
87+
if (1 === $count) {
88+
return $classes[0];
89+
}
90+
if (!$input->isInteractive()) {
91+
throw new \InvalidArgumentException(sprintf("The type \"%s\" is ambiguous.\nDid you mean one of these?\n %s", $shortClassName, implode("\n ", $classes)));
92+
}
93+
94+
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]);
95+
}
96+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Console\Descriptor;
13+
14+
use Symfony\Component\Console\Descriptor\DescriptorInterface;
15+
use Symfony\Component\Console\Output\OutputInterface;
16+
use Symfony\Component\Console\Style\SymfonyStyle;
17+
use Symfony\Component\Form\ResolvedFormTypeInterface;
18+
use Symfony\Component\Form\Util\OptionsResolverWrapper;
19+
use Symfony\Component\OptionsResolver\OptionsResolver;
20+
21+
/**
22+
* @author Yonel Ceruto <[email protected]>
23+
*
24+
* @internal
25+
*/
26+
abstract class Descriptor implements DescriptorInterface
27+
{
28+
/**
29+
* @var SymfonyStyle
30+
*/
31+
protected $output;
32+
protected $type;
33+
protected $ownOptions = array();
34+
protected $overriddenOptions = array();
35+
protected $parentOptions = array();
36+
protected $extensionOptions = array();
37+
protected $requiredOptions = array();
38+
protected $parents = array();
39+
protected $extensions = array();
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function describe(OutputInterface $output, $object, array $options = array())
45+
{
46+
$this->output = $output;
47+
48+
switch (true) {
49+
case $object instanceof ResolvedFormTypeInterface:
50+
$this->describeResolvedFormType($object, $options);
51+
break;
52+
default:
53+
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
54+
}
55+
}
56+
57+
abstract protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedFormType, array $options = array());
58+
59+
protected function collectOptions(ResolvedFormTypeInterface $type)
60+
{
61+
$this->parents = array();
62+
$this->extensions = array();
63+
64+
if (null !== $type->getParent()) {
65+
$optionsResolver = clone $this->getParentOptionsResolver($type->getParent());
66+
} else {
67+
$optionsResolver = new OptionsResolver();
68+
}
69+
70+
$type->getInnerType()->configureOptions($ownOptionsResolver = new OptionsResolverWrapper());
71+
$this->ownOptions = array_diff($ownOptionsResolver->getDefinedOptions(), $optionsResolver->getDefinedOptions());
72+
$overriddenOptions = array_intersect(array_merge($ownOptionsResolver->getDefinedOptions(), $ownOptionsResolver->getUndefinedOptions()), $optionsResolver->getDefinedOptions());
73+
74+
$this->parentOptions = array();
75+
foreach ($this->parents as $class => $parentOptions) {
76+
$this->overriddenOptions[$class] = array_intersect($overriddenOptions, $parentOptions);
77+
$this->parentOptions[$class] = array_diff($parentOptions, $overriddenOptions);
78+
}
79+
80+
$type->getInnerType()->configureOptions($optionsResolver);
81+
$this->collectTypeExtensionsOptions($type, $optionsResolver);
82+
$this->extensionOptions = array();
83+
foreach ($this->extensions as $class => $extensionOptions) {
84+
$this->overriddenOptions[$class] = array_intersect($overriddenOptions, $extensionOptions);
85+
$this->extensionOptions[$class] = array_diff($extensionOptions, $overriddenOptions);
86+
}
87+
88+
$this->overriddenOptions = array_filter($this->overriddenOptions);
89+
$this->requiredOptions = $optionsResolver->getRequiredOptions();
90+
91+
$this->parents = array_keys($this->parents);
92+
$this->extensions = array_keys($this->extensions);
93+
}
94+
95+
private function getParentOptionsResolver(ResolvedFormTypeInterface $type)
96+
{
97+
$this->parents[$class = get_class($type->getInnerType())] = array();
98+
99+
if (null !== $type->getParent()) {
100+
$optionsResolver = clone $this->getParentOptionsResolver($type->getParent());
101+
} else {
102+
$optionsResolver = new OptionsResolver();
103+
}
104+
105+
$inheritedOptions = $optionsResolver->getDefinedOptions();
106+
$type->getInnerType()->configureOptions($optionsResolver);
107+
$this->parents[$class] = array_diff($optionsResolver->getDefinedOptions(), $inheritedOptions);
108+
109+
$this->collectTypeExtensionsOptions($type, $optionsResolver);
110+
111+
return $optionsResolver;
112+
}
113+
114+
private function collectTypeExtensionsOptions(ResolvedFormTypeInterface $type, OptionsResolver $optionsResolver)
115+
{
116+
foreach ($type->getTypeExtensions() as $extension) {
117+
$inheritedOptions = $optionsResolver->getDefinedOptions();
118+
$extension->configureOptions($optionsResolver);
119+
$this->extensions[get_class($extension)] = array_diff($optionsResolver->getDefinedOptions(), $inheritedOptions);
120+
}
121+
}
122+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Console\Descriptor;
13+
14+
use Symfony\Component\Form\ResolvedFormTypeInterface;
15+
16+
/**
17+
* @author Yonel Ceruto <[email protected]>
18+
*
19+
* @internal
20+
*/
21+
class JsonDescriptor extends Descriptor
22+
{
23+
protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedFormType, array $options = array())
24+
{
25+
$this->collectOptions($resolvedFormType);
26+
27+
$formOptions = array(
28+
'own' => $this->ownOptions,
29+
'overridden' => $this->overriddenOptions,
30+
'parent' => $this->parentOptions,
31+
'extension' => $this->extensionOptions,
32+
'required' => $this->requiredOptions,
33+
);
34+
$this->sortOptions($formOptions);
35+
36+
$data = array(
37+
'class' => get_class($resolvedFormType->getInnerType()),
38+
'block_prefix' => $resolvedFormType->getInnerType()->getBlockPrefix(),
39+
'options' => $formOptions,
40+
'parent_types' => $this->parents,
41+
'type_extensions' => $this->extensions,
42+
);
43+
44+
$this->writeData($data, $options);
45+
}
46+
47+
private function writeData(array $data, array $options)
48+
{
49+
$flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0;
50+
$this->output->write(json_encode($data, $flags | JSON_PRETTY_PRINT)."\n");
51+
}
52+
53+
private function sortOptions(array &$options)
54+
{
55+
foreach ($options as &$opts) {
56+
$sorted = false;
57+
foreach ($opts as &$opt) {
58+
if (is_array($opt)) {
59+
sort($opt);
60+
$sorted = true;
61+
}
62+
}
63+
if (!$sorted) {
64+
sort($opts);
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)