📜 ⬆️ ⬇️

Briefly about the architecture of the component Symfony Config



The Symfony 2 Config component is designed to work with configuration files and provides the following features:


The official documentation for this component contains detailed information on its use. And we let's look at how this component is arranged inside.

Determining the 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 will only note that the ArrayNode node is used to build the configuration tree. If you want an ArrayNode accommodate not just pre-defined nodes, but several other ArrayNode , but with a clearly identical predefined internal structure, you can use PrototypedArrayNode .
')
To build a configuration description, use the Symfony\Component\Config\Definition\Builder\TreeBuilder about this way:

 <?php use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->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 entirely in one place. You can do this in parts, and then merge the parts using the append method of the NodeBuilder .

Normalization


Normalization is the reduction of the names of the keys of the nodes and their values, if necessary, to a canonical form. In fact, now normalization is used only to bring the nodes described in xml in the form

 <children> <child> </child> </children> 

to mind
  "children" => Array( [0] => " " ) 

To normalize the nodes, call the normalize() method from Symfony\Component\Config\Definition\NodeInterface . And besides, Symfony\Component\Config\Definition\BaseNode has another method preNormalize . The latter is used to foo_bar keys like foo_bar and foo-bar .

Finalize


The process of finalizing a node performs actions to prepare the node for reading inside the configuration and check for compliance with the type declared and its rules. The finalization is done using the finalizeValue method of the descendants of BaseNode

Data validation is performed using the predefined NodeDefinition methods and its descendants like isRequired , as well as using the extended validation delegated to the Symfony\Component\Config\Definition\Builder\ValidationBuilder class.

The rules for merging data from several parts are contained in the class Symfony\Component\Config\Definition\Builder\MergeBuilder . Delegation of checks to it is performed by the merge () method of the NodeDefinition class. For example, you can prohibit overriding the value of a selected configuration key by other configuration files after it was read for the first time.

The process of validation / normalization / finalization of the configuration itself looks like this:

 $configs = array($config1, $config2); //     $processor = new Processor(); //   $configuration = new Configuration(); //  Configuration c   (. ). $processedConfiguration = $processor->processConfiguration( $configuration, $configs ); 

Bilder


As it is easy to see, for the process of building the configuration description itself, TreeBuilder uses an instance of the class Symfony\Component\Config\Definition\Builder\NodeBuilder . Therefore, you can easily define your own node types for configuration. To do this, you need to create your own NodeInterface implementation and your descendant \Symfony\Component\Config\Definition\Builder\NodeDefinition . After that, just call the setNodeClass method.

In detail, the process of determining the configuration structure is described here .

Dumper


Once the configuration structure is built, it can be dumped using various dumpers from the Symfony\Component\Config\Definition\Dumper namespace. Now there are two options: YamlReferenceDumper and XmlReferenceDumper . These dumpers are used, for example, when you call from the console ./bin/symfony config:dump-reference (see Symfony\Bundle\FrameworkBundle\Command\ConfigDumpReferenceCommand )

Loading configuration


Resources and Loaders



It must be said that the Config component itself does not contain specific implementations of the loaders. It only provides the necessary interfaces for their implementation. Moreover, the method of loading and the target container for the loaded data is also not regulated. If you look at the implementation of 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 a loaded configuration using the Symfony\Component\Config\ConfigCache :

 <?php use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\Resource\FileResource; $cachePath = __DIR__.'/cache/appSomeCacheFile.php'; //   ,      ,     $cacheFile = new ConfigCache($cachePath, true); if (!$cacheFile->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 of ConfigCache for further work. ConfigCacheFactory takes in a callable constructor that will rebuild the cache.

Component usage example


The Symfony Config component can be used without framework. As an example, I will give a small piece of code written by the respected magickatt :

 <?php //      try { $basepath = __DIR__ . '/config'; $configuration = Yaml::parse($basepath . '/config.yml'); } catch (\InvalidArgumentException $exception) { exit(",   "); } //  ConfigurationInterface    *.yml  $yamlConfiguration = new \Configuration(); //    (     *.yml) $processor = new Processor(); $configuration = $processor->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; } } 

Source: https://habr.com/ru/post/271417/


All Articles