![](http://habrastorage.org/getpro/habr/avatars/42b/f2d/505/42bf2d505ae10a5d2b91fc2aadeab739.jpg)
Symfony2 application by section
Observing the mouth of a virtuoso magician and his beautiful helpers, many, however, are focused on something completely different: how does he do it? how is everything arranged inside?
![](https://habrastorage.org/storage/defbdc12/fd177c73/efb06b7b/e5df7d72.gif)
The same thing happens when working with frameworks that do everything for us, but I want to know how they do it, and if necessary, be able to change behavior. Unfortunately, the documentation, no matter how good it is ( and Symfony 2 is already good ), tells how to use all this "magic", but does not reveal the whole essence.
This article is an attempt to understand how the application is initialized and what is the "Symfony2 Kernel".
For those who do not like large texts, a brief outline of the main components and a short description of the actions that take place are immediately attached.
![](https://habrastorage.org/storage/c7e32824/ecf34456/96ebf76b/dcbfba05.png)
Clickable
1. All requests are accepted by the Front Controller (FrontController).
2. Configure autoload classes (autoload).
3. A kernel is created depending on the environment.
4. The kernel starts.
1. The list of Bundles is initialized.
2. A Dependency Injection Container is created.
1. A container is created with the basic parameters.
2. Each bundle modifies a build container.
3. The application configuration is loading.
4. The container is compiled.
1. Extensions are being processed.
2. References to parameters are replaced by real values.
3. The container is put into read-only mode (frozen).
3. Bundles are launched.
As the front controller are ordinary scripts. Examples are scripts from the standard Symfony2 distribution .
All these front controllers are built on the same principle:
The kernel is a class that implements the KernelInterface interface ; its task is to initialize the environment. The core is based on two main components: the Dependency Injection container (the theory can be gleaned, for example, from Fowler ) and the bundle system. The bundle is an analogue of the plugin from Symfony 1.x. You can read more about bundles in the official documentation . Of course, in addition to the kernel interface itself, there is also a standard abstract Kernel implementation , which will be mainly discussed later.
The initialization code looks something like this:
The result of this stage is a fully ready-to-use container, switched to read-only mode. This process is divided into 4 sub-steps:
As noted above, during the compilation process, each bundle has the opportunity to make modifications to a common container. The main way to modify is to add compiler passes
At the compilation stage, the container performs a series of passes to bring its contents to its final state. Passes are an implementation of the CompilerPassInterface interface and come in 6 types (in order of execution): merge, beforeOptimization, optimization, beforeRemoving, removing, afterRemoving - by default , the compiler configuration already contains a set of passwords.
In bundles, CompilerPass is most often used to process tags in a container . Example from a standard TwigBundle:
The container is the central element of the system, in fact, all the functionality of the application is concentrated in it; accordingly, to add any functionality, you need to put the necessary services and parameters into the container. For the convenience of such modifications, there are container extensions (which are processed with a special “merge” pass mentioned above).
Extensions is an implementation of the ExtensionInterface interface , and the abstract Extension class reduces routine actions and adds convenient methods. Extensions usually work as follows:
Example extension configuration ( app / config / config.yml ):
This configuration says that you need to pass the extension ( ExtensionInterface :: load () ) named twig ( ExtensionInterface :: getAlias () ) the parameters defined in the corresponding section.
Connecting the bundle extension is very simple, you just need to create a class with the corresponding name ( DependencyInjection \ BundleNameExtension ) and the extension :: build () method code will load the extension itself (the main thing is not to forget to call the parent method and the heir;)).
And the last step is launching the bundles. In fact, most bundles do nothing at this stage, it is designed to perform actions that cannot be performed by the container. For example, at this stage, the main Symfony2 bundle (FrameworkBundle) loads the class cache that has been mentioned more than once in this article.
On this, the initialization process of the Symfony2 application can be considered completed: all the preparatory steps are completed, and the container contains all the necessary functionality. It remains to get the necessary service from the container and call its methods to execute the application.
The reader will probably be concerned about the rather complicated and resource-intensive initialization process, but it's not so bad. Symfony2 along with flexibility does not forget about speed, so out of the box provides many optimization solutions. So, for example, the entire process of creating a container is cached by creating a class containing all the settings, and the next time you call it (if it is debugging mode) instead of loading many configs of various parameters, the container compiler, etc. the generated class will be just loaded and a ready-made container object will be instantly created.
This concludes the first dive into the wilds of Symfony2. If this type of articles about Symfony2 will interest readers, it is possible to turn all this into a series of articles with analysis of the framework internals, individual components and popular bundles. In addition, this is my first article, despite a long stay on Habré. I like talking more than writing, but I'm trying to educate in myself this way of expressing my thoughts. Therefore, I will be very grateful for the feedback directly on the presentation of the material: how difficult it is to perceive; what is better to emphasize, what less; perhaps more illustrations and code examples are needed; rely more on those who are familiar with the subject area, or try to describe so that it is clear to everyone, etc.
![](https://habrastorage.org/storage/defbdc12/fd177c73/efb06b7b/e5df7d72.gif)
The same thing happens when working with frameworks that do everything for us, but I want to know how they do it, and if necessary, be able to change behavior. Unfortunately, the documentation, no matter how good it is ( and Symfony 2 is already good ), tells how to use all this "magic", but does not reveal the whole essence.
This article is an attempt to understand how the application is initialized and what is the "Symfony2 Kernel".
For those who do not like large texts, a brief outline of the main components and a short description of the actions that take place are immediately attached.
Brief Process Description
![](https://habrastorage.org/storage/c7e32824/ecf34456/96ebf76b/dcbfba05.png)
Clickable
1. All requests are accepted by the Front Controller (FrontController).
2. Configure autoload classes (autoload).
3. A kernel is created depending on the environment.
4. The kernel starts.
1. The list of Bundles is initialized.
2. A Dependency Injection Container is created.
1. A container is created with the basic parameters.
2. Each bundle modifies a build container.
3. The application configuration is loading.
4. The container is compiled.
1. Extensions are being processed.
2. References to parameters are replaced by real values.
3. The container is put into read-only mode (frozen).
3. Bundles are launched.
Frontcontroller
As the front controller are ordinary scripts. Examples are scripts from the standard Symfony2 distribution .
- app / console - access to the application through the CLI.
- web / app.php - access to the application through the web.
- web / app_dev.php - access to the application through the web in development mode.
All these front controllers are built on the same principle:
- autoload is configured by connecting a script with registration of the class loader;
- sometimes the class cache is used (frequently used classes collected in 1 file);
- The kernel starts;
Kernel
The kernel is a class that implements the KernelInterface interface ; its task is to initialize the environment. The core is based on two main components: the Dependency Injection container (the theory can be gleaned, for example, from Fowler ) and the bundle system. The bundle is an analogue of the plugin from Symfony 1.x. You can read more about bundles in the official documentation . Of course, in addition to the kernel interface itself, there is also a standard abstract Kernel implementation , which will be mainly discussed later.
The initialization code looks something like this:
// init bundles
$this->initializeBundles();
// init container
$this->initializeContainer();
foreach ($this->getBundles() as $bundle) {
$bundle->setContainer($this->container);
$bundle->boot();
}
- (4.1) initializeBundles - collects information about bundles (the ones that the developer registers for his application through KernelInterface :: registerBundles , see the example from the standard delivery );
- (4.2) initializeContainer - creates a DI container and fills it with data;
- (4.3) each of the bundles is launched.
Container initialization
The result of this stage is a fully ready-to-use container, switched to read-only mode. This process is divided into 4 sub-steps:
- A container ( ContainerBuilder ) is created and standard kernel parameters are placed there: paths to the main directories, environment parameters (environment, debug) and others.
- For each bundle is run the build , at this stage, given the opportunity to bundle container modification
- The configuration defined by the developer is added (via the kernel registerContainerConfiguration ). In the standard package, this method looks very simple:
public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); }
- Finally, the container compile ( compile ): actual values of the parameters are substituted; performed "compiler passes» * ( CompilerPass), they perform modifications, additions Bundle (more about them later); the container is “frozen”, i.e. goes into read-only mode, after which any attempt to change it will throw an exception .
Modification of the container bundles
As noted above, during the compilation process, each bundle has the opportunity to make modifications to a common container. The main way to modify is to add compiler passes
Compiler pass
At the compilation stage, the container performs a series of passes to bring its contents to its final state. Passes are an implementation of the CompilerPassInterface interface and come in 6 types (in order of execution): merge, beforeOptimization, optimization, beforeRemoving, removing, afterRemoving - by default , the compiler configuration already contains a set of passwords.
- before * / after * are just “hooks” for custom “passes” and are not used by standard ones (added by default);
- merge - handles container extensions
- optimization - optimize the container, examples include: ResolveInterfaceInjectorsPass (converts interface injections ), CheckCircularReferencesPass (checks for circular references in the container)
- removing - remove unused services, for example, RemoveUnusedDefinitionsPass (removes unused private services)
In bundles, CompilerPass is most often used to process tags in a container . Example from a standard TwigBundle:
$definition = $container->getDefinition('twig');
$calls = $definition->getMethodCalls();
$definition->setMethodCalls(array());
foreach ($container->findTaggedServiceIds('twig.extension') as $id => $attributes) {
$definition->addMethodCall('addExtension', array(new Reference($id)));
}
$definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls));
Container extensions
The container is the central element of the system, in fact, all the functionality of the application is concentrated in it; accordingly, to add any functionality, you need to put the necessary services and parameters into the container. For the convenience of such modifications, there are container extensions (which are processed with a special “merge” pass mentioned above).
Extensions is an implementation of the ExtensionInterface interface , and the abstract Extension class reduces routine actions and adds convenient methods. Extensions usually work as follows:
- definitions of services and default parameters are loaded from the extension config file (most often an xml file);
- processing the extension config defined for the application, overwriting the default values;
- classes for assembly into one file are registered (the same class cache in one file mentioned in the front controller)
Example extension configuration ( app / config / config.yml ):
# Twig Configuration
twig:
debug: %kernel.debug%
strict_variables: %kernel.debug%
This configuration says that you need to pass the extension ( ExtensionInterface :: load () ) named twig ( ExtensionInterface :: getAlias () ) the parameters defined in the corresponding section.
Connecting the bundle extension is very simple, you just need to create a class with the corresponding name ( DependencyInjection \ BundleNameExtension ) and the extension :: build () method code will load the extension itself (the main thing is not to forget to call the parent method and the heir;)).
Launch bundle
And the last step is launching the bundles. In fact, most bundles do nothing at this stage, it is designed to perform actions that cannot be performed by the container. For example, at this stage, the main Symfony2 bundle (FrameworkBundle) loads the class cache that has been mentioned more than once in this article.
Conclusion
On this, the initialization process of the Symfony2 application can be considered completed: all the preparatory steps are completed, and the container contains all the necessary functionality. It remains to get the necessary service from the container and call its methods to execute the application.
The reader will probably be concerned about the rather complicated and resource-intensive initialization process, but it's not so bad. Symfony2 along with flexibility does not forget about speed, so out of the box provides many optimization solutions. So, for example, the entire process of creating a container is cached by creating a class containing all the settings, and the next time you call it (if it is debugging mode) instead of loading many configs of various parameters, the container compiler, etc. the generated class will be just loaded and a ready-made container object will be instantly created.
From the author
This concludes the first dive into the wilds of Symfony2. If this type of articles about Symfony2 will interest readers, it is possible to turn all this into a series of articles with analysis of the framework internals, individual components and popular bundles. In addition, this is my first article, despite a long stay on Habré. I like talking more than writing, but I'm trying to educate in myself this way of expressing my thoughts. Therefore, I will be very grateful for the feedback directly on the presentation of the material: how difficult it is to perceive; what is better to emphasize, what less; perhaps more illustrations and code examples are needed; rely more on those who are familiar with the subject area, or try to describe so that it is clear to everyone, etc.