diff --git a/components/config/definition.rst b/components/config/definition.rst
index e62c930b0ed..f16c35d7d60 100644
--- a/components/config/definition.rst
+++ b/components/config/definition.rst
@@ -391,6 +391,8 @@ with ``append()``::
This is also useful to help you avoid repeating yourself if you have sections
of the config that are repeated in different places.
+.. _component-config-normalization:
+
Normalization
-------------
@@ -445,6 +447,8 @@ a second argument::
->fixXmlConfig('child', 'children')
->children()
->arrayNode('children')
+ // ...
+ ->end()
->end()
;
diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst
index 8b4535b0d55..ecff6254849 100644
--- a/contributing/code/standards.rst
+++ b/contributing/code/standards.rst
@@ -148,9 +148,13 @@ Service Naming Conventions
~~~~~~~~~~~~~~~~~~~~~~~~~~
* A service name contains groups, separated by dots;
+
* The DI alias of the bundle is the first group (e.g. ``fos_user``);
+
* Use lowercase letters for service and parameter names;
+
* A group name uses the underscore notation;
+
* Each service has a corresponding parameter containing the class name,
following the ``SERVICE NAME.class`` convention.
diff --git a/cookbook/bundles/configuration.rst b/cookbook/bundles/configuration.rst
new file mode 100644
index 00000000000..9d374d2dc4d
--- /dev/null
+++ b/cookbook/bundles/configuration.rst
@@ -0,0 +1,399 @@
+.. index::
+ single: Configuration; Semantic
+ single: Bundle; Extension configuration
+
+How to Create Friendly Configuration for a Bundle
+=================================================
+
+If you open your application configuration file (usually ``app/config/config.yml``),
+you'll see a number of different configuration "namespaces", such as ``framework``,
+``twig`` and ``doctrine``. Each of these configures a specific bundle, allowing
+you to configure things at a high level and then let the bundle make all the
+low-level, complex changes based on your settings.
+
+For example, the following tells the FrameworkBundle to enable the form
+integration, which involves the definition of quite a few services as well
+as integration of other related components:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ framework:
+ form: true
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ $container->loadFromExtension('framework', array(
+ 'form' => true,
+ ));
+
+.. sidebar:: Using Parameters to Configure your Bundle
+
+ If you don't have plans to share your bundle between projects, it doesn't
+ make sense to use this more advanced way of configuration. Since you use
+ the bundle only in one project, you can just change the service
+ configuration each time.
+
+ If you *do* want to be able to configure something from within
+ ``config.yml``, you can always create a parameter there and use that
+ parameter somewhere else.
+
+Using the Bundle Extension
+--------------------------
+
+The basic idea is that instead of having the user override individual
+parameters, you let the user configure just a few, specifically created,
+options. As the bundle developer, you then parse through that configuration and
+load correct services and parameters inside an "Extension" class.
+
+As an example, imagine you are creating a social bundle, which provides
+integration with Twitter and such. To be able to reuse your bundle, you have to
+make the ``client_id`` and ``client_secret`` variables configurable. Your
+bundle configuration would look like:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ acme_social:
+ twitter:
+ client_id: 123
+ client_secret: $ecret
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container->loadFromExtension('acme_social', array(
+ 'client_id' => 123,
+ 'client_secret' => '$ecret',
+ ));
+
+.. seealso::
+
+ Read more about the extension in :doc:`/cookbook/bundles/extension`.
+
+.. tip::
+
+ If a bundle provides an Extension class, then you should *not* generally
+ override any service container parameters from that bundle. The idea
+ is that if an Extension class is present, every setting that should be
+ configurable should be present in the configuration made available by
+ that class. In other words, the extension class defines all the public
+ configuration settings for which backward compatibility will be maintained.
+
+.. seealso::
+
+ For parameter handling within a Dependency Injection class see
+ :doc:`/cookbook/configuration/using_parameters_in_dic`.
+
+
+Processing the ``$configs`` Array
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+First things first, you have to create an extension class as explained in
+:doc:`extension`.
+
+Whenever a user includes the ``acme_social`` key (which is the DI alias) in a
+configuration file, the configuration under it is added to an array of
+configurations and passed to the ``load()`` method of your extension (Symfony2
+automatically converts XML and YAML to an array).
+
+For the configuration example in the previous section, the array passed to your
+``load()`` method will look like this::
+
+ array(
+ array(
+ 'twitter' => array(
+ 'client_id' => 123,
+ 'client_secret' => '$ecret',
+ ),
+ ),
+ )
+
+Notice that this is an *array of arrays*, not just a single flat array of the
+configuration values. This is intentional, as it allows Symfony to parse
+several configuration resources. For example, if ``acme_social`` appears in
+another configuration file - say ``config_dev.yml`` - with different values
+beneath it, the incoming array might look like this::
+
+ array(
+ // values from config.yml
+ array(
+ 'twitter' => array(
+ 'client_id' => 123,
+ 'client_secret' => '$secret',
+ ),
+ ),
+ // values from config_dev.yml
+ array(
+ 'twitter' => array(
+ 'client_id' => 456,
+ ),
+ ),
+ )
+
+The order of the two arrays depends on which one is set first.
+
+But don't worry! Symfony's Config component will help you merge these values,
+provide defaults and give the user validation errors on bad configuration.
+Here's how it works. Create a ``Configuration`` class in the
+``DependencyInjection`` directory and build a tree that defines the structure
+of your bundle's configuration.
+
+The ``Configuration`` class to handle the sample configuration looks like::
+
+ // src/Acme/SocialBundle/DependencyInjection/Configuration.php
+ namespace Acme\SocialBundle\DependencyInjection;
+
+ use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+ use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+ class Configuration implements ConfigurationInterface
+ {
+ public function getConfigTreeBuilder()
+ {
+ $treeBuilder = new TreeBuilder();
+ $rootNode = $treeBuilder->root('acme_social');
+
+ $rootNode
+ ->children()
+ ->arrayNode('twitter')
+ ->children()
+ ->integerNode('client_id')->end()
+ ->scalarNode('client_secret')->end()
+ ->end()
+ ->end() // twitter
+ ->end()
+ ;
+
+ return $treeBuilder;
+ }
+ }
+
+.. seealso::
+
+ The ``Configuration`` class can be much more complicated than shown here,
+ supporting "prototype" nodes, advanced validation, XML-specific normalization
+ and advanced merging. You can read more about this in
+ :doc:`the Config component documentation `. You
+ can also see it in action by checking out some of the core Configuration
+ classes, such as the one from the `FrameworkBundle Configuration`_ or the
+ `TwigBundle Configuration`_.
+
+This class can now be used in your ``load()`` method to merge configurations and
+force validation (e.g. if an additional option was passed, an exception will be
+thrown)::
+
+ public function load(array $configs, ContainerBuilder $container)
+ {
+ $configuration = new Configuration();
+
+ $config = $this->processConfiguration($configuration, $configs);
+ // ...
+ }
+
+The ``processConfiguration()`` method uses the configuration tree you've defined
+in the ``Configuration`` class to validate, normalize and merge all of the
+configuration arrays together.
+
+.. tip::
+
+ Instead of calling ``processConfiguration()`` in your extension each time you
+ provide some configuration options, you might want to use the
+ :class:`Symfony\\Component\\HttpKernel\\DependencyInjection\\ConfigurableExtension`
+ to do this automatically for you::
+
+ // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+ namespace Acme\HelloBundle\DependencyInjection;
+
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
+
+ class AcmeHelloExtension extends ConfigurableExtension
+ {
+ // note that this method is called loadInternal and not load
+ protected function loadInternal(array $mergedConfig, ContainerBuilder $container)
+ {
+ // ...
+ }
+ }
+
+ This class uses the ``getConfiguration()`` method to get the Configuration
+ instance, you should override it if your Configuration class is not called
+ ``Configuration`` or if it is not placed in the same namespace as the
+ extension.
+
+.. sidebar:: Processing the Configuration yourself
+
+ Using the Config component is fully optional. The ``load()`` method gets an
+ array of configuration values. You can simply parse these arrays yourself
+ (e.g. by overriding configurations and using :phpfunction:`isset` to check
+ for the existence of a value). Be aware that it'll be very hard to support XML.
+
+ .. code-block:: php
+
+ public function load(array $configs, ContainerBuilder $container)
+ {
+ $config = array();
+ // let resources override the previous set value
+ foreach ($configs as $subConfig) {
+ $config = array_merge($config, $subConfig);
+ }
+
+ // ... now use the flat $config array
+ }
+
+Modifying the Configuration of Another Bundle
+---------------------------------------------
+
+If you have multiple bundles that depend on each other, it may be useful
+to allow one ``Extension`` class to modify the configuration passed to another
+bundle's ``Extension`` class, as if the end-developer has actually placed that
+configuration in their ``app/config/config.yml`` file. This can be achieved
+using a prepend extension. For more details, see
+:doc:`/cookbook/bundles/prepend_extension`.
+
+Dump the Configuration
+----------------------
+
+The ``config:dump-reference`` command dumps the default configuration of a
+bundle in the console using the Yaml format.
+
+As long as your bundle's configuration is located in the standard location
+(``YourBundle\DependencyInjection\Configuration``) and does not require
+arguments to be passed to the constructor it will work automatically. If you
+have something different, your ``Extension`` class must override the
+:method:`Extension::getConfiguration() `
+method and return an instance of your ``Configuration``.
+
+Supporting XML
+--------------
+
+Symfony allows people to provide the configuration in three different formats:
+Yaml, XML and PHP. Both Yaml and PHP use the same syntax and are supported by
+default when using the Config component. Supporting XML requires you to do some
+more things. But when sharing your bundle with others, it is recommended that
+you follow these steps.
+
+Make your Config Tree ready for XML
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Config component provides some methods by default to allow it to correctly
+process XML configuration. See ":ref:`component-config-normalization`" of the
+component documentation. However, you can do some optional things as well, this
+will improve the experience of using XML configuration:
+
+Choosing an XML Namespace
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In XML, the `XML namespace`_ is used to determine which elements belong to the
+configuration of a specific bundle. The namespace is returned from the
+:method:`Extension::getNamespace() `
+method. By convention, the namespace is a URL (it doesn't have to be a valid
+URL nor does it need to exists). By default, the namespace for a bundle is
+``http://example.org/dic/schema/DI_ALIAS``, where ``DI_ALIAS`` is the DI alias of
+the extension. You might want to change this to a more professional URL::
+
+ // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+
+ // ...
+ class AcmeHelloExtension extends Extension
+ {
+ // ...
+
+ public function getNamespace()
+ {
+ return 'http://acme_company.com/schema/dic/hello';
+ }
+ }
+
+Providing an XML Schema
+~~~~~~~~~~~~~~~~~~~~~~~
+
+XML has a very useful feature called `XML schema`_. This allows you to
+describe all possible elements and attributes and their values in an XML Schema
+Definition (an xsd file). This XSD file is used by IDEs for auto completion and
+it is used by the Config component to validate the elements.
+
+In order to use the schema, the XML configuration file must provide an
+``xsi:schemaLocation`` attribute pointing to the XSD file for a certain XML
+namespace. This location always starts with the XML namespace. This XML
+namespace is then replaced with the XSD validation base path returned from
+:method:`Extension::getXsdValidationBasePath() `
+method. This namespace is then followed by the rest of the path from the base
+path to the file itself.
+
+By convention, the XSD file lives in the ``Resources/config/schema``, but you
+can place it anywhere you like. You should return this path as the base path::
+
+ // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+
+ // ...
+ class AcmeHelloExtension extends Extension
+ {
+ // ...
+
+ public function getXsdValidationBasePath()
+ {
+ return __DIR__.'/../Resources/config/schema';
+ }
+ }
+
+Assume the XSD file is called ``hello-1.0.xsd``, the schema location will be
+``http://acme_company.com/schema/dic/hello/hello-1.0.xsd``:
+
+.. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+.. _`FrameworkBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+.. _`TwigBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php
+.. _`XML namespace`: http://en.wikipedia.org/wiki/XML_namespace
+.. _`XML schema`: http://en.wikipedia.org/wiki/XML_schema
diff --git a/cookbook/bundles/extension.rst b/cookbook/bundles/extension.rst
index d35580aabca..cc4ce322399 100644
--- a/cookbook/bundles/extension.rst
+++ b/cookbook/bundles/extension.rst
@@ -2,113 +2,36 @@
single: Configuration; Semantic
single: Bundle; Extension configuration
-How to Expose a semantic Configuration for a Bundle
-===================================================
+How to Load Service Configuration inside a Bundle
+=================================================
-If you open your application configuration file (usually ``app/config/config.yml``),
-you'll see a number of different configuration "namespaces", such as ``framework``,
-``twig``, and ``doctrine``. Each of these configures a specific bundle, allowing
-you to configure things at a high level and then let the bundle make all the
-low-level, complex changes that result.
+In Symfony, you'll find yourself using many services. These services can be
+registered in the `app/config` directory of your application. But when you
+want to decouple the bundle for use in other projects, you want to include the
+service configuration in the bundle itself. This article will teach you how to
+do that.
-For example, the following tells the FrameworkBundle to enable the form
-integration, which involves the defining of quite a few services as well
-as integration of other related components:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- framework:
- # ...
- form: true
-
- .. code-block:: xml
-
-
-
-
-
- .. code-block:: php
-
- $container->loadFromExtension('framework', array(
- // ...
- 'form' => true,
- // ...
- ));
-
-When you create a bundle, you have two choices on how to handle configuration:
-
-1. **Normal Service Configuration** (*easy*):
-
- You can specify your services in a configuration file (e.g. ``services.yml``)
- that lives in your bundle and then import it from your main application
- configuration. This is really easy, quick and totally effective. If you
- make use of :ref:`parameters `, then
- you still have the flexibility to customize your bundle from your application
- configuration. See ":ref:`service-container-imports-directive`" for more
- details.
-
-2. **Exposing Semantic Configuration** (*advanced*):
-
- This is the way configuration is done with the core bundles (as described
- above). The basic idea is that, instead of having the user override individual
- parameters, you let the user configure just a few, specifically created
- options. As the bundle developer, you then parse through that configuration
- and load services inside an "Extension" class. With this method, you won't
- need to import any configuration resources from your main application
- configuration: the Extension class can handle all of this.
-
-The second option - which you'll learn about in this article - is much more
-flexible, but also requires more time to setup. If you're wondering which
-method you should use, it's probably a good idea to start with method #1,
-and then change to #2 later if you need to. If you plan to distribute your
-bundle, the second option is recommended.
-
-The second method has several specific advantages:
-
-* Much more powerful than simply defining parameters: a specific option value
- might trigger the creation of many service definitions;
-
-* Ability to have configuration hierarchy;
-
-* Smart merging when several configuration files (e.g. ``config_dev.yml``
- and ``config.yml``) override each other's configuration;
-
-* Configuration validation (if you use a :ref:`Configuration Class `);
-
-* IDE auto-completion when you create an XSD and developers use XML.
-
-.. sidebar:: Overriding bundle parameters
-
- If a Bundle provides an Extension class, then you should generally *not*
- override any service container parameters from that bundle. The idea
- is that if an Extension class is present, every setting that should be
- configurable should be present in the configuration made available by
- that class. In other words the extension class defines all the publicly
- supported configuration settings for which backward compatibility will
- be maintained.
-
-.. seealso::
+Creating an Extension Class
+---------------------------
- For parameter handling within a Dependency Injection class see
- :doc:`/cookbook/configuration/using_parameters_in_dic`.
+In order to load service configuration, you have to create a Dependency
+Injection Extension for your bundle. This class has some conventions in order
+to be detected automatically. But you'll later see how you can change it to
+your own preferences. By default, the Extension has to comply with the
+following conventions:
-.. index::
- single: Bundle; Extension
- single: DependencyInjection; Extension
+* It has to live in the ``DependencyInjection`` namespace of the bundle;
-Creating an Extension Class
----------------------------
+* The name is equal to the bundle name with the ``Bundle`` suffix replaced by
+ ``Extension`` (e.g. the Extension class of ``AcmeHelloBundle`` would be
+ called ``AcmeHelloExtension``).
-If you do choose to expose a semantic configuration for your bundle, you'll
-first need to create a new "Extension" class, which will handle the process.
-This class should live in the ``DependencyInjection`` directory of your bundle
-and its name should be constructed by replacing the ``Bundle`` suffix of the
-Bundle class name with ``Extension``. For example, the Extension class of
-``AcmeHelloBundle`` would be called ``AcmeHelloExtension``::
+The Extension class should implement the
+:class:`Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface`,
+but usually you would simply extend the
+:class:`Symfony\\Component\\DependencyInjection\\Extension\\Extension` class::
- // Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+ // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
@@ -118,496 +41,80 @@ Bundle class name with ``Extension``. For example, the Extension class of
{
public function load(array $configs, ContainerBuilder $container)
{
- // ... where all of the heavy logic is done
- }
-
- public function getXsdValidationBasePath()
- {
- return __DIR__.'/../Resources/config/';
- }
-
- public function getNamespace()
- {
- return 'http://www.example.com/symfony/schema/';
+ // ... you'll load the files here later
}
}
-.. note::
-
- The ``getXsdValidationBasePath`` and ``getNamespace`` methods are only
- required if the bundle provides optional XSD's for the configuration.
-
-The presence of the previous class means that you can now define an ``acme_hello``
-configuration namespace in any configuration file. The namespace ``acme_hello``
-is constructed from the extension's class name by removing the word ``Extension``
-and then lowercasing and underscoring the rest of the name. In other words,
-``AcmeHelloExtension`` becomes ``acme_hello``.
-
-You can begin specifying configuration under this namespace immediately:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- acme_hello: ~
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('acme_hello', array());
-
-.. tip::
-
- If you follow the naming conventions laid out above, then the ``load()``
- method of your extension code is always called as long as your bundle
- is registered in the Kernel. In other words, even if the user does not
- provide any configuration (i.e. the ``acme_hello`` entry doesn't even
- appear), the ``load()`` method will be called and passed an empty ``$configs``
- array. You can still provide some sensible defaults for your bundle if
- you want.
-
-Registering the Extension Class
--------------------------------
-
-An Extension class will automatically be registered by Symfony2 when
-following these simple conventions:
-
-* The extension must be stored in the ``DependencyInjection`` sub-namespace;
-
-* The extension must be named after the bundle name and suffixed with
- ``Extension`` (``AcmeHelloExtension`` for ``AcmeHelloBundle``);
-
-* The extension *should* provide an XSD schema (but will be registered automatically
- regardless).
-
Manually Registering an Extension Class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When not following the conventions, you will have to manually register your
-extension. To manually register an extension class override the
-:method:`Bundle::build() `
-method in your bundle::
+extension. To do this, you should override the
+:method:`Bundle::getContainerExtension() `
+method to return the instance of the extension::
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
class AcmeHelloBundle extends Bundle
{
- public function build(ContainerBuilder $container)
+ public function getContainerExtension()
{
- parent::build($container);
-
- // register extensions that do not follow the conventions manually
- $container->registerExtension(new UnconventionalExtensionClass());
- }
- }
-
-In this case, the extension class must also implement a ``getAlias()`` method
-and return a unique alias named after the bundle (e.g. ``acme_hello``). This
-is required because the class name doesn't follow the conventions by ending
-in ``Extension``.
-
-Additionally, the ``load()`` method of your extension will *only* be called
-if the user specifies the ``acme_hello`` alias in at least one configuration
-file. Once again, this is because the Extension class doesn't follow the
-conventions set out above, so nothing happens automatically.
-
-Parsing the ``$configs`` Array
-------------------------------
-
-Whenever a user includes the ``acme_hello`` namespace in a configuration file,
-the configuration under it is added to an array of configurations and
-passed to the ``load()`` method of your extension (Symfony2 automatically
-converts XML and YAML to an array).
-
-Take the following configuration:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- acme_hello:
- foo: fooValue
- bar: barValue
-
- .. code-block:: xml
-
-
-
-
-
-
-
- barValue
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('acme_hello', array(
- 'foo' => 'fooValue',
- 'bar' => 'barValue',
- ));
-
-The array passed to your ``load()`` method will look like this::
-
- array(
- array(
- 'foo' => 'fooValue',
- 'bar' => 'barValue',
- ),
- )
-
-Notice that this is an *array of arrays*, not just a single flat array of the
-configuration values. This is intentional. For example, if ``acme_hello``
-appears in another configuration file - say ``config_dev.yml`` - with different
-values beneath it, then the incoming array might look like this::
-
- array(
- array(
- 'foo' => 'fooValue',
- 'bar' => 'barValue',
- ),
- array(
- 'foo' => 'fooDevValue',
- 'baz' => 'newConfigEntry',
- ),
- )
-
-The order of the two arrays depends on which one is set first.
-
-It's your job, then, to decide how these configurations should be merged
-together. You might, for example, have later values override previous values
-or somehow merge them together.
-
-Later, in the :ref:`Configuration Class `
-section, you'll learn of a truly robust way to handle this. But for now,
-you might just merge them manually::
-
- public function load(array $configs, ContainerBuilder $container)
- {
- $config = array();
- foreach ($configs as $subConfig) {
- $config = array_merge($config, $subConfig);
+ return new UnconventionalExtensionClass();
}
-
- // ... now use the flat $config array
}
-.. caution::
-
- Make sure the above merging technique makes sense for your bundle. This
- is just an example, and you should be careful to not use it blindly.
+Since the new Extension class name doesn't follow the naming conventions, you
+should also override
+:method:`Extension::getAlias() `
+to return the correct DI alias. The DI alias is the name used to refer to the
+bundle in the container (e.g. in the ``app/config/config.yml`` file). By
+default, this is done by removing the ``Extension`` prefix and converting the
+class name to underscores (e.g. ``AcmeHelloExtension``'s DI alias is
+``acme_hello``).
Using the ``load()`` Method
---------------------------
-Within ``load()``, the ``$container`` variable refers to a container that only
-knows about this namespace configuration (i.e. it doesn't contain service
-information loaded from other bundles). The goal of the ``load()`` method
-is to manipulate the container, adding and configuring any methods or services
-needed by your bundle.
+In the ``load()`` method, all services and parameters related to this extension
+will be loaded. This method doesn't get the actual container instance, but a
+copy. This container only has the parameters from the actual container. After
+loading the services and parameters, the copy will be merged into the actual
+container, to ensure all services and parameters are also added to the actual
+container.
-Loading external Configuration Resources
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+In the ``load()`` method, you can use PHP code to register service definitions,
+but it is more common if you put the these definitions in a configuration file
+(using the Yaml, XML or PHP format). Luckily, you can use the file loaders in
+the extension!
-One common thing to do is to load an external configuration file that may
-contain the bulk of the services needed by your bundle. For example, suppose
-you have a ``services.xml`` file that holds much of your bundle's service
-configuration::
+For instance, assume you have a file called ``services.xml`` in the
+``Resources/config`` directory of your bundle, your load method looks like::
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
+ // ...
public function load(array $configs, ContainerBuilder $container)
{
- // ... prepare your $config variable
-
- $loader = new XmlFileLoader(
- $container,
- new FileLocator(__DIR__.'/../Resources/config')
- );
- $loader->load('services.xml');
- }
-
-You might even do this conditionally, based on one of the configuration values.
-For example, suppose you only want to load a set of services if an ``enabled``
-option is passed and set to true::
-
- public function load(array $configs, ContainerBuilder $container)
- {
- // ... prepare your $config variable
-
- $loader = new XmlFileLoader(
- $container,
- new FileLocator(__DIR__.'/../Resources/config')
- );
-
- if (isset($config['enabled']) && $config['enabled']) {
- $loader->load('services.xml');
- }
- }
-
-Configuring Services and Setting Parameters
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Once you've loaded some service configuration, you may need to modify the
-configuration based on some of the input values. For example, suppose you
-have a service whose first argument is some string "type" that it will use
-internally. You'd like this to be easily configured by the bundle user, so
-in your service configuration file (e.g. ``services.xml``), you define this
-service and use a blank parameter - ``acme_hello.my_service_type`` - as
-its first argument:
-
-.. code-block:: xml
-
-
-
-
-
-
-
-
-
-
- %acme_hello.my_service_type%
-
-
-
-
-But why would you define an empty parameter and then pass it to your service?
-The answer is that you'll set this parameter in your extension class, based
-on the incoming configuration values. Suppose, for example, that you want
-to allow the user to define this *type* option under a key called ``my_type``.
-Add the following to the ``load()`` method to do this::
-
- public function load(array $configs, ContainerBuilder $container)
- {
- // ... prepare your $config variable
-
$loader = new XmlFileLoader(
$container,
new FileLocator(__DIR__.'/../Resources/config')
);
$loader->load('services.xml');
-
- if (!isset($config['my_type'])) {
- throw new \InvalidArgumentException(
- 'The "my_type" option must be set'
- );
- }
-
- $container->setParameter(
- 'acme_hello.my_service_type',
- $config['my_type']
- );
}
-Now, the user can effectively configure the service by specifying the ``my_type``
-configuration value:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- acme_hello:
- my_type: foo
- # ...
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('acme_hello', array(
- 'my_type' => 'foo',
- ...,
- ));
-
-Global Parameters
-~~~~~~~~~~~~~~~~~
-
-When you're configuring the container, be aware that you have the following
-global parameters available to use:
-
-* ``kernel.name``
-* ``kernel.environment``
-* ``kernel.debug``
-* ``kernel.root_dir``
-* ``kernel.cache_dir``
-* ``kernel.logs_dir``
-* ``kernel.bundles``
-* ``kernel.charset``
-
-.. caution::
-
- All parameter and service names starting with a ``_`` are reserved for the
- framework, and new ones must not be defined by bundles.
-
-.. _cookbook-bundles-extension-config-class:
-
-Validation and Merging with a Configuration Class
--------------------------------------------------
-
-So far, you've done the merging of your configuration arrays by hand and
-are checking for the presence of config values manually using the ``isset()``
-PHP function. An optional *Configuration* system is also available which
-can help with merging, validation, default values, and format normalization.
+Other available loaders are the ``YamlFileLoader``, ``PhpFileLoader`` and
+``IniFileLoader``.
.. note::
- Format normalization refers to the fact that certain formats - largely XML -
- result in slightly different configuration arrays and that these arrays
- need to be "normalized" to match everything else.
-
-To take advantage of this system, you'll create a ``Configuration`` class
-and build a tree that defines your configuration in that class::
-
- // src/Acme/HelloBundle/DependencyInjection/Configuration.php
- namespace Acme\HelloBundle\DependencyInjection;
-
- use Symfony\Component\Config\Definition\Builder\TreeBuilder;
- use Symfony\Component\Config\Definition\ConfigurationInterface;
-
- class Configuration implements ConfigurationInterface
- {
- public function getConfigTreeBuilder()
- {
- $treeBuilder = new TreeBuilder();
- $rootNode = $treeBuilder->root('acme_hello');
-
- $rootNode
- ->children()
- ->scalarNode('my_type')->defaultValue('bar')->end()
- ->end();
-
- return $treeBuilder;
- }
- }
-
-This is a *very* simple example, but you can now use this class in your ``load()``
-method to merge your configuration and force validation. If any options other
-than ``my_type`` are passed, the user will be notified with an exception
-that an unsupported option was passed::
-
- public function load(array $configs, ContainerBuilder $container)
- {
- $configuration = new Configuration();
-
- $config = $this->processConfiguration($configuration, $configs);
-
- // ...
- }
-
-The ``processConfiguration()`` method uses the configuration tree you've defined
-in the ``Configuration`` class to validate, normalize and merge all of the
-configuration arrays together.
-
-The ``Configuration`` class can be much more complicated than shown here,
-supporting array nodes, "prototype" nodes, advanced validation, XML-specific
-normalization and advanced merging. You can read more about this in
-:doc:`the Config component documentation `.
-You can also see it in action by checking out some of the core Configuration classes,
-such as the one from the `FrameworkBundle Configuration`_ or the `TwigBundle Configuration`_.
-
-Modifying the Configuration of another Bundle
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you have multiple bundles that depend on each other, it may be useful
-to allow one ``Extension`` class to modify the configuration passed to another
-bundle's ``Extension`` class, as if the end-developer has actually placed that
-configuration in their ``app/config/config.yml`` file.
-
-For more details, see :doc:`/cookbook/bundles/prepend_extension`.
-
-Default Configuration Dump
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``config:dump-reference`` command allows a bundle's default configuration to
-be output to the console in YAML.
-
-As long as your bundle's configuration is located in the standard location
-(``YourBundle\DependencyInjection\Configuration``) and does not have a
-``__construct()`` it will work automatically. If you have something
-different, your ``Extension`` class must override the
-:method:`Extension::getConfiguration() `
-method and return an instance of your
-``Configuration``.
-
-Comments and examples can be added to your configuration nodes using the
-``->info()`` and ``->example()`` methods::
-
- // src/Acme/HelloBundle/DependencyExtension/Configuration.php
- namespace Acme\HelloBundle\DependencyInjection;
-
- use Symfony\Component\Config\Definition\Builder\TreeBuilder;
- use Symfony\Component\Config\Definition\ConfigurationInterface;
-
- class Configuration implements ConfigurationInterface
- {
- public function getConfigTreeBuilder()
- {
- $treeBuilder = new TreeBuilder();
- $rootNode = $treeBuilder->root('acme_hello');
-
- $rootNode
- ->children()
- ->scalarNode('my_type')
- ->defaultValue('bar')
- ->info('what my_type configures')
- ->example('example setting')
- ->end()
- ->end()
- ;
+ The ``IniFileLoader`` can only be used to load parameters and it can only
+ load them as strings.
- return $treeBuilder;
- }
- }
-
-This text appears as YAML comments in the output of the ``config:dump-reference``
-command.
-
-.. index::
- pair: Convention; Configuration
+Using Configuration to Change the Services
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. _`FrameworkBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
-.. _`TwigBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php
+The Extension is also the class that handles the configuration for that
+particular bundle (e.g. the configuration in ``app/config/config.yml``). To
+read more about it, see the ":doc:`/cookbook/bundles/configuration`" article.
diff --git a/cookbook/bundles/index.rst b/cookbook/bundles/index.rst
index df0cf217b9d..87641de5e23 100644
--- a/cookbook/bundles/index.rst
+++ b/cookbook/bundles/index.rst
@@ -10,4 +10,5 @@ Bundles
override
remove
extension
+ configuration
prepend_extension
diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc
index 20992e334b9..8f6486c0115 100644
--- a/cookbook/map.rst.inc
+++ b/cookbook/map.rst.inc
@@ -14,6 +14,7 @@
* :doc:`/cookbook/bundles/override`
* :doc:`/cookbook/bundles/remove`
* :doc:`/cookbook/bundles/extension`
+ * :doc:`/cookbook/bundles/configuration`
* :doc:`/cookbook/bundles/prepend_extension`
* :doc:`/cookbook/cache/index`