Symfony组件概述:配置

在本文中,我们将尝试处理一个名为Config的组件,该组件有助于加载和处理各种数据,无论其来源如何。


以下是文章Symfony2组件概述的翻译:Config 原始版本于2014年发布,其中指出了Symfony的第二版,但此信息与当前最新的第四版有关。


想象一下,我们想创建一个博客生成器,该生成器将采用几个参数,例如标题( title ),描述( description ),主页上的帖子数( posts_main_page ),社交媒体图标( social )以及是否存在RSS feed( rss )。 。 为此,我们将以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 

现在,我们将尝试解析我们的文件,检查必填字段的可用性,并在必要时设置默认值。 我们将检查所有获得的数据是否符合既定规则,例如, rss只能包含一个布尔值,并且posts_main_page应当包含一个介于1到10之间的整数值。每次访问该文件时,我们都必须重复这些过程,除非使用了缓存系统。 另外,这种机制使其他格式的文件(例如INIXMLJSON的使用变得复杂。


为了简化上述操作,我们将使用Config组件 。 该组件简单,经过良好测试且足够灵活,可用于不同的项目。


建筑学


我们将项目分为两个主要部分:


  1. 定义参数的层次结构。
    该组件使您可以确定配置源的格式,可以是从简单的INI文件到更奇特的任何内容。 TreeBuilder类将帮助确定参数的类型,使它们成为必需/可选并设置默认值。
  2. 检测,加载和处理。
    指定源格式后,必须找到,加载和处理它。 最后,该组件将返回一个带有检查值的简单数组,或者在错误时引发异常。

例子


让我们回到我们的例子。 因此,我们想创建一个灵活的博客生成系统。 首先,我们定义一个层次结构(树), TreeBuilder类的实例将为我们提供帮助,它提供了类似于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; } } 

不用担心,如果您是第一次看到类似的PHP代码结构,则其中的DSL语法总是有些奇怪。 在上面的示例中,我们确定了博客的根节点,并从中构建了配置树的结构,其分支是我们需要的参数及其值的规则。 例如, title表示为标量类型的必需参数, description表示为可选参数,默认情况下为空,在rss中,我们期望布尔值默认为false ,并且posts_main_page的整数值应介于1到10之间,默认值为5 。


好了,我们定义了结构,现在让我们开始进行加载和处理。 根据条件,源可以是任何源,首先,我们需要将其转换为常规数组,以便使用我们的配置结构进一步检查和处理所有值。 对于每种源格式,我们需要一个单独的类,因此,如果要使用YAMLXML文件格式,则需要创建两个类。 在下面的示例中,为简单起见,仅显示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 ); } } 

如您所见,一切都很简单。 LoaderResolver类使用YamlConfigLoader::supports方法来验证配置源。 YamlConfigLoader::load方法使用另一个Symfony YAML组件YAML文件转换为数据数组。


总之,在处理源以获得必要的值时,我们将配置结构和加载器结合在一起:


 <?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; } 

让我们分析一下这段代码。 首先,我们定义可以在其中放置配置文件的目录数组,并将其作为参数放置在FileLocator对象中,该对象在指定目录中查找config.yml文件。 然后,创建一个YamlConfigLoader对象,该对象返回带有值的数组,并且该数组已经由我们的配置结构处理。


结果,我们得到以下数组:


 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" } } } 

如果我们尝试config.yml删除rsspost_main_page字段来更改config.yml ,则将获得默认值:


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

快取


处理大型配置文件可能是一项耗时的任务。 所描述的组件具有一个简单的缓存机制,该机制基于检查配置文件的更改日期。


要启用缓存,只需几行代码即可:


 <?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; } } 

ConfigCache类的实例检查文件缓存是否存在,如果可用,则比较修改配置文件的日期。 创建文件缓存时,我们还会保存一个已用对象列表,以供进一步比较。


多次加载


要添加另一种配置格式,只需定义一个负责特定格式的类即可。 在下面的示例中,我们添加了对XML配置的支持和相应的处理程序。 LoaderResolver类将帮助我们将不同的格式组合到一个公共池中,而DelegatingLoader类将根据请求加载所需的文件。


 <?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')); 

背景信息生成


除其他外,该组件具有为您的文档生成参考信息的功能。


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

它将输出:


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

总结


也许您会发现所有这些都太复杂和令人困惑,并且可以使用几个函数来完成。 也许,但这就是一个好的OOP结构的“价格”。 另一方面,此组件具有多个优点:


  • 通过测试和积极支持,组件的覆盖率达到80%。
  • 添加新的配置格式非常容易。 定义一个将源数据转换为常规数组的处理程序就足够了。 其他格式也将使用类似的结构。 组件任何部分的扩展都通过添加必要的接口来实现。
  • 缓存具有针对dev环境和prod环境的灵活设置,开箱即用。
  • 内置验证参数及其值。
  • 背景信息生成。

Source: https://habr.com/ru/post/zh-CN413413/


All Articles