OptionsResolver组件

编辑该页面

OptionsResolver组件

OptionsResolver组件是一种改进的替代array_replacePHP函数。它允许您创建一个选项系统所需的选项,默认值,验证(类型、价值)、标准化和更多。

安装

1
美元作曲家需要symfony / opob娱乐下载tions-resolver

请注意

如果你安装这个组件之外的Symfony应用程序,你必须要求ob娱乐下载供应商/ autoload.php文件在你的代码,使作曲家提供的类加载机制。读这篇文章为更多的细节。

使用

想象你有一个梅勒类有四个选项:主机,用户名,密码港口:

1 2 3 4 5 6 7 8 9
梅勒{受保护的美元选项;公共函数__construct(数组美元选项= []){美元- >选择=美元选项;}}

当访问选择美元,你需要添加一些样板代码检查哪些选项设置:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
梅勒{/ /……公共函数sendMail(美元,美元){美元邮件=……;美元邮件- >setHost (美元- >选项(“主机”)? ?“smtp.example.org”);美元邮件- >setUsername (美元- >选项(“用户名”)? ?“用户”);美元邮件- >向setPassword (美元- >选项(“密码”)? ?“爸爸$ $词”);美元邮件- >setPort (美元- >选项(“端口”)? ?25);/ /……}}

选项的默认值也埋在代码的业务逻辑。使用array_replace修复:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
梅勒{/ /……公共函数__construct(数组美元选项= []){美元- >选择= array_replace ([“主机”= >“smtp.example.org”,“用户名”= >“用户”,“密码”= >“爸爸$ $词”,“端口”= >25),美元选项);}}

现在所有四个选项是有保证的,但是你仍然可以使一个错误当使用如下梅勒类:

1 2 3
美元梅勒=梅勒([“usernme”= >“johndoe”,/ /用户名是错误的拼写是“usernme”]);

没有显示错误信息。在最好的情况下,错误在测试过程中会出现,但开发者会花时间找问题。在最坏的情况下,不可能出现错误直到部署到活动的系统。

幸运的是,OptionsResolver类可以帮助您解决这个问题:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
使用ob娱乐下载\组件\OptionsResolver\OptionsResolver;梅勒{/ /……公共函数__construct(数组美元选项= []){美元解析器=OptionsResolver ();美元解析器- >setDefaults ([“主机”= >“smtp.example.org”,“用户名”= >“用户”,“密码”= >“爸爸$ $词”,“端口”= >25]);美元- >选择=美元解析器- >解决(美元选项);}}

像以前一样,所有选项都将保证集。此外,一个UndefinedOptionsException如果抛出一个未知的选择是通过:

1 2 3 4 5 6
美元梅勒=梅勒([“usernme”= >“johndoe”]);/ / UndefinedOptionsException:选择“usernme”并不存在。/ /定义的选项:“主机”、“密码”、“港”、“用户名”

剩下的代码可以访问选项的值没有样板代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/ /……梅勒{/ /……公共函数sendMail(美元,美元){美元邮件=……;美元邮件- >setHost (美元- >选项(“主机”]);美元邮件- >setUsername (美元- >选项(“用户名”]);美元邮件- >向setPassword (美元- >选项(“密码”]);美元邮件- >setPort (美元- >选项(“端口”]);/ /……}}

这是一个很好的实践将选择配置到一个单独的方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21日22日23日24
/ /……梅勒{/ /……公共函数__construct(数组美元选项= []){美元解析器=OptionsResolver ();美元- >configureOptions (美元解析器);美元- >选择=美元解析器- >解决(美元选项);}公共函数configureOptions(OptionsResolver美元解析器){美元解析器- >setDefaults ([“主机”= >“smtp.example.org”,“用户名”= >“用户”,“密码”= >“爸爸$ $词”,“端口”= >25,“加密”= >]);}}

首先,代码变得易于阅读,特别是如果构造函数不仅处理选项。第二,子类会覆盖configureOptions ()调整方法的配置选项:

