@@ -22,6 +22,220 @@ validity, further compiler passes are used to optimize the configuration
22
22
before it is cached. For example, private services and abstract services
23
23
are removed, and aliases are resolved.
24
24
25
+ Managing Configuration with Extensions
26
+ --------------------------------------
27
+
28
+ As well as loading configuration directly into the container as shown in
29
+ :doc: `/components/dependency_injection/introduction `, you can manage it by
30
+ registering extensions with the container. The first step in the compilation
31
+ process is to load configuration from any extension classes registered with
32
+ the container. Unlike the configuration loaded directly they are only processed
33
+ when the container is compiled. If your application is modular then extensions
34
+ allow each module to register and manage their own service configuration.
35
+
36
+ The extensions must implement :class: `Symfony\\ Component\\ DependencyInjection\\ Extension\\ ExtensionInterface `
37
+ and can be registered with the container with::
38
+
39
+ $container->registerExtension($extension);
40
+
41
+ The main work of the extension is done in the ``load `` method. In the load method
42
+ you can load configuration from one or more configuration files as well as
43
+ manipulate the container definitions using the methods shown in :doc: `/components/dependency_injection/definitions `.
44
+
45
+ The ``load `` method is passed a fresh container to set up, which is then
46
+ merged afterwards into the container it is registered with. This allows you
47
+ to have several extensions managing container definitions independently.
48
+ The extensions do not add to the containers configuration when they are added
49
+ but are processed when the container's ``compile `` method is called.
50
+
51
+ A very simple extension may just load configuration files into the container::
52
+
53
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
54
+ use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
55
+ use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
56
+ use Symfony\Component\Config\FileLocator;
57
+
58
+ class AcmeDemoExtension implements ExtensionInterface
59
+ {
60
+ public function load(array $configs, ContainerBuilder $container)
61
+ {
62
+ $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
63
+ $loader->load('services.xml');
64
+ }
65
+
66
+ // ...
67
+ }
68
+
69
+ This does not gain very much compared to loading the file directly into the
70
+ overall container being built. It just allows the files to be split up amongst
71
+ the modules/bundles. Being able to affect the configuration of a module from
72
+ configuration files outside of the module/bundle is needed to make a complex
73
+ application configurable. This can be done by specifying sections of config files
74
+ loaded directly into the container as being for a particular extension. These
75
+ sections on the config will not be processed directly by the container but by the
76
+ relevant Extension.
77
+
78
+ The Extension must specify a ``getAlias `` method to implement the interface::
79
+
80
+ // ...
81
+
82
+ class AcmeDemoExtension implements ExtensionInterface
83
+ {
84
+ // ...
85
+
86
+ public function getAlias()
87
+ {
88
+ return 'acme_demo';
89
+ }
90
+ }
91
+
92
+ For YAML configuration files specifying the alias for the Extension as a key
93
+ will mean that those values are passed to the Extension's ``load `` method:
94
+
95
+ .. code-block :: yaml
96
+ # ...
97
+
98
+ acme_demo :
99
+ foo : fooValue
100
+ bar : barValue
101
+
102
+ If this file is loaded into the configuration then the values in it are only
103
+ processed when the container is compiled at which point the Extensions are loaded::
104
+
105
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
106
+ use Symfony\Component\Config\FileLocator;
107
+ use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
108
+
109
+ $container = new ContainerBuilder();
110
+ $loader = new YamlFileLoader($container, new FileLocator(__DIR__));
111
+ $loader->load('config.yml');
112
+
113
+ $container->registerExtension(new AcmeDemoExtension);
114
+ // ...
115
+ $container->compile();
116
+
117
+ The values from those sections of the config files are passed into the first
118
+ argument of the ``load `` method of the extension::
119
+
120
+ public function load(array $configs, ContainerBuilder $container)
121
+ {
122
+ $foo = $configs[0]['foo']; //fooValue
123
+ $bar = $configs[0]['bar']; //barValue
124
+ }
125
+
126
+ The ``$configs `` argument is an array containing each different config file
127
+ that was loaded into the container. You are only loading a single config file
128
+ in the above example but it will still be within an array. The array will look
129
+ like this::
130
+
131
+ array(
132
+ array(
133
+ 'foo' => 'fooValue',
134
+ 'bar' => 'barValue',
135
+ )
136
+ )
137
+
138
+ Whilst you can manually manage merging the different files, it is much better
139
+ to use :doc: `the Config Component</components/config/introduction> ` to merge
140
+ and validate the config values. Using the configuration processing you could
141
+ access the config value this way::
142
+
143
+ use Symfony\Component\Config\Definition\Processor;
144
+ // ...
145
+
146
+ public function load(array $configs, ContainerBuilder $container)
147
+ {
148
+ $configuration = new Configuration();
149
+ $processor = new Processor();
150
+ $config = $processor->processConfiguration($configuration, $configs);
151
+
152
+ $foo = $config['foo']; //fooValue
153
+ $bar = $config['bar']; //barValue
154
+
155
+ // ...
156
+ }
157
+
158
+ There are a further two methods you must implement. One to return the XML
159
+ namespace so that the relevant parts of an XML config file are passed to
160
+ the extension. The other to specify the base path to XSD files to validate
161
+ the XML configuration::
162
+
163
+ public function getXsdValidationBasePath()
164
+ {
165
+ return __DIR__.'/../Resources/config/';
166
+ }
167
+
168
+ public function getNamespace()
169
+ {
170
+ return 'http://www.example.com/symfony/schema/';
171
+ }
172
+
173
+ ..note::
174
+ XSD validation is optional, returning ``false`` from the ``getXsdValidationBasePath``
175
+ method will disable it.
176
+
177
+ The XML version of the config would then look like this:
178
+
179
+ .. code-block :: xml
180
+ <?xml version =" 1.0" ?>
181
+
182
+ <container xmlns =" http://symfony.com/schema/dic/services"
183
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
184
+ xmlns : acme_demo =" http://www.example.com/symfony/schema/"
185
+ xsi : schemaLocation =" http://www.example.com/symfony/schema/ http://www.example.com/symfony/schema/hello-1.0.xsd" >
186
+
187
+ <acme_demo : config >
188
+ <acme_demo : foo >fooValue</acme_hello : foo >
189
+ <acme_demo : bar >barValue</acme_demo : bar >
190
+ </acme_demo : config >
191
+
192
+ </container >
193
+
194
+ ..note::
195
+ In the Symfony2 full stack framework there is a base Extension class which
196
+ implements these methods as well as a short cut method for processing the
197
+ configuration. See :doc:`/cookbook/bundles/extension` for more details.
198
+
199
+ The processed config value can now be added as container parameters as if they were
200
+ listed in a ``parameters `` section of the config file but with merging multiple files
201
+ and validation of the configuration thrown in::
202
+
203
+ public function load(array $configs, ContainerBuilder $container)
204
+ {
205
+ $configuration = new Configuration();
206
+ $processor = new Processor();
207
+ $config = $processor->processConfiguration($configuration, $configs);
208
+
209
+ $container->setParameter('acme_demo.FOO', $config['foo'])
210
+
211
+ // ...
212
+ }
213
+
214
+ More complex configuration requirements can be catered for in the Extension
215
+ classes. For example, you may choose to load a main service configuration file
216
+ but also load a secondary one only if a certain parameter is set::
217
+
218
+ public function load(array $configs, ContainerBuilder $container)
219
+ {
220
+ $configuration = new Configuration();
221
+ $processor = new Processor();
222
+ $config = $processor->processConfiguration($configuration, $configs);
223
+
224
+ $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
225
+ $loader->load('services.xml');
226
+
227
+ if ($config['advanced']) {
228
+ $loader->load('advanced.xml');
229
+ }
230
+ }
231
+
232
+ .. note ::
233
+
234
+ If you need to manipulate the configuration loaded by an extension then
235
+ you cannot do it from another extension as it uses a fresh container.
236
+ You should instead use a compiler pass which works with the full container
237
+ after the extensions have been processed.
238
+
25
239
Creating a Compiler Pass
26
240
------------------------
27
241
@@ -85,34 +299,6 @@ For example, to run your custom pass after the default removal passes have been
85
299
$container = new ContainerBuilder();
86
300
$container->addCompilerPass(new CustomCompilerPass, PassConfig::TYPE_AFTER_REMOVING);
87
301
88
-
89
- Managing Configuration with Extensions
90
- --------------------------------------
91
-
92
- As well as loading configuration directly into the container as shown in
93
- :doc: `/components/dependency_injection/introduction `, you can manage it by registering
94
- extensions with the container. The extensions must implement :class: `Symfony\\ Component\\ DependencyInjection\\ Extension\\ ExtensionInterface `
95
- and can be registered with the container with::
96
-
97
- $container->registerExtension($extension);
98
-
99
- The main work of the extension is done in the ``load `` method. In the load method
100
- you can load configuration from one or more configuration files as well as
101
- manipulate the container definitions using the methods shown in :doc: `/components/dependency_injection/definitions `.
102
-
103
- The ``load `` method is passed a fresh container to set up, which is then
104
- merged afterwards into the container it is registered with. This allows you
105
- to have several extensions managing container definitions independently.
106
- The extensions do not add to the containers configuration when they are added
107
- but are processed when the container's ``compile `` method is called.
108
-
109
- .. note ::
110
-
111
- If you need to manipulate the configuration loaded by an extension then
112
- you cannot do it from another extension as it uses a fresh container.
113
- You should instead use a compiler pass which works with the full container
114
- after the extensions have been processed.
115
-
116
302
Dumping the Configuration for Performance
117
303
-----------------------------------------
118
304
@@ -134,7 +320,7 @@ configuration. The ``PhpDumper`` makes dumping the compiled container easy::
134
320
$container = new ProjectServiceContiner();
135
321
} else {
136
322
$container = new ContainerBuilder();
137
- //--
323
+ // ...
138
324
$container->compile();
139
325
140
326
$dumper = new PhpDumper($container);
@@ -180,7 +366,7 @@ but getting an up to date configuration whilst developing your application::
180
366
$container = new MyCachedContainer();
181
367
} else {
182
368
$container = new ContainerBuilder();
183
- //--
369
+ // ...
184
370
$container->compile();
185
371
186
372
if(!$isDebug)
@@ -211,7 +397,7 @@ and use them as metadata for the cache::
211
397
212
398
if (!$containerConfigCache->isFresh()) {
213
399
$containerBuilder = new ContainerBuilder();
214
- //--
400
+ // ...
215
401
$containerBuilder->compile();
216
402
217
403
$dumper = new PhpDumper($containerBuilder);
0 commit comments