定义和加工配置的值(3.0)

验证配置的值

从各种资源加载配置值之后,这些值和他们的结构都会通过配置组件的“Definition”部分被验证。配置的这些值会表现出一定的层次结构。此外,这个值可能应该有一个特定的类型,数量上的限制或者是一组给定的值中的一个。例如,下面的配置(在yaml中)显示出一个清晰的结构并且一些验证规则,应该适用于它(像:“auto_connect 值一定是boolean”):

当加载多个配置文件,他应该有能力做到合并和覆盖一些值。有些值不能合并并且我们要第一时间保住他们。此外,一些键当输入一个特定的值之后才有效(例如在上面的实例中:当driver 键是sqlite时memory键才有意义)。

 

使用TreeBuilder定义配置值的层次结构

所有的涉及配置值的规则都能够使用TreeBuilder定义。

从一个自定义并实现了ConfigurationInterfaceConfiguration类中返回一个TreeBuilder实例:

 

在树状中添加节点

节点变量

一个树状以语义的方式定义包含的节点。这意味着要使用缩进和流利的符号,能够反映出配置值的实际结构:

这个root节点本身是一个数组节点,并且有子节点,像boolean节点auto_connect和这个scalar节点default_connection。总之:在定义节点之后,要调用end()再进一步设置层次结构。

 

Node类型

使用适当的节点定义去验证提供值的类型。

节点类型可用于:

  • scalar (generic type that includes booleans, strings, integers, floats and null) 通用类型包含booleans,strings,integers,floats或者null
  • boolean
  • integer
  • float
  • enum (similar to scalar, but it only allows a finite set of values) 类似scalar,但它仅允许有限的一组值
  • array
  • variable (no validation) 没有验证

并用node($name, $type)创建或者用快捷方式xxxxNode($name)方法。

 

数值型节点的约束

数值型节点(float和integer)提供了两个额外的约束 – min()和max() – 允许去验证这些值:

 

Enum节点

Enum节点对一组给定的输入值提供一个约束匹配:

这将限制性别只能从男性或女性中选择。

 

Array节点

可以添加一个更深层次的层次结构,通过array节点。array节点本身可以具有一个预定义的一组变量节点:

或者你还可以为每个节点内部节点数组定义一个prototype:

一个prototype可以用于添加一个定义,这个定义可能在当前结点使用多次。在上面的例子中按照prototype的定义,他可能有多个connection数组(包含driver,host)。

 

Array节点选项

定义一个数组子节点之前,你可以提供像这样的选项:

useAttributeAsKey()

提供一个子节点名称,其值应作为所得数组中的键的名称。这种方法还限定被处理配置数组键,下面的例子说明了这种方式。

requiresAtLeastOneElement()

在这个数组里至少应该有一个元素(只能在isRequired()被调用后)

addDefaultsIfNotSet()

如果任何子节点有默认值,如果没有明确的规定值,可以使用他们。

normalizeKeys(false)

如果调用(false),键就用破折号不是标准的下划线。建议用prototype节点在这里用户将定义一个键-值映射,以避免不必要的转变。

一个基础的数组原型定义如下:

当使用YAML时,配置如下:

或者xml配置:

配置经过处理:

一个更复杂的例子是定义一个有子原型数组:

使用YAML配置如下:

XML配置如下:

经过处理后的配置:

前面的输出去匹配预期的结果。然后,比对使用YAML配置的配置树如下:

这个输出的配置与之前的一模一样。换句话说,这个sf_connection和default配置键丢失。原因是Symfony的配置组件将数组作为默认列表。

在写这篇文章,有一个不一致:如果仅仅一个文件提供所讨论的配置,这个键(sf_connection和default)不会丢失。但如果更多文件提供这个配置,这个键就像上面描述的他会丢失。

为了保住这个数组键就要使用useAttributeAsKey()方法:

