diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml
index 80bc32d3ff221..7dc4ea6d2fbae 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml
@@ -100,6 +100,9 @@
+
+
+
diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md
index 1c8936995c226..8af1a324dca95 100644
--- a/src/Symfony/Component/Form/CHANGELOG.md
+++ b/src/Symfony/Component/Form/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+3.4.0
+-----
+
+ * added `DebugCommand`
+
3.3.0
-----
diff --git a/src/Symfony/Component/Form/Command/DebugCommand.php b/src/Symfony/Component/Form/Command/DebugCommand.php
index d646ffa154305..d9b3eee838244 100644
--- a/src/Symfony/Component/Form/Command/DebugCommand.php
+++ b/src/Symfony/Component/Form/Command/DebugCommand.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Command;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -31,13 +32,19 @@ class DebugCommand extends Command
private $formRegistry;
private $namespaces;
+ private $types;
+ private $extensions;
+ private $guessers;
- public function __construct(FormRegistryInterface $formRegistry, array $namespaces = array('Symfony\Component\Form\Extension\Core\Type'))
+ public function __construct(FormRegistryInterface $formRegistry, array $namespaces = array('Symfony\Component\Form\Extension\Core\Type'), array $types = array(), array $extensions = array(), array $guessers = array())
{
parent::__construct();
$this->formRegistry = $formRegistry;
$this->namespaces = $namespaces;
+ $this->types = $types;
+ $this->extensions = $extensions;
+ $this->guessers = $guessers;
}
/**
@@ -47,18 +54,25 @@ protected function configure()
{
$this
->setDefinition(array(
- new InputArgument('class', InputArgument::REQUIRED, 'The form type class'),
+ new InputArgument('class', InputArgument::OPTIONAL, 'The form type class'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt or json)', 'txt'),
))
->setDescription('Displays form type information')
->setHelp(<<<'EOF'
-The %command.name% command displays information about a form type.
+The %command.name% command displays information about form types.
-Either the fully-qualified class name or the short class name can be used:
+ php %command.full_name%
+
+The command lists all built-in types, services types, type extensions and guessers currently available.
php %command.full_name% Symfony\Component\Form\Extension\Core\Type\ChoiceType
php %command.full_name% ChoiceType
+The command lists all defined options that contains the given form type, as well as their parents and type extensions.
+
+ php %command.full_name% --format=json
+
+The command lists everything in a machine readable json format.
EOF
)
;
@@ -71,12 +85,18 @@ protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
- if (!class_exists($class = $input->getArgument('class'))) {
- $class = $this->getFqcnTypeClass($input, $io, $class);
+ if (null === $class = $input->getArgument('class')) {
+ $object = null;
+ $options['types'] = $this->types;
+ $options['extensions'] = $this->extensions;
+ $options['guessers'] = $this->guessers;
+ } else {
+ if (!class_exists($class)) {
+ $class = $this->getFqcnTypeClass($input, $io, $class);
+ }
+ $object = $this->formRegistry->getType($class);
}
- $object = $this->formRegistry->getType($class);
-
$helper = new DescriptorHelper();
$options['format'] = $input->getOption('format');
$helper->describe($io, $object, $options);
@@ -92,13 +112,13 @@ 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)));
+ throw new InvalidArgumentException(sprintf("Could not find type \"%s\" into the following namespaces:\n %s", $shortClassName, implode("\n ", $this->namespaces)));
}
if (1 === $count) {
return $classes[0];
}
if (!$input->isInteractive()) {
- throw new \InvalidArgumentException(sprintf("The type \"%s\" is ambiguous.\nDid you mean one of these?\n %s", $shortClassName, implode("\n ", $classes)));
+ throw new InvalidArgumentException(sprintf("The type \"%s\" is ambiguous.\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]);
diff --git a/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php b/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
index af77f19931244..c72a19d7993f1 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/Descriptor.php
@@ -12,8 +12,12 @@
namespace Symfony\Component\Form\Console\Descriptor;
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\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\OptionsResolver;
@@ -25,9 +29,7 @@
*/
abstract class Descriptor implements DescriptorInterface
{
- /**
- * @var SymfonyStyle
- */
+ /** @var StyleInterface */
protected $output;
protected $type;
protected $ownOptions = array();
@@ -43,9 +45,12 @@ abstract class Descriptor implements DescriptorInterface
*/
public function describe(OutputInterface $output, $object, array $options = array())
{
- $this->output = $output;
+ $this->output = $output instanceof StyleInterface ? $output : new SymfonyStyle(new ArrayInput(array()), $output);
switch (true) {
+ case null === $object:
+ $this->describeDefaults($options);
+ break;
case $object instanceof ResolvedFormTypeInterface:
$this->describeResolvedFormType($object, $options);
break;
@@ -54,8 +59,24 @@ public function describe(OutputInterface $output, $object, array $options = arra
}
}
+ abstract protected function describeDefaults(array $options = array());
+
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;
+ }
+
protected function collectOptions(ResolvedFormTypeInterface $type)
{
$this->parents = array();
diff --git a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
index 638ea7a5ff71e..7616d616f1441 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php
@@ -20,6 +20,16 @@
*/
class JsonDescriptor extends Descriptor
{
+ protected function describeDefaults(array $options = array())
+ {
+ $data['builtin_form_types'] = $this->getCoreTypes();
+ $data['service_form_types'] = array_values(array_diff($options['types'], $data['builtin_form_types']));
+ $data['type_extensions'] = $options['extensions'];
+ $data['type_guessers'] = $options['guessers'];
+
+ $this->writeData($data, $options);
+ }
+
protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedFormType, array $options = array())
{
$this->collectOptions($resolvedFormType);
diff --git a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
index b12a22bb9e26a..d5072f1e9a632 100644
--- a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
+++ b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php
@@ -21,6 +21,26 @@
*/
class TextDescriptor extends Descriptor
{
+ protected function describeDefaults(array $options = array())
+ {
+ $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);
+ 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->section('Type extensions');
+ $this->output->listing($options['extensions']);
+
+ $this->output->section('Type guessers');
+ $this->output->listing($options['guessers']);
+ }
+
protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedFormType, array $options = array())
{
$this->collectOptions($resolvedFormType);
diff --git a/src/Symfony/Component/Form/DependencyInjection/FormPass.php b/src/Symfony/Component/Form/DependencyInjection/FormPass.php
index ff1ac8af60658..bed74532d1571 100644
--- a/src/Symfony/Component/Form/DependencyInjection/FormPass.php
+++ b/src/Symfony/Component/Form/DependencyInjection/FormPass.php
@@ -77,6 +77,7 @@ private function processFormTypes(ContainerBuilder $container)
if ($container->hasDefinition($this->formDebugCommandService)) {
$commandDefinition = $container->getDefinition($this->formDebugCommandService);
$commandDefinition->setArgument(1, array_keys($namespaces));
+ $commandDefinition->setArgument(2, array_keys($servicesMap));
}
return ServiceLocatorTagPass::register($container, $servicesMap);
@@ -85,6 +86,7 @@ private function processFormTypes(ContainerBuilder $container)
private function processFormTypeExtensions(ContainerBuilder $container)
{
$typeExtensions = array();
+ $typeExtensionsClasses = array();
foreach ($this->findAndSortTaggedServices($this->formTypeExtensionTag, $container) as $reference) {
$serviceId = (string) $reference;
$serviceDefinition = $container->getDefinition($serviceId);
@@ -97,20 +99,35 @@ private function processFormTypeExtensions(ContainerBuilder $container)
}
$typeExtensions[$extendedType][] = new Reference($serviceId);
+ $typeExtensionsClasses[] = $serviceDefinition->getClass();
}
foreach ($typeExtensions as $extendedType => $extensions) {
$typeExtensions[$extendedType] = new IteratorArgument($extensions);
}
+ if ($container->hasDefinition($this->formDebugCommandService)) {
+ $commandDefinition = $container->getDefinition($this->formDebugCommandService);
+ $commandDefinition->setArgument(3, $typeExtensionsClasses);
+ }
+
return $typeExtensions;
}
private function processFormTypeGuessers(ContainerBuilder $container)
{
$guessers = array();
+ $guessersClasses = array();
foreach ($container->findTaggedServiceIds($this->formTypeGuesserTag, true) as $serviceId => $tags) {
$guessers[] = new Reference($serviceId);
+
+ $serviceDefinition = $container->getDefinition($serviceId);
+ $guessersClasses[] = $serviceDefinition->getClass();
+ }
+
+ if ($container->hasDefinition($this->formDebugCommandService)) {
+ $commandDefinition = $container->getDefinition($this->formDebugCommandService);
+ $commandDefinition->setArgument(4, $guessersClasses);
}
return new IteratorArgument($guessers);
diff --git a/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
index c2083ea12ce4d..610f6197d62cb 100644
--- a/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
+++ b/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php
@@ -21,6 +21,15 @@
class DebugCommandTest extends TestCase
{
+ public function testDebugDefaults()
+ {
+ $tester = $this->createCommandTester();
+ $ret = $tester->execute(array(), array('decorated' => false));
+
+ $this->assertEquals(0, $ret, 'Returns 0 in case of success');
+ $this->assertContains('Built-in form types', $tester->getDisplay());
+ }
+
public function testDebugSingleFormType()
{
$tester = $this->createCommandTester();
diff --git a/src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php
index c760e5ecf972c..3a5efcefeb684 100644
--- a/src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php
+++ b/src/Symfony/Component/Form/Tests/Console/Descriptor/AbstractDescriptorTest.php
@@ -24,6 +24,19 @@
abstract class AbstractDescriptorTest extends TestCase
{
+ /** @dataProvider getDescribeDefaultsTestData */
+ public function testDescribeDefaults($object, array $options, $fixtureName)
+ {
+ $expectedDescription = $this->getExpectedDescription($fixtureName);
+ $describedObject = $this->getObjectDescription($object, $options);
+
+ if ('json' === $this->getFormat()) {
+ $this->assertEquals(json_encode(json_decode($expectedDescription), JSON_PRETTY_PRINT), json_encode(json_decode($describedObject), JSON_PRETTY_PRINT));
+ } else {
+ $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $describedObject)));
+ }
+ }
+
/** @dataProvider getDescribeResolvedFormTypeTestData */
public function testDescribeResolvedFormType(ResolvedFormTypeInterface $type, array $options, $fixtureName)
{
@@ -37,6 +50,15 @@ public function testDescribeResolvedFormType(ResolvedFormTypeInterface $type, ar
}
}
+ public function getDescribeDefaultsTestData()
+ {
+ $options['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');
+
+ yield array(null, $options, 'defaults_1');
+ }
+
public function getDescribeResolvedFormTypeTestData()
{
$typeExtensions = array(
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.json
new file mode 100644
index 0000000000000..565c17601ed1e
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.json
@@ -0,0 +1,45 @@
+{
+ "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\\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\\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"
+ ],
+ "service_form_types": [
+ "Symfony\\Bridge\\Doctrine\\Form\\Type\\EntityType"
+ ],
+ "type_extensions": [
+ "Symfony\\Component\\Form\\Extension\\Csrf\\Type\\FormTypeCsrfExtension"
+ ],
+ "type_guessers": [
+ "Symfony\\Component\\Form\\Extension\\Validator\\ValidatorTypeGuesser"
+ ]
+}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.txt
new file mode 100644
index 0000000000000..dd08d5f7a64c3
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/defaults_1.txt
@@ -0,0 +1,27 @@
+
+[33mBuilt-in form types (Symfony\Component\Form\Extension\Core\Type)[39m
+[33m----------------------------------------------------------------[39m
+
+ BirthdayType, ButtonType, CheckboxType, ChoiceType, CollectionType
+ CountryType, CurrencyType, DateIntervalType, DateTimeType, DateType
+ EmailType, FileType, FormType, HiddenType, IntegerType
+ LanguageType, LocaleType, MoneyType, NumberType, PasswordType
+ PercentType, RadioType, RangeType, RepeatedType, ResetType
+ SearchType, SubmitType, TextType, TextareaType, TimeType
+ TimezoneType, UrlType
+
+[33mService form types[39m
+[33m------------------[39m
+
+ * Symfony\Bridge\Doctrine\Form\Type\EntityType
+
+[33mType extensions[39m
+[33m---------------[39m
+
+ * Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension
+
+[33mType guessers[39m
+[33m-------------[39m
+
+ * Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser
+