Descripción general del componente Symfony: Config

En este artículo, trataremos de lidiar con un componente llamado Config , que ayuda a cargar y procesar varios datos independientemente de la fuente.


La siguiente es una traducción del artículo Descripción general de los componentes de Symfony2: Config . El original se publicó en 2014 e indica la segunda versión de Symfony, pero la información es relevante para la última, por el momento, la cuarta versión.


Imaginemos que queremos crear un generador de blog que tome varios parámetros, como un título ( título ), descripción ( descripción ), el número de publicaciones en la página principal ( posts_main_page ), íconos de redes sociales ( social ) y la presencia o ausencia de fuentes RSS ( rss ) . Para estos fines, describiremos el archivo de configuración en el formato YAML :


 blog: title:     description:    ... rss: true posts_main_page: 2 social: twitter: url: http://twitter.com/raulfraile icon: twitter.png sensiolabs_connect: url: https://connect.sensiolabs.com/profile/raulfraile icon: sensiolabs_connect.png 

Ahora intentaremos analizar nuestro archivo, verificar la disponibilidad de los campos obligatorios y establecer valores predeterminados si es necesario. Verificaremos que todos los datos obtenidos cumplan con las reglas establecidas, por ejemplo, rss solo puede contener un valor booleano, y posts_main_page debe contener un valor entero en el rango de 1 a 10. Tendremos que repetir estos procedimientos cada vez que se acceda al archivo, a menos que, por supuesto, se use el sistema de almacenamiento en caché . Además, dicho mecanismo complica el uso de archivos de otros formatos como INI , XML o JSON .


Para simplificar las acciones anteriores, utilizaremos el componente Config . El componente es simple, bien probado y lo suficientemente flexible como para usarlo en diferentes proyectos.


Arquitectura


Dividiremos el proyecto en dos partes principales:


  1. Definición de la estructura jerárquica de parámetros.
    El componente le permite determinar el formato de la fuente de configuración, que puede ser desde un simple archivo INI hasta algo más exótico. La clase TreeBuilder ayudará a determinar los tipos de parámetros, los hará obligatorios / opcionales y establecerá el valor predeterminado.
  2. Detección, carga y procesamiento.
    Después de especificar el formato de origen, debe encontrarse, cargarse y procesarse. Finalmente, el componente devolverá una matriz simple con valores marcados o lanzará una excepción en caso de error.

Ejemplo


Volvamos a nuestro ejemplo. Y así, queremos crear un sistema flexible de generación de blogs. Primero, definimos una estructura jerárquica (árbol), y una instancia de la clase TreeBuilder nos ayudará con esto, que proporciona una interfaz similar a la sintaxis DSL .


 <?php namespace RaulFraile\Config; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Builder\TreeBuilder; class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('blog'); $rootNode ->children() ->scalarNode('title') ->isRequired() ->end() ->scalarNode('description') ->defaultValue('') ->end() ->booleanNode('rss') ->defaultValue(false) ->end() ->integerNode('posts_main_page') ->min(1) ->max(10) ->defaultValue(5) ->end() ->arrayNode('social') ->arrayPrototype() ->children() ->scalarNode('url')->end() ->scalarNode('icon')->end() ->end() ->end() ->end() ->end() ; return $treeBuilder; } } 

No se preocupe si ve una estructura similar de código PHP por primera vez, la sintaxis DSL siempre parece un poco extraña. En el ejemplo anterior, determinamos el nodo raíz del blog y construimos la estructura del árbol de configuración a partir de él, cuyas ramas son los parámetros que necesitamos y las reglas para sus valores. Por ejemplo, el título se indica como un parámetro obligatorio de un tipo escalar, la descripción como un parámetro opcional, que está vacío de forma predeterminada, en rss esperamos un valor booleano que sea false de forma predeterminada, y posts_main_page debe contener un valor entero en el rango de 1 a 10, siendo 5 el valor predeterminado .


Bueno, definimos la estructura, ahora vamos a cargar y procesar. Por condición, la fuente puede ser cualquiera, para empezar necesitamos convertirla en una matriz regular para verificar y procesar todos los valores usando nuestra estructura de configuración. Para cada formato de origen, necesitamos una clase separada, por lo que si vamos a utilizar los formatos de archivo YAML y XML , debemos crear dos clases. En el siguiente ejemplo, por simplicidad, solo se presenta la clase de formato YAML :


 <?php namespace RaulFraile\Config; use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Yaml\Yaml; class YamlConfigLoader extends FileLoader { public function load($resource, $type = null) { $configValues = Yaml::parse(file_get_contents($resource)); return $configValues; } public function supports($resource, $type = null) { return is_string($resource) && 'yml' === pathinfo( $resource, PATHINFO_EXTENSION ); } } 

Como puede ver, todo es muy simple. La clase LoaderResolver utiliza el método YamlConfigLoader::supports para verificar la fuente de configuración. El método YamlConfigLoader::load convierte un archivo YAML en una matriz de datos utilizando otro componente Symfony YAML .