1 2 3 4 5 6 7 8 9 10 11 12 13
/ /……GoogleMailer扩展梅勒{公共函数configureOptions(OptionsResolver美元解析器){::configureOptions (美元解析器);美元解析器- >setDefaults ([“主机”= >“smtp.google.com”,“加密”= >“ssl”]);}}

需要选择

如果调用者必须设置一个选项,该选项传递给setRequired ()。例如,使主机选择需要,你能做什么:

1 2 3 4 5 6 7 8 9 10 11
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setRequired (“主机”);}}

如果您省略一个必需的选项,一个MissingOptionsException将被扔:

1 2 3
美元梅勒=梅勒();/ / MissingOptionsException:所需的选项“主机”不见了。

setRequired ()方法接受一个名称或一组选项名称,如果你有不止一个需要选择:

1 2 3 4 5 6 7 8 9 10 11
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setRequired ([“主机”,“用户名”,“密码”]);}}

使用isRequired ()发现如果一个选项是必需的。您可以使用getRequiredOptions ()来检索所需的所有选项的名称:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
/ /……GoogleMailer扩展梅勒{公共函数configureOptions(OptionsResolver美元解析器){::configureOptions (美元解析器);如果(美元解析器- >isRequired (“主机”)){/ /……}美元requiredOptions=美元解析器- >getRequiredOptions ();}}

如果你想要检查是否需要选择是失踪从默认选项,您可以使用isMissing ()。这之间的区别isRequired ()是,这个方法将返回false如果需要选择已经设置:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setRequired (“主机”);}}/ /……GoogleMailer扩展梅勒{公共函数configureOptions(OptionsResolver美元解析器){::configureOptions (美元解析器);美元解析器- >isRequired (“主机”);/ / = >正确的美元解析器- >isMissing (“主机”);/ / = >正确的美元解析器- >setDefault (“主机”,“smtp.google.com”);美元解析器- >isRequired (“主机”);/ / = >正确的美元解析器- >isMissing (“主机”);/ / = >假}}

getMissingOptions ()方法允许您访问所有失踪的名称选项。

类型验证

选项您可以运行额外的检查,以确保他们正确传递。验证的类型选择,电话setAllowedTypes ():

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……/ /允许指定一个类型美元解析器- >setAllowedTypes (“主机”,“字符串”);/ /允许指定多个类型美元解析器- >setAllowedTypes (“端口”,(“零”,“int”]);/ /检查所有物品在递归数组类型美元解析器- >setAllowedTypes (“日期”,“DateTime []”);美元解析器- >setAllowedTypes (“端口”,“int []”);}}

你可以传递任何类型的一个is_ <类型> ()PHP函数中定义。你也可以通过完全限定的类或接口的名字(这是检查使用运算符)。此外,您可以递归地验证数组中所有项目通过向与类型[]

如果你现在通过一个无效的选项,一个InvalidOptionsException抛出:

1 2 3 4 5 6
美元梅勒=梅勒([“主机”= >25]);/ / InvalidOptionsException:选择“主机”与“25”是价值/ /将类型的“字符串”,但“int”类型的

在子类中,您可以使用addAllowedTypes ()添加额外的允许没有删除已经设置的类型。

值验证

一些选项只能取一个固定的预定义的值列表。例如,假设梅勒类都有一个运输选项之一sendmail,邮件smtp。使用这种方法setAllowedValues ()验证通过选择包含这些值之一:

1 2 3 4 5 6 7 8 9 10 11 12
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setDefault (“交通”,“发送邮件”);美元解析器- >setAllowedValues (“交通”,(“发送邮件”,“邮件”,“smtp”]);}}

如果你通过一个无效的交通,一个InvalidOptionsException抛出:

1 2 3 4 5 6
美元梅勒=梅勒([“交通”= >“发送邮件”]);/ / InvalidOptionsException:选择“运输”价值“发送邮件”/ /无效。接受值:“发送邮件”,“邮件”,“smtp”

对更复杂的验证方案,选择通过关闭它返回真正的可接受的值和无效值:

1 2 3 4
/ /……美元解析器- >setAllowedValues (“交通”,函数(美元价值){/ /返回true或false});

提示

你甚至可以使用验证器通过使用组件来验证输入createIsValidCallable ()方法:

1 2 3 4 5 6 7 8
使用ob娱乐下载\组件\OptionsResolver\OptionsResolver;使用ob娱乐下载\组件\验证器\约束\长度;使用ob娱乐下载\组件\验证器\验证;/ /……美元解析器- >setAllowedValues (“交通”、验证::createIsValidCallable (长度([“最小值”= >10))));

在子类中,您可以使用addAllowedValues ()添加额外的允许没有删除已经设置的值。

选择归一化

有时,选项值需要归一化之前,您可以使用它们。例如,假设主机应该开始http://。要做到这一点,您可以编写标准化者。标准化者执行验证后一个选项。您可以配置一个标准化者通过调用setNormalizer ():

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
使用ob娱乐下载\组件\OptionsResolver\选项;/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setNormalizer (“主机”,函数(选项美元选项,美元价值){如果(“http://”! = = substr (美元价值,0,7)){美元价值=“http://”美元价值;}返回美元价值;});}}

标准化者接收到实际美元的价值并返回标准化形式。你也看到,关闭以一个选择美元参数。这是有用的,如果你需要使用其他选项在归一化:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setNormalizer (“主机”,函数(选项美元选项,美元价值){如果(“http://”! = = substr (美元价值,0,7)& &“https://”! = = substr (美元价值,0,8)){如果(“ssl”= = =美元选项(“加密”){美元价值=“https://”美元价值;}其他的{美元价值=“http://”美元价值;}}返回美元价值;});}}

规范化新允许价值在父类子类被规范化,使用addNormalizer ()方法。这种方式,美元的价值参数将收到以前归一化值,否则你可以预先考虑新的标准化者通过真正的作为第三个参数。

依赖于另一个选项的默认值

假设你想设置的默认值港口选择基于加密的用户选择梅勒类。更准确地说,你想设置端口465年如果使用SSL和25否则。

您可以实现此功能通过一个闭包的默认值港口选择。关闭接收选项作为参数。根据这些选项,您可以返回所需的默认值:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
使用ob娱乐下载\组件\OptionsResolver\选项;/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setDefault (“加密”,);美元解析器- >setDefault (“端口”,函数(选项美元选项){如果(“ssl”= = =美元选项(“加密”){返回465年;}返回25;});}}

谨慎

可调用的参数必须类型暗示选项。否则,可调用本身被认为是选项的默认值。

请注意

关闭只是如果执行港口选择不设置由用户或覆盖在一个子类。

之前设置的默认值可以通过添加访问关闭第二个参数:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setDefaults ([“加密”= >,“主机”= >“example.org”]);}}GoogleMailer扩展梅勒{公共函数configureOptions(OptionsResolver美元解析器){::configureOptions (美元解析器);美元解析器- >setDefault (“主机”,函数(选项美元选项,美元previousValue){如果(“ssl”= = =美元选项(“加密”){返回“secure.example.org”;}/ /基类的默认值配置返回美元previousValue;});}}

示例中可以看到,此功能主要是有用的,如果你想重复使用默认值设置在子类的父类。

选项没有默认值

在某些情况下,它是有用的定义一个选项没有设置一个默认值。这是有用的,如果你需要知道是否用户实际上设置一个选项。例如,如果您设置一个选项的默认值,它是不可能知道用户通过这个值或如果它来自默认:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setDefault (“端口”,25);}/ /……公共函数sendMail(美元,美元){/ /这是默认值或类的调用者真的/ /设置端口25 ?如果(25= = =美元- >选项(“端口”){/ /……}}}

您可以使用setDefined ()定义一个选项没有设置一个默认值。然后选择只会包含在选项如果它实际上是传递给解决解决():

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setDefined (“端口”);}/ /……公共函数sendMail(美元,美元){如果(array_key_exists (“端口”,美元- >选项)){回声“集合!”;}其他的{回声“没有!”;}}}美元梅勒=梅勒();美元梅勒- >sendMail (美元,美元);/ / = >没有设置!美元梅勒=梅勒([“端口”= >25]);美元梅勒- >sendMail (美元,美元);/ / = >设置!

你也可以通过选项数组的名字,如果你想定义多个选择的:

1 2 3 4 5 6 7 8 9 10
/ /……梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >setDefined ([“端口”,“加密”]);}}

