Briefly about the architecture of the Symfony Config component
- Tutorial
The Symfony 2 Config component is designed to work with configuration files and provides the following features:
- Support tree configuration
- Abstraction of the components of the configuration from which it is loaded (resources, resource loaders, etc.)
- Support for an arbitrary number of configuration components and some assembly and merging rules
- Caching the read configuration and automatically rebuilding it when changing one of the source files
- Validation of the configuration according to various rules and detailed information about parsing errors
The official documentation for this component contains detailed information on its use. And let's look at how this component is arranged inside.
Defining a configuration structure
Types of configuration keys
Here is the class diagram that describes the configuration structure.
The purpose of almost all classes is clear from their name. I only note that a node is used to build the configuration tree
ArrayNode
. If you want to place inside ArrayNode
not just predefined nodes, but several others ArrayNode
, but with a clearly identical predetermined internal structure, you can use it PrototypedArrayNode
. To build a configuration description, a class is used in
Symfony\Component\Config\Definition\Builder\TreeBuilder
approximately this way:root('acme_demo');
$rootNode
->children()
->arrayNode('entities')
->addDefaultsIfNotSet()
->prototype('scalar')->end()
->defaultValue(
array(
'Acme\BaseBundle\Entity\DefaultEntity1',
'Acme\BaseBundle\Entity\DefaultEntity2',
)
)
->end();
return $rootNode;
}
}
The configuration structure does not have to be declared all in one place. You can do this in parts, and then combine the parts using the
append
y method NodeBuilder
.Normalization
Normalization is the reduction of node key names and their values, if necessary, to a canonical form. In fact, now normalization is used only to bring the nodes described in xml as
Значение потомка
to mind
"children" => Array(
[0] => "Значение потомка"
)
To normalize the nodes, the method
normalize()
from is Symfony\Component\Config\Definition\NodeInterface
called. And besides, Symfony\Component\Config\Definition\BaseNode
there is another method preNormalize
. The latter is used to generalize keys of type foo_bar
and foo-bar
.Finalization
The node finalization process takes steps to prepare the node for reading inside the configuration and check for compliance with the declared type and its rules. Finalization is performed using the
finalizeValue
descendant method.
Data validation is performed using both predefined methods and its descendants, sort of , as well as using extended validation delegated to the class .
Rules for combining data from several parts are contained in the class . The delegation of checks to it is performed by the merge () method of the class . For example, you can prevent the value of the selected configuration key from being redefined by other configuration files after it has been read for the first time.
The validation / normalization / finalization process itself looks like this:BaseNode
NodeDefinition
isRequired
Symfony\Component\Config\Definition\Builder\ValidationBuilder
Symfony\Component\Config\Definition\Builder\MergeBuilder
NodeDefinition
$configs = array($config1, $config2); //Загруженные любым способом части конфигурации
$processor = new Processor(); // Процессор конфигурации
$configuration = new Configuration(); // Класс Configuration c правилами проверки (см. выше).
$processedConfiguration = $processor->processConfiguration(
$configuration,
$configs
);
Bilder
As you can see, for the process of constructing a configuration description, it
TreeBuilder
uses an instance of the class Symfony\Component\Config\Definition\Builder\NodeBuilder
. Therefore, you can well define your types of nodes for configuration. To do this, you need to create your own implementation option NodeInterface
and your descendant \Symfony\Component\Config\Definition\Builder\NodeDefinition
. Then just call the setNodeClass
y method NodeBuilder
. The process of determining the configuration structure is described in detail here .
Dumper
After the configuration structure is built, it can be dumped using various dampers from the namespace
Symfony\Component\Config\Definition\Dumper
. Now there are two options: YamlReferenceDumper
and XmlReferenceDumper
. These dampers are used, for example, when you call from the console ./bin/symfony config:dump-reference
(see Symfony\Bundle\FrameworkBundle\Command\ConfigDumpReferenceCommand
)Download configuration
Resources and downloaders
- Parts of the configuration in Symfony are described by resources (
Symfony\Component\Config\Resource\ResourceInterface
). The concept of a resource is quite abstract. It can be either a file or any other data source. For example, a database table or a field in it. - Resources are monitored for changes by resource inspectors (
Symfony\Component\Config\ResourceCheckerInterface
). - Loading configurations from resources is done by loaders (
Symfony\Component\Config\Loader\LoaderInterface
). - Resolvers (
Symfony\Component\Config\Loader\LoaderResolverInterface
) perform the search for a suitable bootloader for the resource . Symfony\Component\Config\Loader\DelegatingLoader
allows you to load a resource by automatically finding the necessary loader, sorting through an array of transferred resolvers.- You can place configuration files in various folders. You can search for files in them using
Symfony\Component\Config\FileLocator
I must say that the Config component itself does not contain specific bootloader implementations. It only provides the necessary interfaces for their implementation. Moreover, the loading method and the target container for the downloaded data are also not regulated. If you look at the implementation
Symfony\Component\DependencyInjection\Loader\YamlFileLoader
, you can see that the configuration is loaded directly into the container.Configuration caching
Symfony Config allows you to cache the loaded configuration using the class
Symfony\Component\Config\ConfigCache
:isFresh()) {
$configFiles = []; // Здесь имена файлов, из которых состоит конфигурация
$resources = array();
foreach ($configFiles as $cfgFile) {
// Здесь загружаем конфигурацию
// .....
// И добавляем ресурс в массив
$resources[] = new FileResource($cfgFile);
}
$code = '...'; //Здесь строим кэш из загруженных данных
//Пишем кеш. Рядом с файлом кеша запишется файл с метаданными со списком исходных ресурсов
$cacheFile->write($code, $resources);
}
// Подключаем файл кеша
require $cachePath;
You can encapsulate the cache rebuilding algorithm, for example, in a class, and then use
Symfony\Component\Config\ConfigCacheFactory
instead ConfigCache
for further work. ConfigCacheFactory
takes in a constructor callable
that will rebuild the cache.Component usage example
The Symfony Config component can also be used without a framework. As an example, here is a small piece of code written by a reputable magickatt :
processConfiguration(
$yamlConfiguration,
array($configuration) // Здесь может быть любое количество *.yml файлов
);
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
class Configuration
{
/**
* @return TreeBuilder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('arbitary');
$rootNode->children()
->scalarNode('host')
->isRequired()
->cannotBeEmpty()
->end()
->scalarNode('username')
->isRequired()
->cannotBeEmpty()
->end()
->scalarNode('password')
->isRequired()
->cannotBeEmpty()
->end()
->booleanNode('bindRequiresDn')
->defaultTrue()
->end();
return $treeBuilder;
}
}