En conclusión, combinamos la estructura de configuración y el cargador cuando procesamos la fuente para obtener los valores necesarios:


 <?php use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\Definition\Processor; use RaulFraile\Config\YamlConfigLoader; use RaulFraile\Config\Configuration; include_once __DIR__. '/vendor/autoload.php'; //    $directories = array(__DIR__.'/config'); $locator = new FileLocator($directories); //       $loader = new YamlConfigLoader($locator); $configValues = $loader->load($locator->locate('config.yml')); //   $processor = new Processor(); $configuration = new Configuration(); try { $processedConfiguration = $processor->processConfiguration( $configuration, $configValues ); //   var_dump($processedConfiguration); } catch (Exception $e) { echo $e->getMessage() . PHP_EOL; } 

Analicemos este código. Primero, definimos una matriz de directorios donde se pueden ubicar los archivos de configuración y lo FileLocator como un parámetro en el objeto FileLocator que busca el archivo config.yml en el directorio especificado. Luego, cree un objeto YamlConfigLoader que devuelva una matriz con valores y que ya esté procesada por nuestra estructura de configuración.


Como resultado, obtenemos la siguiente matriz:


 array(5) { 'title' => string(7) "My blog" 'description' => string(24) "This is just a test blog" 'rss' => bool(true) 'posts_main_page' => int(2) 'social' => array(2) { 'twitter' => array(2) { 'url' => string(29) "http://twitter.com/raulfraile" 'icon' => string(11) "twitter.png" } 'sensiolabs_connect' => array(2) { 'url' => string(49) "https://connect.sensiolabs.com/profile/raulfraile" 'icon' => string(22) "sensiolabs_connect.png" } } } 

Si intentamos cambiar config.yml eliminando los campos rss y post_main_page , obtendremos los valores predeterminados:


 array(5) { ... 'rss' => bool(false) 'posts_main_page' => int(5) 

Almacenamiento en caché


El procesamiento de archivos de configuración grandes puede ser una tarea lenta. El componente descrito tiene un mecanismo de almacenamiento en caché simple basado en la verificación de la fecha de cambio del archivo de configuración.


Para habilitar el caché, basta con unas pocas líneas de código:


 <?php use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Definition\Processor; use RaulFraile\Config\YamlConfigLoader; use RaulFraile\Config\Configuration; include_once __DIR__. '/vendor/autoload.php'; $cachePath = __DIR__.'/cache/config.php'; $configFile = 'config.yml'; //    /    $cache = new ConfigCache($cachePath, true); if (!$cache->isFresh()) { $directories = array(__DIR__.'/config'); $locator = new FileLocator($directories); $loader = new YamlConfigLoader($locator); $configFilePath = $locator->locate($configFile); $configValues = $loader->load($configFilePath); $resource = new FileResource($configFilePath); $processor = new Processor(); $configuration = new Configuration(); try { $processedConfiguration = $processor->processConfiguration( $configuration, $configValues ); //     $cache->write(serialize($processedConfiguration), array($resource)); } catch (Exception $e) { echo $e->getMessage() . PHP_EOL; } } 

Una instancia de la clase ConfigCache comprueba la existencia de un caché de archivos y, si está disponible, compara la fecha en que se modificó el archivo de configuración. Cuando creamos un caché de archivos también guardamos una lista de objetos usados ​​para una comparación adicional.


Carga múltiple


Para agregar otro formato de configuración, es suficiente definir una clase que será responsable de un formato específico. En el ejemplo a continuación, agregamos soporte para la configuración XML y un controlador correspondiente. La clase LoaderResolver nos ayudará a combinar diferentes formatos en un grupo común, y la clase DelegatingLoader cargará el archivo requerido a pedido.


 <?php namespace RaulFraile\Config; use Symfony\Component\Config\Loader\FileLoader; class XmlConfigLoader extends FileLoader { public function load($resource, $type = null) { //  xml return $configValues; } public function supports($resource, $type = null) { return is_string($resource) && 'xml' === pathinfo( $resource, PATHINFO_EXTENSION ); } } 

 $loaderResolver = new LoaderResolver(array( new YamlConfigLoader($locator), new XmlConfigLoader($locator) )); $delegatingLoader = new DelegatingLoader($loaderResolver); $configValues = $delegatingLoader->load($locator->locate('config.xml')); 

Generación de información de fondo


Entre otras cosas, el componente tiene la funcionalidad de generar información de referencia para su documentación.


 <?php ... use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; $dumper = new YamlReferenceDumper(); echo $dumper->dump($configuration); 

Producirá:


 blog: title: ~ # Required description: '' rss: false posts_main_page: 5 social: url: ~ icon: ~ 

Resumen


Quizás descubra que todo esto es demasiado complicado y confuso, y puede hacerlo con un par de funciones. Quizás, pero ese es el "precio" para una buena estructura de OOP. Por otro lado, este componente ofrece varias ventajas:


  • ~ 80% de cobertura del componente con pruebas y soporte activo.
  • Agregar nuevos formatos de configuración es realmente fácil. Es suficiente definir un controlador que convierta los datos de origen en una matriz regular. Se utilizará una estructura similar para otros formatos. La extensión de cualquier parte del componente se implementa agregando la interfaz necesaria.
  • El almacenamiento en caché funciona de forma inmediata con configuraciones flexibles para entornos de desarrollo y prod .
  • Verificación incorporada de parámetros y sus valores.
  • Generación de información de fondo.

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


All Articles