的方法isDefined ()getDefinedOptions ()让你找出哪些选项定义:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/ /……GoogleMailer扩展梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){::configureOptions (美元解析器);如果(美元解析器- >isDefined (“主机”)){/ /以下被称为之一:/ /解析器- > setDefault(“主机”,…);/ /解析器- > setRequired('主机');/ /解析器- > setDefined('主机');}美元definedOptions=美元解析器- >getDefinedOptions ();}}

嵌套的选择

假设你有一个选择线轴这有两个推荐类型路径。而不是将它定义为一个简单的数组的值,您可以通过一个闭包的默认值线轴选择与OptionsResolver论点。根据这个例子中,您可以定义下的选项线轴及其所需的默认值:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29
梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){美元解析器- >setDefault (“轴”,函数(OptionsResolver美元spoolResolver){美元spoolResolver- >setDefaults ([“类型”= >“文件”,“路径”= >“/ spool /道路/”]);美元spoolResolver- >setAllowedValues (“类型”,(“文件”,“记忆”]);美元spoolResolver- >setAllowedTypes (“路径”,“字符串”);});}公共函数sendMail(美元,美元){如果(“记忆”= = =美元- >选项(“轴”][“类型”){/ /……}}}美元梅勒=梅勒([“轴”= > [“类型”= >“记忆”、]]);

嵌套的选择也支持所需的选项,验证(类型、价值)和归一化的值。如果一个嵌套的默认值选项取决于另一个选项在父级别定义,添加第二个选项关闭访问他们的理由:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){美元解析器- >setDefault (“沙盒”,);美元解析器- >setDefault (“轴”,函数(OptionsResolver美元spoolResolver、期权美元){美元spoolResolver- >setDefaults ([“类型”= >美元(“沙盒”]?“记忆”:“文件”,/ /……]);});}}

谨慎

参数必须关闭类型的暗示OptionsResolver选项分别。否则,闭包本身被认为是选项的默认值。

在同样的方式,父母选择可以正常访问嵌套选项数组:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
梅勒{/ /……公共函数configureOptions(OptionsResolver美元解析器){美元解析器- >setDefault (“轴”,函数(OptionsResolver美元spoolResolver){美元spoolResolver- >setDefaults ([“类型”= >“文件”,/ /……]);});美元解析器- >setDefault (“分析”,函数(选项美元选项){返回“文件”= = =美元选项(“轴”][“类型”];});}}

请注意

一个选项是定义为嵌套意味着你必须通过一个数组的值在运行时解决它。

原型的选择

在有些情况下,您将不得不解决和验证一组选项,在另一个选择可能会重复很多次。让我们想象一个连接选项,将接受一系列的数据库连接主机,数据库,用户密码每一个。

实现这个的最好办法是定义连接选择原型:

1 2 3 4 5 6
美元解析器- >setDefault (“连接”,函数(OptionsResolver美元connResolver){美元connResolver- >setPrototype (真正的)- >setRequired ([“主机”,“数据库”])- >setDefaults ([“用户”= >“根”,“密码”= >]);});

根据原型定义在上面的示例中,可以有多个连接数组如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
美元解析器- >解决([“连接”= > [“默认”= > [“主机”= >“127.0.0.1”,“数据库”= >sob娱乐下载ymfony的),“测试”= > [“主机”= >“127.0.0.1”,“数据库”= >“ob娱乐下载symfony_test”,“用户”= >“测试”,“密码”= >“测试”),/ /……)));

数组的键(默认的,测试等)的原型选择validation-free和可以任意值,有助于区分连接。

请注意

原型的选择只能是定义在一个嵌套的选择和在其决议将期望数组的数组。