这个方法的参数(就是上面例子中的name)把这个属性名称添加到每一个的xml节点中去以便辨别他们。现在你可以在之前的YAML配置中添加或者是下面的XML配置:

在这两种情况下,经过处理的配置保存住了sf_connection和default键:

 

默认和必填值

对于所有的节点类型,他们可以定义默认值并替换这些值,如果这个节点有一些值:

defaultValue()  设置默认值

isRequired() 必须定义的(但可以为空)

cannotBeEmpty() 可能不包含空值

default*() (null,true,false),快捷方式defaultValue()

treat*Like() (null,true,false),提供一个替代值去替换这个例子中的*值。

 

记录配置

所有的选项都可以使用info()方法记录。

使用config:dump-reference命令输出配置树,这个信息将被作为注释。

在YAML有可能是:

在XML

 

可选部分

如果你有哪些是可选的,可以启用/禁用。你能够利用这个canBeEnabled()canBeDisabled()快捷方式。

这个canBeDisabled方法看起来不同之处就是默认是启用的。

 

合并选项

额外的配置可能会提供合并处理。对于数组:

performNoDeepMerging()

当第二个配置中定义的值也是数组,不要试图合并一个数组,而是完全覆盖它

对于所有节点:

cannotBeOverwritten()

不要让其他配置数组去覆盖现有的节点值

 

附加部分

如果你有一个复杂的配置需要验证,这个树(tree)可能长得很大,我估计你可能想要把它分成各个部分。你能够做的是让一个单独的节点作为一个部分并且然后使用append()添加到主树:

如果你有部分重复在不同的地方,这也有助于你避免重复的配置。

这个例子的结果如下:

 

 

标准化

当他们第一次规范化处理配置文件,然后合并,最后树用于验证得到的结果数组。这个规范化的处理,习惯从不同的配置格式中一些差异的结果,主要是YAML和XML之间的差异。

它通常使用的键的分割符在yaml是_,在xml里是-。例如,在yaml里是auto_connect,在xml里是auto-connect。标准化将两个都是auto_connect。

如果他是混合写法foo-bar_moo或者它已经存在,这个目标键将不会改变。

值的数组在YAML和XML中的表现不同。在YAML中可能是这样的:

但是在xml中是:

这种差异在标准化可以被删除通过使用xml中的多元化键。你能够指定你想要的一个键是多元化的,使用fixXmlConfig()方法。

如果他是不规则的多元化,你能指定另一个它作为第二个参数:

也解决了这个问题,fixXmlConfig确保单个XML元素仍然变成了一个数组。你可能有:

并且有时只有:

在第一个例子中默认情况下connection将是一个数组,第二个是一个字符串就很难验证。fixXmlConfig就可以确保始终是一个数组。

如果你需要,你可以进一步控制标准化进程。例如,你可能想允许一个字符串去设置或者被使用,让它作为一个特定的键或者几个键明确被设定。因此,如果在这个配置中除了名字的都是可选的:

你可以允许下面的方式:

通过改变一个字符串值name到关联数组中作为键(key)

 

验证规则

更先进的验证规则可以使用ExprBuilder。这个builder实现了很知名的控制结构”Fluent Interface“风格。这个builder是使用高级的验证规则来定义节点,像:

一个验证规则总是有一个”if“部分。你能用下面方法指定这个部分:

  • ifTrue()
  • ifString()
  • ifNull()
  • ifArray()
  • ifInArray()
  • ifNotInArray()
  • always()

验证规则还需要一个“then”部分:

  • then()
  • thenEmptyArray()
  • thenInvalid()
  • thenUnset()

通常,”then“是一个闭包。他的返回值将作为节点一个新的值,替换节点原始的值。

 

处理配置值

The Processor使用tree,因为他使用内置的TreeBuilder构建去处理多个应该被合并的配置值数组。如果任何值都没有预想的类型,是强制性的并且未定义,或者一些其他方式不能验证,都会抛出一个异常。否则,这个结果将是一个干净的配置值的数组:

 

发表评论