Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

merged branch kbond/config_dump_command (PR #3187)

Commits
-------

4847d3a renamed command
e97af0b code fixes
df94282 [FrameworkBundle] removed unnecessary DebugCommand
fa32885 [SecurityBundle] added configuration info
2f8ad93 [MonologBundle] added configuration info
9757958 [FrameworkBundle] added configuration info
58939f1 [TwigBundle] added configuration docs
8dc40e4 [FrameworkBundle] added config:dump console command

Discussion
----------

Config dump command

Bug fix: no
Feature addition: yes
Backwards compatibility break: no
Symfony2 tests pass: yes
Fixes the following tickets: #1663
Todo: add more config info/examples

[![Build Status](https://secure.travis-ci.org/kbond/symfony.png?branch=config_dump_command)](http://travis-ci.org/kbond/symfony)

This is a config dump command based on the additions of PR #1099.  This was initially part of that PR and there is some discussion there about it (symfony#1099)

### Usage:

1. dump by root node: ``app/console config:dump framework``
2. dump by bundle name: ``app/console config:dump FrameworkBundle``

A few issues/notes:

* Only dumps to yaml
* Only 1 configuration per bundle (this was brought by @stof here: symfony#1099 (comment))
* Works out of the box for most bundles but not ones that use a non-standard ``Configuration`` class (such as the assetic bundle).  In this case ``Extension::getConfiguration()`` must be configurated.

I have used it to create some (most) of the config reference docs.  It works fine but I find it somewhat crude, any suggestions to improve it would be appreciated.

---------------------------------------------------------------------------

by kbond at 2012-01-24T21:00:43Z

Few more issues:

1. Should I abstract the logic to a "normalizer" class that converts the Configuration class into a manageable array?  I struggle with this idea because isn't that what ``TreeBuilder`` basically is?
2. @stof made a good point that ``config:dump`` doesn't really describe what this does.  Would dumping your config be useful?  Perhaps ``config:dump framework`` dumps the config for your project while ``config:dump --ref framework`` dumps the default reference?

---------------------------------------------------------------------------

by stof at 2012-01-24T21:18:15Z

@kbond you cannot really dump the config. Part of it does not go through these extensions at all. And it does not make much sense anyway IMO.
the command as is does the right job IMO (i.e. dumping a reference for the extension). But its name should be improved

---------------------------------------------------------------------------

by kbond at 2012-01-24T21:20:51Z

``config:reference`` perhaps?

---------------------------------------------------------------------------

by fabpot at 2012-02-02T10:05:19Z

This command is about displaying the default configuration for a given bundle. So, what about `config:dump-reference`? As I understand, the command name is the last element to figure out before merging, right?

---------------------------------------------------------------------------

by stof at 2012-02-02T10:19:49Z

@fabpot indeed.

---------------------------------------------------------------------------

by stof at 2012-02-02T10:34:16Z

and +1 for ``config:dump-reference``

---------------------------------------------------------------------------

by Tobion at 2012-02-02T12:08:03Z

why not use the words you chose yourself: `config:dump-default`
I think it's more explicit.
  • Loading branch information...
commit 79a957be77d3afbb8b3ddbb83a330e05d85b876a 2 parents 5251177 + 4847d3a
Fabien Potencier fabpot authored
261 src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php
View
@@ -0,0 +1,261 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Command;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Config\Definition\NodeInterface;
+use Symfony\Component\Config\Definition\ArrayNode;
+use Symfony\Component\Config\Definition\PrototypedArrayNode;
+use Symfony\Component\Config\Definition\BooleanNode;
+
+/**
+ * A console command for dumping available configuration reference
+ *
+ * @author Kevin Bond <kevinbond@gmail.com>
+ */
+class ConfigDumpReferenceCommand extends ContainerDebugCommand
+{
+ protected $output;
+
+ /**
+ * @see Command
+ */
+ protected function configure()
+ {
+ $this
+ ->setDefinition(array(
+ new InputArgument('name', InputArgument::REQUIRED, 'The Bundle or extension alias')
+ ))
+ ->setName('config:dump-reference')
+ ->setDescription('Dumps default configuration for an extension.')
+ ->setHelp(<<<EOF
+The <info>config:dump</info> command dumps the default configuration for an extension/bundle.
+
+The extension alias or bundle name can be used:
+
+Example:
+
+ <info>%command.name% framework</info>
+
+or
+
+ <info>%command.name% FrameworkBundle</info>
+
+EOF
+ )
+ ;
+ }
+
+ /**
+ * @see Command
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->output = $output;
+ $kernel = $this->getContainer()->get('kernel');
+ $containerBuilder = $this->getContainerBuilder();
+
+ $name = $input->getArgument('name');
+
+ $extension = null;
+
+ if (preg_match('/Bundle$/', $name)) {
+ // input is bundle name
+ $extension = $kernel->getBundle($name)->getContainerExtension();
+
+ if (!$extension) {
+ throw new \LogicException('No extensions with configuration available for "'.$name.'"');
+ }
+
+ $message = 'Default configuration for "'.$name.'"';
+ } else {
+ foreach ($kernel->getBundles() as $bundle) {
+ $extension = $bundle->getContainerExtension();
+
+ if ($extension && $extension->getAlias() === $name) {
+ break;
+ }
+
+ $extension = null;
+ }
+
+ if (!$extension) {
+ throw new \LogicException('No extension with alias "'.$name.'" is enabled');
+ }
+
+ $message = 'Default configuration for extension with alias: "'.$name.'"';
+ }
+
+ $configuration = $extension->getConfiguration(array(), $containerBuilder);
+
+ if (!$configuration) {
+ throw new \LogicException('The extension with alias "'.$extension->getAlias().
+ '" does not have it\'s getConfiguration() method setup');
+ }
+
+ $rootNode = $configuration->getConfigTreeBuilder()->buildTree();
+
+ $output->writeln($message);
+
+ // root node
+ $this->outputNode($rootNode);
+ }
+
+ /**
+ * Outputs a single config reference line
+ *
+ * @param string $text
+ * @param int $indent
+ */
+ private function outputLine($text, $indent = 0)
+ {
+ $indent = strlen($text) + $indent;
+
+ $format = '%'.$indent.'s';
+
+ $this->output->writeln(sprintf($format, $text));
+ }
+
+ private function outputArray(array $array, $depth)
+ {
+ $is_indexed = array_values($array) === $array;
+
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ $val = '';
+ } else {
+ $val = $value;
+ }
+
+ if ($is_indexed) {
+ $this->outputLine('- '.$val, $depth * 4);
+ } else {
+ $this->outputLine(sprintf('%-20s %s', $key.':', $val), $depth * 4);
+ }
+
+ if (is_array($value)) {
+ $this->outputArray($value, $depth + 1);
+ }
+ }
+ }
+
+ /**
+ * @param NodeInterface $node
+ * @param int $depth
+ */
+ private function outputNode(NodeInterface $node, $depth = 0)
+ {
+ $comments = array();
+ $default = '';
+ $defaultArray = null;
+ $children = null;
+ $example = $node->getExample();
+
+ // defaults
+ if ($node instanceof ArrayNode) {
+ $children = $node->getChildren();
+
+ if ($node instanceof PrototypedArrayNode) {
+ $prototype = $node->getPrototype();
+
+ if ($prototype instanceof ArrayNode) {
+ $children = $prototype->getChildren();
+ }
+
+ // check for attribute as key
+ if ($key = $node->getKeyAttribute()) {
+ $keyNode = new ArrayNode($key, $node);
+ $keyNode->setInfo('Prototype');
+
+ // add children
+ foreach ($children as $childNode) {
+ $keyNode->addChild($childNode);
+ }
+ $children = array($key => $keyNode);
+ }
+ }
+
+ if (!$children) {
+ if ($node->hasDefaultValue() && count($defaultArray = $node->getDefaultValue())) {
+ $default = '';
+ } elseif (!is_array($example)) {
+ $default = '[]';
+ }
+ }
+ } else {
+ $default = '~';
+
+ if ($node->hasDefaultValue()) {
+ $default = $node->getDefaultValue();
+
+ if (true === $default) {
+ $default = 'true';
+ } elseif (false === $default) {
+ $default = 'false';
+ } elseif (null === $default) {
+ $default = '~';
+ }
+ }
+ }
+
+ // required?
+ if ($node->isRequired()) {
+ $comments[] = 'Required';
+ }
+
+ // example
+ if ($example && !is_array($example)) {
+ $comments[] = 'Example: '.$example;
+ }
+
+ $default = (string) $default != '' ? ' '.$default : '';
+ $comments = count($comments) ? '# '.implode(', ', $comments) : '';
+
+ $text = sprintf('%-20s %s %s', $node->getName().':', $default, $comments);
+
+ if ($info = $node->getInfo()) {
+ $this->outputLine('');
+ $this->outputLine('# '.$info, $depth * 4);
+ }
+
+ $this->outputLine($text, $depth * 4);
+
+ // output defaults
+ if ($defaultArray) {
+ $this->outputLine('');
+
+ $message = count($defaultArray) > 1 ? 'Defaults' : 'Default';
+
+ $this->outputLine('# '.$message.':', $depth * 4 + 4);
+
+ $this->outputArray($defaultArray, $depth + 1);
+ }
+
+ if (is_array($example)) {
+ $this->outputLine('');
+
+ $message = count($example) > 1 ? 'Examples' : 'Example';
+
+ $this->outputLine('# '.$message.':', $depth * 4 + 4);
+
+ $this->outputArray($example, $depth + 1);
+ }
+
+ if ($children) {
+ foreach ($children as $childNode) {
+ $this->outputNode($childNode, $depth + 1);
+ }
+ }
+ }
+}
2  src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php
View
@@ -183,7 +183,7 @@ protected function outputService(OutputInterface $output, $serviceId)
*
* @return ContainerBuilder
*/
- private function getContainerBuilder()
+ protected function getContainerBuilder()
{
if (!$this->getApplication()->getKernel()->isDebug()) {
throw new \LogicException(sprintf('Debug information about the container is only available in debug mode.'));
12 src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
View
@@ -46,7 +46,7 @@ public function getConfigTreeBuilder()
$rootNode
->children()
- ->scalarNode('charset')->end()
+ ->scalarNode('charset')->setInfo('general configuration')->end()
->scalarNode('trust_proxy_headers')->defaultFalse()->end()
->scalarNode('secret')->isRequired()->end()
->scalarNode('ide')->defaultNull()->end()
@@ -73,6 +73,7 @@ private function addFormSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('form')
+ ->setInfo('form configuration')
->canBeUnset()
->treatNullLike(array('enabled' => true))
->treatTrueLike(array('enabled' => true))
@@ -98,6 +99,7 @@ private function addEsiSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('esi')
+ ->setInfo('esi configuration')
->canBeUnset()
->treatNullLike(array('enabled' => true))
->treatTrueLike(array('enabled' => true))
@@ -114,6 +116,7 @@ private function addProfilerSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('profiler')
+ ->setInfo('profiler configuration')
->canBeUnset()
->children()
->booleanNode('only_exceptions')->defaultFalse()->end()
@@ -142,6 +145,7 @@ private function addRouterSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('router')
+ ->setInfo('router configuration')
->canBeUnset()
->children()
->scalarNode('resource')->isRequired()->end()
@@ -159,6 +163,7 @@ private function addSessionSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('session')
+ ->setInfo('session configuration')
->canBeUnset()
->children()
->booleanNode('auto_start')->defaultFalse()->end()
@@ -201,6 +206,7 @@ private function addTemplatingSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('templating')
+ ->setInfo('templating configuration')
->canBeUnset()
->children()
->scalarNode('assets_version')->defaultValue(null)->end()
@@ -251,6 +257,7 @@ private function addTemplatingSection(ArrayNodeDefinition $rootNode)
->fixXmlConfig('engine')
->children()
->arrayNode('engines')
+ ->setExample(array('twig'))
->isRequired()
->requiresAtLeastOneElement()
->beforeNormalization()
@@ -314,6 +321,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('translator')
+ ->setInfo('translator configuration')
->canBeUnset()
->treatNullLike(array('enabled' => true))
->treatTrueLike(array('enabled' => true))
@@ -331,6 +339,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('validation')
+ ->setInfo('validation configuration')
->canBeUnset()
->treatNullLike(array('enabled' => true))
->treatTrueLike(array('enabled' => true))
@@ -349,6 +358,7 @@ private function addAnnotationsSection(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->arrayNode('annotations')
+ ->setInfo('annotation configuration')
->addDefaultsIfNotSet()
->children()
->scalarNode('cache')->defaultValue('file')->end()
20 src/Symfony/Bundle/MonologBundle/DependencyInjection/Configuration.php
View
@@ -107,6 +107,26 @@ public function getConfigTreeBuilder()
->ifTrue(function($v) { return isset($v['debug']); })
->thenInvalid('The "debug" name cannot be used as it is reserved for the handler of the profiler')
->end()
+ ->setExample(array(
+ 'syslog' => array(
+ 'type' => 'stream',
+ 'path' => '/var/log/symfony.log',
+ 'level' => 'ERROR',
+ 'bubble' => 'false',
+ 'formatter' => 'my_formatter',
+ 'processors' => array('some_callable')
+ ),
+ 'main' => array(
+ 'type' => 'fingerscrossed',
+ 'action_level' => 'WARNING',
+ 'buffer_size' => 30,
+ 'handler' => 'custom',
+ ),
+ 'custom' => array(
+ 'type' => 'service',
+ 'id' => 'my_handler'
+ )
+ ))
->end()
->end()
;
24 src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
View
@@ -57,8 +57,8 @@ public function getConfigTreeBuilder()
$rootNode
->children()
- ->scalarNode('access_denied_url')->defaultNull()->end()
- ->scalarNode('session_fixation_strategy')->cannotBeEmpty()->defaultValue('migrate')->end()
+ ->scalarNode('access_denied_url')->defaultNull()->setExample('/foo/error403')->end()
+ ->scalarNode('session_fixation_strategy')->cannotBeEmpty()->setInfo('strategy can be: none, migrate, invalidate')->defaultValue('migrate')->end()
->booleanNode('hide_user_not_found')->defaultTrue()->end()
->booleanNode('always_authenticate_before_granting')->defaultFalse()->end()
->booleanNode('erase_credentials')->defaultTrue()->end()
@@ -89,7 +89,7 @@ private function addAclSection(ArrayNodeDefinition $rootNode)
->children()
->arrayNode('acl')
->children()
- ->scalarNode('connection')->end()
+ ->scalarNode('connection')->setInfo('any name configured in doctrine.dbal section')->end()
->arrayNode('cache')
->addDefaultsIfNotSet()
->children()
@@ -290,6 +290,16 @@ private function addProvidersSection(ArrayNodeDefinition $rootNode)
->fixXmlConfig('provider')
->children()
->arrayNode('providers')
+ ->setExample(array(
+ 'memory' => array(
+ 'name' => 'memory',
+ 'users' => array(
+ 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'),
+ 'bar' => array('password' => 'bar', 'roles' => '[ROLE_USER, ROLE_ADMIN]')
+ )
+ ),
+ 'entity' => array('entity' => array('class' => 'SecurityBundle:User', 'property' => 'username'))
+ ))
->disallowNewKeysInSubsequentConfigs()
->isRequired()
->requiresAtLeastOneElement()
@@ -340,6 +350,14 @@ private function addEncodersSection(ArrayNodeDefinition $rootNode)
->fixXmlConfig('encoder')
->children()
->arrayNode('encoders')
+ ->setExample(array(
+ 'Acme\DemoBundle\Entity\User1' => 'sha512',
+ 'Acme\DemoBundle\Entity\User2' => array(
+ 'algorithm' => 'sha512',
+ 'encode_as_base64' => 'true',
+ 'iterations'=> 5000
+ )
+ ))
->requiresAtLeastOneElement()
->useAttributeAsKey('class')
->prototype('array')
4 src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php
View
@@ -56,6 +56,7 @@ private function addFormSection(ArrayNodeDefinition $rootNode)
->arrayNode('resources')
->addDefaultsIfNotSet()
->defaultValue(array('form_div_layout.html.twig'))
+ ->setExample(array('MyBundle::form.html.twig'))
->validate()
->ifTrue(function($v) { return !in_array('form_div_layout.html.twig', $v); })
->then(function($v){
@@ -77,6 +78,7 @@ private function addGlobalsSection(ArrayNodeDefinition $rootNode)
->children()
->arrayNode('globals')
->useAttributeAsKey('key')
+ ->setExample(array('foo' => '"@bar"', 'pi' => 3.14))
->prototype('array')
->beforeNormalization()
->ifTrue(function($v){ return is_string($v) && 0 === strpos($v, '@'); })
@@ -116,7 +118,7 @@ private function addTwigOptions(ArrayNodeDefinition $rootNode)
$rootNode
->children()
->scalarNode('autoescape')->end()
- ->scalarNode('base_template_class')->end()
+ ->scalarNode('base_template_class')->setExample('Twig_Template')->end()
->scalarNode('cache')->defaultValue('%kernel.cache_dir%/twig')->end()
->scalarNode('charset')->defaultValue('%kernel.charset%')->end()
->scalarNode('debug')->defaultValue('%kernel.debug%')->end()
Please sign in to comment.
Something went wrong with that request. Please try again.