不以为然的选择

一旦一个选项已经过时或者你决定不维护它了,你可以使用反对它setDeprecated ()方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
美元解析器- >setDefined ([的主机名,“主机”])/ /这个输出以下通用弃用信息:/ / acme /包1.2以来:选择“主机名”是弃用。- >setDeprecated (的主机名,“acme /包”,“1.2”)/ /还可以通过一个定制的弃用消息(% %占位符名称可用)- >setDeprecated (的主机名,“acme /包”,“1.2”,“选择”主机名”是弃用,使用“主机”。);

请注意

弃用消息将触发只有所使用的选项,它的值是由用户提供的或延迟选项的选择是在闭包中评估和标准化者。

请注意

当使用一个选择弃用在您自己的图书馆,你可以通过的第二个参数offsetGet ()方法不会触发弃用警告。

而不是传递信息,还可以通过一个闭包,返回一个字符串(弃用消息)或一个空字符串忽视弃用。这个闭包是有用的只允许反对某些类型或值的选择:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
美元解析器- >setDefault (“加密”,)- >setDefault (“端口”,)- >setAllowedTypes (“端口”,(“零”,“int”])- >setDeprecated (“端口”,“acme /包”,“1.2”,函数(选项美元选项,美元价值){如果(= = =美元价值){返回”通过“零”选择“端口”是弃用,通过一个整数代替。”;}/ /弃用也可能依赖于另一个选择如果(“ssl”= = =美元选项(“加密”)& &456年= = !美元价值){返回”比“456”时,通过不同的端口“加密”选项设置为“ssl”弃用。;}返回;});

请注意

弃用基于只有当触发选项的值由用户提供。

这个闭包接收验证后的值作为参数选择和正常化之前它当期权被解决。

链接选项配置

在许多情况下,您可能需要定义多个配置为每个选项。例如,假设InvoiceMailer类有一个主机选项是必需的和运输选项之一sendmail,邮件smtp。可以提高代码的可读性避免重复选项名称为每个配置使用定义()方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ /……InvoiceMailer{/ /……公共函数configureOptions(OptionsResolver美元解析器){/ /……美元解析器- >定义(“主机”)- >要求()- >默认的(“smtp.example.org”)- >allowedTypes (“字符串”)- >信息(“IP地址或主机名”);美元解析器- >定义(“交通”)- >要求()- >默认的(“交通”)- >allowedValues (“发送邮件”,“邮件”,“smtp”);}}

性能调整

与当前的实现configureOptions ()方法将呼吁每一个的实例梅勒类。根据选择的数量配置和创建实例的数量,这可能为应用程序添加显著的开销。如果开销成为一个问题,你可以改变你的每个类只配置一次代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21日22日23日24日25日26日
/ /……梅勒{私人静态美元resolversByClass= [];受保护的美元选项;公共函数__construct(数组美元选项= []){/ /这是什么类型的梅勒,梅勒,GoogleMailer,…吗?美元= get_class (美元);/ /是configureOptions()之前执行这个类?如果(!收取(自我::$resolversByClass [美元))){自我::$resolversByClass [美元]=OptionsResolver ();美元- >configureOptions (自我::$resolversByClass [美元]);}美元- >选择=自我::$resolversByClass [美元]- >解决(美元选项);}公共函数configureOptions(OptionsResolver美元解析器){/ /……}}

现在,OptionsResolver每次创建类实例和重用的。请注意,这可能会导致长时间运行的应用程序中的内存泄漏,如果默认选项包含对象的引用或对象图。如果是这种情况,实现方法clearOptionsConfig ()并定期称之为:

1 2 3 4 5 6 7 8 9 10 11 12
/ /……梅勒{私人静态美元resolversByClass= [];公共静态函数clearOptionsConfig(){自我::$resolversByClass = [];}/ /……}

就是这样!你现在有了所需的所有工具和知识过程选项在您的代码中。

这项工作,包括代码示例,许可下Creative Commons冲锋队3.0许可证。
ob娱乐下载Symfony 6.0支持通过SensioLabs