How to Create Friendly Configuration for a Bundle

If you open your main application configuration directory (usually
config/packages/), you'll see a number of different files, such as
framework.yaml, twig.yaml and doctrine.yaml. Each of these
configures a specific bundle, allowing you to define options at a high level and
then let the bundle make all the low-level, complex changes based on your
settings.

For example, the following configuration 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:

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:

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.

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 (Symfony
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:

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/packages/dev/acme_social.yaml - with
different values beneath it, the incoming array might look like this:

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:

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):

1
2
3
4
5
6
7
8
9
10

// src/Acme/SocialBundle/DependencyInjection/AcmeSocialExtension.phppublicfunctionload(array$configs,ContainerBuilder$container){$configuration=newConfiguration();$config=$this->processConfiguration($configuration,$configs);// you now have these 2 config keys// $config['twitter']['client_id'] and $config['twitter']['client_secret']}

The processConfiguration() method uses the configuration tree you've defined
in the Configuration class to validate, normalize and merge all the
configuration arrays together.

Now, you can use the $config variable to modify a service provided by your bundle.
For example, imagine your bundle has the following example config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<!-- src/Acme/SocialBundle/Resources/config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><containerxmlns="http://symfony.com/schema/dic/services"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"><services><serviceid="acme.social.twitter_client"class="Acme\SocialBundle\TwitterClient"><argument></argument><!-- will be filled in with client_id dynamically --><argument></argument><!-- will be filled in with client_secret dynamically --></service></services></container>

In your extension, you can load this and dynamically set its arguments:

Instead of calling processConfiguration() in your extension each time you
provide some configuration options, you might want to use the
ConfigurableExtension
to do this automatically for you:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.phpnamespaceAcme\HelloBundle\DependencyInjection;useSymfony\Component\DependencyInjection\ContainerBuilder;useSymfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;classAcmeHelloExtensionextendsConfigurableExtension{// note that this method is called loadInternal and not loadprotectedfunctionloadInternal(array$mergedConfig,ContainerBuilder$container){// ...}}

This class uses the getConfiguration() method to get the Configuration
instance.

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 isset to check
for the existence of a value). Be aware that it'll be very hard to support XML.

1
2
3
4
5
6
7
8
9
10

publicfunctionload(array$configs,ContainerBuilder$container){$config=array();// let resources override the previous set valueforeach($configsas$subConfig){$config=array_merge($config,$subConfig);}// ... now use the flat $config array}

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. This can be achieved using a prepend extension.
For more details, see How to Simplify Configuration of Multiple Bundles.

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 have
a constructor it will work automatically. If you
have something different, your Extension class must override the
Extension::getConfiguration()
method and return an instance of your Configuration.

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.

The Config component provides some methods by default to allow it to correctly
process XML configuration. See "Normalization" of the
component documentation. However, you can do some optional things as well, this
will improve the experience of using XML configuration:

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
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/schema/dic/DI_ALIAS, where DI_ALIAS is the DI alias of
the extension. You might want to change this to a more professional URL:

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
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: