OptionsResolver组件
编辑本页警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 4.1,现已不再维护。
读本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。
OptionsResolver组件
OptionsResolver组件是array_replace类固醇。它允许您创建一个包含必需选项、默认值、验证(类型、值)、规范化等的选项系统。
安装
1
$作曲家需要symfony/选项解析ob娱乐下载器
或者,您可以克隆https://github.com/ob娱乐下载symfony/options-resolver存储库。
请注意
如果在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 16 17 18 19 20 21 22 23 24 25 26
类梅勒{/ /……公共函数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”,// usernme拼写错误(而不是username)]);
不会显示错误。在最好的情况下,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
在子类中,您可以使用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://”.$价值;}}返回$价值;});}}
依赖于其他选项的默认值
属性的默认值港口
方法的用户所选择的加密梅勒
类。更准确地说,您希望将端口设置为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 ();}}
性能调整
对于当前的实现,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 = [];}/ /……}
就是这样!现在您已经拥有了处理代码中的选项所需的所有工具和知识。