OptionsResolver组件
编辑本页警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 5.2,现已不再维护。
读本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。
OptionsResolver组件
的OptionsResolver组件是改进的array_replacePHP函数。它允许您创建一个包含必需选项、默认值、验证(类型、值)、规范化等的选项系统。
安装
1
$作曲家需要symfony/选项解析ob娱乐下载器
请注意
如果在Symfony应用程序外部安装此组件,则必须要求ob娱乐下载供应商/ autoload.php
文件,以启用Composer提供的类自动加载机制。读这篇文章欲知详情。
使用
想象一下你有一个梅勒
类,它有四个选项:宿主
,用户名
,密码
而且港口
:
1 2 3 4 5 6 7 8 9
类梅勒{受保护的$选项;公共函数__construct(数组$选项= []){$这->选择=$选项;}}
访问选择美元
,你需要添加一些样板代码来检查设置了哪些选项:
12 3 4 5 6 7 8 9 10 11 12 13 14 15
类梅勒{/ /……公共函数sendMail($从,$来){$邮件=……;$邮件->setHost ($这->选项(“主机”) ? ?“smtp.example.org”);$邮件->setUsername ($这->选项(“用户名”) ? ?“用户”);$邮件->向setPassword ($这->选项(“密码”) ? ?“爸爸$ $词”);$邮件->setPort ($这->选项(“端口”) ? ?25);/ /……}}
此外,选项的默认值隐藏在代码的业务逻辑中。使用array_replace要解决这个问题:
12 3 4 5 6 7 8 9 10 11 12 13 14
类梅勒{/ /……公共函数__construct(数组$选项= []){$这->选项= array_replace([“主机”= >“smtp.example.org”,“用户名”= >“用户”,“密码”= >“爸爸$ $词”,“端口”= >25),$选项);}}
现在,保证所有四个选项都已设置,但在使用梅勒
类:
1 2 3
$梅勒=新梅勒([“usernme”= >“johndoe”,// username被错误地拼写成usernme]);
不会显示错误。在最好的情况下,bug会在测试过程中出现,但开发人员会花时间寻找问题。在最坏的情况下,bug可能直到部署到活动系统中才会出现。
幸运的是,OptionsResolver类帮助您解决这个问题:
12 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: The option "usernme"不存在。//定义的选项是:"host", "password", "port", "username"
其余的代码可以在没有样板代码的情况下访问选项的值:
12 3 4 5 6 7 8 9 10 11 12 13 14 15
/ /……类梅勒{/ /……公共函数sendMail($从,$来){$邮件=……;$邮件->setHost ($这->选项(“主机”]);$邮件->setUsername ($这->选项(“用户名”]);$邮件->向setPassword ($这->选项(“密码”]);$邮件->setPort ($这->选项(“端口”]);/ /……}}
将选项配置拆分为一个单独的方法是一个很好的实践:
12 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 ()
调整选项配置的方法:
12 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 (“主机”);}}
如果省略了必选选项,则aMissingOptionsException将被抛出:
1 2 3
$梅勒=新梅勒();// MissingOptionsException:缺少必需的选项“host”。
的setRequired ()方法接受单个名称或选项名称数组(如果您有多个必选选项):
1 2 3 4 5 6 7 8 9 10 11
/ /……类梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){/ /……$解析器->setRequired ([“主机”,“用户名”,“密码”]);}}
使用isRequired ()以确定是否需要某个选项。你可以使用getRequiredOptions ()检索所有必需选项的名称:
12 3 4 5 6 7 8 9 10 11 12 13 14
/ /……类GoogleMailer扩展梅勒{公共函数configureOptions(OptionsResolver$解析器){父::configureOptions ($解析器);如果($解析器->isRequired (“主机”)) {/ /……}$requiredOptions=$解析器->getRequiredOptions ();}}
如果要检查默认选项中是否仍然缺少必需的选项,可以使用isMissing ().这和isRequired ()如果已经设置了必需的选项,该方法将返回false:
12 34 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 (“主机”);// => true$解析器->isMissing (“主机”);// => true$解析器->setDefault (“主机”,“smtp.google.com”);$解析器->isRequired (“主机”);// => true$解析器->isMissing (“主机”);// => false}}
的getMissingOptions ()方法使您可以访问所有缺失选项的名称。
类型验证
您可以对选项运行额外的检查,以确保它们被正确地传递。要验证选项的类型,请调用setAllowedTypes ():
12 3 4 5 6 7 8 9 10 11 12 13 14 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的选项“host”是//类型为string,但类型为int
在子类中,您可以使用addAllowedTypes ()添加其他允许的类型,而不删除已设置的类型。
值验证
有些选项只能接受固定的预定义值列表中的一个。例如,假设梅勒
类有一个运输
选项,可以是其中之一sendmail
,邮件
而且smtp
.使用方法setAllowedValues ()验证所传递的选项是否包含以下值之一:
12 3 4 5 6 7 8 9 10 11 12
/ /……类梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){/ /……$解析器->setDefault (“交通”,“发送邮件”);$解析器->setAllowedValues (“交通”, (“发送邮件”,“邮件”,“smtp”]);}}
如果你通过了一个无效的传输,一个InvalidOptionsException抛出:
1 2 3 4 5 6
$梅勒=新梅勒([“交通”= >“发送邮件”]);// InvalidOptionsException:选项"transport"的值是"send-mail"//无效。可接受的值为:sendmail, mail, smtp
对于具有更复杂验证方案的选项,传递一个返回的闭包真正的
对于可接受的值和假
对于无效值:
1 2 3 4
/ /……$解析器->setAllowedValues (“交通”,函数($价值){//返回true或false});
在子类中,您可以使用addAllowedValues ()添加额外的允许值,而不删除已设置的值。
选择归一化
有时,在使用选项值之前,需要对它们进行规范化。例如,假设宿主
应该总是以http://
.为此,您可以编写规范化程序。标准化器在验证一个选项之后执行。可以通过调用来配置规范化器setNormalizer ():
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
使用ob娱乐下载\组件\OptionsResolver\选项;/ /……类梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){/ /……$解析器->setNormalizer (“主机”,函数(选项$选项,$价值){如果(“http://”! = = substr ($价值,0,7)) {$价值=“http://”.$价值;}返回$价值;});}}
规范化器接收实际值美元的价值
并返回规范化的形式。你可以看到闭包也需要选择美元
参数。如果你需要在规范化过程中使用其他选项,这是很有用的:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
/ /……类梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){/ /……$解析器->setNormalizer (“主机”,函数(选项$选项,$价值){如果(“http://”! = = substr ($价值,0,7) & &“https://”! = = substr ($价值,0,8)) {如果(“ssl”= = =$选项[“加密”) {$价值=“https://”.$价值;}其他的{$价值=“http://”.$价值;}}返回$价值;});}}
要规范化父类中正在规范化的子类中的新允许值,请使用addNormalizer ().这条路,美元的价值
参数将接收先前归一化的值,否则您可以在新的归一化器前面通过传入真正的
作为第三个论点。
依赖于其他选项的默认值
属性的默认值港口
方法的用户所选择的加密梅勒
类。更准确地说,您希望将端口设置为465
如果使用SSL和25
否则。
类的默认值传递闭包来实现此特性港口
选择。闭包接收选项作为参数。基于这些选项,您可以返回所需的默认值:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
使用ob娱乐下载\组件\OptionsResolver\选项;/ /……类梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){/ /……$解析器->setDefault (“加密”,零);$解析器->setDefault (“端口”,函数(选项$选项){如果(“ssl”= = =$选项[“加密”) {返回465;}返回25;});}}
谨慎
可调用对象的实参必须是类型提示为选项
.否则,可调用对象本身被视为该选项的默认值。
请注意
对象时才执行闭包港口
选项不是由用户设置,也不是在子类中覆盖。
之前设置的默认值可以通过在闭包中添加第二个参数来访问:
12 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;});}}
如示例所示,如果您想在子类中重用父类中设置的默认值,则此特性非常有用。
没有默认值的选项
在某些情况下,定义一个选项而不设置默认值是有用的。如果您需要知道用户是否实际上是否设置一个选项。例如,如果你为一个选项设置了默认值,不可能知道用户是否传递了这个值,或者它是否来自默认值:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
/ /……类梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){/ /……$解析器->setDefault (“端口”,25);}/ /……公共函数sendMail($从,$来){//这是默认值还是类的调用者真的//设置端口为25?如果(25= = =$这->选项(“端口”) {/ /……}}}
你可以使用setDefined ()定义一个选项而不设置默认值。然后,只有在实际传递给时,该选项才会包含在已解析的选项中解决():
12 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 ()让您了解定义了哪些选项:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
/ /……类GoogleMailer扩展梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){父::configureOptions ($解析器);如果($解析器->isDefined (“主机”)) {//下面的一个被调用:// $resolver->setDefault('host',…);/ /解析器- > setRequired('主机');/ /解析器- > setDefined('主机');}$definedOptions=$解析器->getDefinedOptions ();}}
嵌套的选择
假设您有一个名为线轴
哪个有两个子选项类型
而且路径
.属性的默认值传递闭包,而不是将其定义为简单的值数组线轴
选项。OptionsResolver论点。基于此实例,您可以定义下面的选项线轴
及其期望的默认值:
12 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($从,$来){如果(“记忆”= = =$这->选项(“轴”] [“类型”) {/ /……}}}$梅勒=新梅勒([“轴”= > [“类型”= >“记忆”,],]);
嵌套选项还支持所需的选项、验证(类型、值)和其值的规范化。如果嵌套选项的默认值依赖于父级中定义的另一个选项,请添加第二个选项
参数来访问它们:
12 3 4 5 6 7 8 9 10 11 12 13 14 15
类梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){$解析器->setDefault (“沙盒”,假);$解析器->setDefault (“轴”,函数(OptionsResolver$spoolResolver、期权$父){$spoolResolver->setDefaults ([“类型”= >$父[“沙盒”] ?“记忆”:“文件”,/ /……]);});}}
谨慎
闭包的参数必须是类型提示为OptionsResolver
而且选项
分别。否则,闭包本身被视为该选项的默认值。
以同样的方式,父选项可以像普通数组一样访问嵌套选项:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
类梅勒{/ /……公共函数configureOptions(OptionsResolver$解析器){$解析器->setDefault (“轴”,函数(OptionsResolver$spoolResolver){$spoolResolver->setDefaults ([“类型”= >“文件”,/ /……]);});$解析器->setDefault (“分析”,函数(选项$选项){返回“文件”= = =$选项[“轴”] [“类型”];});}}
请注意
一个选项被定义为嵌套的事实意味着您必须在运行时传递一个值数组来解析它。
弃用选项
5.1
签署setDeprecated ()
方法从setDeprecated(string $option, ?string $message)
来setDeprecated(字符串$option,字符串$package,字符串$version, $message)
在Syob娱乐下载mfony 5.1。
选项过时或决定不再维护时,可以使用setDeprecated ()方法:
12 3 4 5 6 7 8 9 10 11 12 13 14 15
$解析器->setDefined ([的主机名,“主机”])//输出以下通用弃用消息://由于acme/package 1.2:选项“hostname”已弃用。->setDeprecated (的主机名,“acme /包”,“1.2”)//你也可以传递一个自定义的弃用消息(%name%占位符可用)->setDeprecated (的主机名,“acme /包”,“1.2”,“不推荐使用‘hostname’选项,改用‘host’。”) ;
请注意
只有在某个地方使用了该选项,或者该选项的值由用户提供,或者该选项在惰性选项和正常化器的闭包中计算时,才会触发弃用消息。
请注意
当您在自己的库中使用已弃用的选项时,可以通过假
的第二个参数offsetGet ()方法以不触发弃用警告。
除了传递消息,你还可以传递一个闭包,它返回一个字符串(弃用消息)或一个空字符串来忽略弃用。这个闭包只适用于弃用某些允许的类型或值:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
$解析器->setDefault (“加密”,零)->setDefault (“端口”,零)->setAllowedTypes (“端口”, (“零”,“int”])->setDeprecated (“端口”,“acme /包”,“1.2”,函数(选项$选项,$价值){如果(零= = =$价值) {返回“向选项“端口”传递“null”已弃用,而是传递一个整数。”;}// deprecation也可能依赖于另一个选项如果(“ssl”= = =$选项[“加密”) & &456= = !$价值) {返回“当“加密”选项设置为“ssl”时,不建议使用与“456”不同的端口。”;}返回”;});
请注意
只有当用户提供该选项时,才会触发基于该值的弃用。
这个闭包在对选项进行验证之后,在解析选项时对其进行规范化之前,接收选项的值作为参数。
链接选项配置
在许多情况下,您可能需要为每个选项定义多个配置。例如,假设InvoiceMailer
类有一个宿主
选项运输
选项,可以是其中之一sendmail
,邮件
而且smtp
.属性可以提高代码的可读性,避免重复每个配置的选项名称定义()方法:
12 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”]);}}
5.1
的定义()
而且信息()
方法是在Symfony 5.1中引入的。ob娱乐下载
性能调整
对于当前的实现,configureOptions ()
方法的每个实例都将调用梅勒
类。根据选项配置的数量和创建实例的数量,这可能会给应用程序增加明显的开销。如果这个开销成为一个问题,你可以改变你的代码,每个类只做一次配置:
12 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(数组$选项= []){//这是什么类型的Mailer, Mailer, GoogleMailer,…?$类= get_class ($这);// configureOptions()在这个类之前执行了吗?如果(!收取(自我:: $resolversByClass [$类))) {自我:: $resolversByClass [$类] =新OptionsResolver ();$这->configureOptions (自我:: $resolversByClass [$类]);}$这->选择=自我:: $resolversByClass [$类]->解决($选项);}公共函数configureOptions(OptionsResolver$解析器){/ /……}}
现在,OptionsResolver实例将在每个类中创建一次,并从那时开始重用。注意,如果默认选项包含对对象或对象图的引用,这可能会导致长时间运行的应用程序发生内存泄漏。如果是这样的话,实现一个方法clearOptionsConfig ()
并定期调用它:
12 3 4 5 6 7 8 9 10 11 12
/ /……类梅勒{私人静态$resolversByClass= [];公共静态函数clearOptionsConfig(){自我:: $resolversByClass = [];}/ /……}
就是这样!现在您已经拥有了处理代码中的选项所需的所有工具和知识。