How to Load Service Configuration inside a Bundle
Warning: You are browsing the documentation for Symfony 3.x, which is no longer maintained.
Read the updated version of this page for Symfony 7.1 (the current stable version).
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.
Creating an Extension Class
In order to load service configuration, you have to create a Dependency Injection (DI) 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:
- It has to live in the
DependencyInjection
namespace of the bundle; - The name is equal to the bundle name with the
Bundle
suffix replaced byExtension
(e.g. the Extension class of the AppBundle would be calledAppExtension
and the one for AcmeHelloBundle would be calledAcmeHelloExtension
).
The Extension class must implement the ExtensionInterface, but instead you should extend the Extension class, which already implements the interface and provides some utilities:
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
class AcmeHelloExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
// ... you'll load the files here later
}
}
Manually Registering an Extension Class
When not following the conventions, you will have to manually register your extension. To do this, you should override the Bundle::getContainerExtension() method to return the instance of the extension:
1 2 3 4 5 6 7 8 9 10
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
class AcmeHelloBundle extends Bundle
{
public function getContainerExtension()
{
return new UnconventionalExtensionClass();
}
}
Since the new Extension class name doesn't follow the naming conventions, you
should also override
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
suffix and converting the
class name to underscores (e.g. AcmeHelloExtension
's DI alias is
acme_hello
).
Using the load()
Method
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.
In the load()
method, you can use PHP code to register service definitions,
but it is more common if you put these definitions in a configuration file
(using the Yaml, XML or PHP format). Luckily, you can use the file loaders in
the extension!
For instance, assume you have a file called services.xml
in the
Resources/config
directory of your bundle, your load()
method looks like:
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
// ...
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader(
$container,
new FileLocator(__DIR__.'/../Resources/config')
);
$loader->load('services.xml');
}
Other available loaders are the YamlFileLoader
, PhpFileLoader
and
IniFileLoader
.
Note
The IniFileLoader
can only be used to load parameters and it can only
load them as strings.
Caution
If you removed the default file with service definitions (i.e.
app/config/services.yml
), make sure to also remove it from the
imports
key in app/config/config.yml
.
Using Configuration to Change the Services
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 "How to Create Friendly Configuration for a Bundle" article.
Adding Classes to Compile
3.3
This technique is discouraged and the addClassesToCompile()
method was
deprecated in Symfony 3.3 because modern PHP versions make it unnecessary.
Symfony creates a big classes.php
file in the cache directory to aggregate
the contents of the PHP classes that are used in every request. This reduces the
I/O operations and increases the application performance.
3.2
The addAnnotatedClassesToCompile()
method was introduced in Symfony 3.2.
Your bundles can also add their own classes into this file thanks to the
addClassesToCompile()
and addAnnotatedClassesToCompile()
methods (both
work in the same way, but the second one is for classes that contain PHP
annotations). These methods are provied by the Extension
class from the
HttpKernel component. Define the classes to compile as an array of their
fully qualified class names:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
use AppBundle\Manager\UserManager;
use AppBundle\Utils\Slugger;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
// ...
public function load(array $configs, ContainerBuilder $container)
{
// ...
// this method can't compile classes that contain PHP annotations
$this->addClassesToCompile([
UserManager::class,
Slugger::class,
// ...
]);
// add here only classes that contain PHP annotations
$this->addAnnotatedClassesToCompile([
'AppBundle\\Controller\\DefaultController',
// ...
]);
}
Note
If some class extends from other classes, all its parents are automatically included in the list of classes to compile.
3.2
The option to add classes to compile using patterns was introduced in Symfony 3.2.
The classes to compile can also be added using file path patterns:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
// ...
public function load(array $configs, ContainerBuilder $container)
{
// ...
$this->addClassesToCompile([
'**Bundle\\Manager\\',
// ...
]);
$this->addAnnotatedClassesToCompile([
'**Bundle\\Controller\\',
// ...
]);
}
Patterns are transformed into the actual class namespaces using the classmap
generated by Composer. Therefore, before using these patterns, you must generate
the full classmap executing the dump-autoload
command of Composer.
Caution
This technique can't be used when the classes to compile use the __DIR__
or __FILE__
constants, because their values will change when loading
these classes from the classes.php
file.