《交响乐5:快车道》的封面欧宝娱乐app下载地址

欧宝娱乐app下载地址交响乐5:快车道是学习现代Symfony发展的最佳书籍,从零到生产。欧宝娱乐app下载地址+300页展示与Docker,API,队列和欧宝娱乐app下载地址异步任务,网克,SPA等的Symfony。

OptionsResolver组件

OptionsResolver组件

OptionsEresolver组件是改进的替代品array_replacePHP函数。它允许您创建一个包含所需选项、默认值、验证(类型、值)、规范化等内容的选项系统。

安装

1
$作曲家需要symfony / op欧宝娱乐app下载地址tions-resolver

笔记

如果在Symfony应用程序之外安装此组件,则必须要求欧宝娱乐app下载地址供应商/ autoload.php.以启用Composer提供的类自动加载机制。读这篇文章为更多的细节。

使用

想象一下你有一个邮箱类,它有四个选项:宿主用户名密码港口

班级邮箱{保护$选项;公共函数__construct数组$选项=[]){这个美元->选项=$选项;}}

当访问$选项,您需要添加一些样板代码来检查设置哪些选项:

班级邮箱{/ /……公共函数sendMail从美元美元,{美元的邮件=;美元的邮件->setHost这个美元->选项[“主机”]? ?“smtp.example.org”);美元的邮件->setUsername这个美元->选项[“用户名”]? ?'用户');美元的邮件->向setPassword这个美元->选项[“密码”]? ?'pa $ word');美元的邮件->setPort这个美元->选项[“端口”]? ?25);/ /……}}

此外,选项的默认值隐藏在代码的业务逻辑中。使用array_replace解决:

班级邮箱{/ /……公共函数__construct数组$选项=[]){这个美元->选项=array_replace([“主机”= >“smtp.example.org”“用户名”= >'用户'“密码”= >'pa $ word'“端口”= >25),$选项);}}

现在保证设置了所有四个选项,但是在使用邮箱类:

梅勒美元=邮箱([“usernme”= >“johndoe”// username被错误地拼写为usernme]);

不会显示任何错误。在最佳情况下,错误会出现在测试期间,但开发人员将花时间寻找问题。在最坏的情况下,在部署到实时系统之前可能不会出现错误。

幸运的是,欧宝娱乐app下载地址Symfony \ \ OptionsResolver \ OptionsResolver组件课程可帮助您解决此问题:

使用欧宝娱乐app下载地址Symfony \ \ OptionsResolver \ OptionsResolver组件;班级邮箱{/ /……公共函数__construct数组$选项=[]){美元的解析器=OptionsResolver();美元的解析器->setdefaults.([“主机”= >“smtp.example.org”“用户名”= >'用户'“密码”= >'pa $ word'“端口”= >25]);这个美元->选项=美元的解析器->解决$选项);}}

与之前一样,所有选项都保证被设置。此外,一个欧宝娱乐app下载地址Symfony \组件\ \ UndefinedOptionsException OptionsResolver \异常如果传递了未知选项则抛出:

梅勒美元=邮箱([“usernme”= >“johndoe”]);// UndefinedOptionsException: usernme选项不存在。//定义选项包括:“主机”,“密码”,“端口”,“用户名”

其余的代码可以在没有样板代码的情况下访问选项的值:

/ /……班级邮箱{/ /……公共函数sendMail从美元美元,{美元的邮件=;美元的邮件->setHost这个美元->选项[“主机”]);美元的邮件->setUsername这个美元->选项[“用户名”]);美元的邮件->向setPassword这个美元->选项[“密码”]);美元的邮件->setPort这个美元->选项[“端口”]);/ /……}}

将选项配置拆分为单独的方法是一个很好的实践:

/ /……班级邮箱{/ /……公共函数__construct数组$选项=[]){美元的解析器=OptionsResolver();这个美元->configureOptions美元的解析器);这个美元->选项=美元的解析器->解决$选项);}公共函数configureOptionsOptionsResolver美元的解析器{美元的解析器->setdefaults.([“主机”= >“smtp.example.org”“用户名”= >'用户'“密码”= >'pa $ word'“端口”= >25“加密”= >]);}}

首先,您的代码变得更容易读取,特别是如果构造函数不仅仅是处理选项。其次,子类现在可以覆盖configureOptions()调整选项配置的方法:

/ /……班级GoogleMailer扩展邮箱{公共函数configureOptionsOptionsResolver美元的解析器{父母::configureOptions美元的解析器);美元的解析器->setdefaults.([“主机”= >“smtp.google.com”“加密”= >“ssl”]);}}

所需的选择

如果呼叫者必须设置一个选项,请通过该选项setRequired ()。例如,制作宿主选项必需,你可以做:

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setRequired.“主机”);}}

如果您省略了必填选项,a欧宝娱乐app下载地址Symfony \组件\ \ MissingOptionsException OptionsResolver \异常将被扔:

梅勒美元=邮箱();// sciteOptionsexception:缺少所需的选项“主机”。

setRequired ()方法接受一个名称或一个选项名称数组(如果有多个必需选项):

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setRequired.([“主机”“用户名”“密码”]);}}

使用isRequired ()找出是否需要某个选项。您可以使用getRequiredOptions()检索所有必需选项的名称:

/ /……班级GoogleMailer扩展邮箱{公共函数configureOptionsOptionsResolver美元的解析器{父母::configureOptions美元的解析器);如果美元的解析器->isRequired“主机”)){/ /……}requiredOptions美元=美元的解析器->getRequiredOptions();}}

如果要检查默认选项中是否仍然缺少必需的选项,可以使用isMissing ()。这个和之间的差异isRequired ()如果已设置所需的选项,此方法将返回false:

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setRequired.“主机”);}}/ /……班级GoogleMailer扩展邮箱{公共函数configureOptionsOptionsResolver美元的解析器{父母::configureOptions美元的解析器);美元的解析器->isRequired“主机”);// => true美元的解析器->isMissing“主机”);// => true美元的解析器->setDefault“主机”“smtp.google.com”);美元的解析器->isRequired“主机”);// => true美元的解析器->isMissing“主机”);/ / = > false}}

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

类型验证

您可以对选项运行额外的检查,以确保它们被正确地传递。要验证选项的类型,请调用setAllowedTypes ()

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……//指定一个允许的类型美元的解析器->setAllowedTypes“主机”“字符串”);//指定多种允许的类型美元的解析器->setAllowedTypes“端口”[“零”'int']);//递归检查阵列中的所有项目,用于类型美元的解析器->setAllowedTypes“日期”'约会时间[]');美元的解析器->setAllowedTypes“端口”“int[]”);}}

可以传递任何类型is_类型< > ()函数在PHP中定义。您还可以传递完全限定的类名或接口名(使用instanceof.)。此外,可以通过将类型加上后缀来递归验证数组中的所有项[]

如果您现在传递一个无效的选项,则欧宝娱乐app下载地址Symfony \ Component \ OptionsResolver \ Exception \ InvalidOptionSexception被抛出:

梅勒美元=邮箱([“主机”= >25]);// InvalidOptionsException: The option "host" with value "25" is//期望的类型是“string”,但类型是“int”

在子类中,可以使用addallowedtypes()添加额外允许的类型,而不删除已经设置的类型。

值验证

某些选项只能拍摄预定义值的固定列表之一。例如,假设邮箱班有A.运输选项可以是其中一个sendmail邮件smtp。使用这种方法setAllowedValues ()要验证传递的选项包含以下值之一:

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setDefault“交通”“发送邮件”);美元的解析器->setAllowedValues“交通”[“发送邮件”“邮件”“smtp”]);}}

如果你通过无效的运输,一个欧宝娱乐app下载地址Symfony \ Component \ OptionsResolver \ Exception \ InvalidOptionSexception被抛出:

梅勒美元=邮箱([“交通”= >'发送邮件']);// InvalidOptionSexception:具有“发送邮件”的选项“传输”/ /无效。可接受的值是:"sendmail", "mail", "smtp"

有关具有更复杂验证方案的选项,请传递返回的关闭真正的可接受的值和错误的无效值:

/ /……美元的解析器->setAllowedValues“交通”函数美元的价值{//返回true或false});

在子类中,可以使用addallowedValues()添加额外的允许值而不删除已经设置的值。

选择归一化

有时,在使用选项值之前需要对它们进行规范化。例如,假设宿主应该始终开始http://。为了做到这一点,你可以写标准化器。标准化器在验证一个选项之后执行。可以通过调用配置规范化器setNormalizer ()

使用欧宝娱乐app下载地址Symfony \ Component \ OptionsResolver \选项;/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setnormalizer“主机”函数选项$选项美元的价值{如果“http://”= = !字符串的子串美元的价值0.7.)){美元的价值=“http://”美元的价值;}返回美元的价值;});}}

标准化器接收实际美元的价值并返回归一化表单。你看到关闭也需要一个$选项范围。如果您需要在归一化期间使用其他选项,这很有用:

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setnormalizer“主机”函数选项$选项美元的价值{如果“http://”= = !字符串的子串美元的价值0.7.&&'https://'= = !字符串的子串美元的价值0.8.)){如果“ssl”===.$选项[“加密”]){美元的价值='https://'美元的价值;}其他的{美元的价值=“http://”美元的价值;}}返回美元的价值;});}}

在父类中规范化的子类中规范化一个允许的新值,使用AddNormalizer()。这种方式,美元的价值参数将接收先前的规范化值,否则您可以通过传入来添加新的规范化器真正的作为第三个论点。

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

属性的默认值港口属性的用户选择的加密邮箱类。更准确地说,您希望将端口设置为465如果使用SSL和25除此以外。

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

使用欧宝娱乐app下载地址Symfony \ Component \ OptionsResolver \选项;/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setDefault“加密”);美元的解析器->setDefault“端口”函数选项$选项{如果“ssl”===.$选项[“加密”]){返回465;}返回25;});}}

谨慎

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

笔记

对象时才执行闭包港口选项不是由用户设置或在子类中重写。

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

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setdefaults.([“加密”= >“主机”= >'example.org']);}}班级GoogleMailer扩展邮箱{公共函数configureOptionsOptionsResolver美元的解析器{父母::configureOptions美元的解析器);美元的解析器->setDefault“主机”函数选项$选项previousValue美元{如果“ssl”===.$选项[“加密”]){返回“secure.example.org”;}//占据基类中配置的默认值返回previousValue美元;});}}

正如在示例中所看到的,如果您想在子类中重用父类中设置的默认值,该特性非常有用。

没有默认值的选项

在某些情况下,定义一个选项而不设置默认值是很有用的。如果您需要知道用户是否实际上是否设置一个选项。例如,如果你为一个选项设置了默认值,就不可能知道是用户传递了这个值还是它来自默认值:

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setDefault“端口”25);}/ /……公共函数sendMail从美元美元,{//这是默认值还是类的调用者//设置端口为25?如果25===.这个美元->选项[“端口”]){/ /……}}}

您可以使用setDefined ()在不设置默认值的情况下定义一个选项。然后,如果它实际传递给已解决的选项,则该选项将仅包含在已解析的选项中解决()

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setDefined“端口”);}/ /……公共函数sendMail从美元美元,{如果array_key_exists.“端口”这个美元->选项)){回声“集合!”;}其他的{回声“没有!”;}}}梅勒美元=邮箱();梅勒美元->sendMail从美元美元,);// =>未设置!梅勒美元=邮箱([“端口”= >25]);梅勒美元->sendMail从美元美元,);/ / = >设置!

如果你想一次性定义多个选项,你也可以传递一个选项名数组:

/ /……班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->setDefined([“端口”“加密”]);}}

的方法isDefined ()getDefinedOptions ()让你知道哪些选项是定义的:

/ /……班级GoogleMailer扩展邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{父母::configureOptions美元的解析器);如果美元的解析器->被定义为“主机”)){//下面的一个叫做:// $ ressver-> setDefault('host',......);// $ resolver-> setRequired('host');/ /解析器- > setDefined('主机');}$ SimiceOptions.=美元的解析器->getDefinedOptions();}}

嵌套选项

假设您有一个名为的选项线轴哪个有两个子选项类型小路。属性的默认值可以传递一个闭包,而不是将其定义为一个简单的值数组线轴选择与欧宝娱乐app下载地址Symfony \ \ OptionsResolver \ OptionsResolver组件争论。基于此实例,您可以定义下面的选项线轴和所需的默认值:

班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{美元的解析器->setDefault“羽毛”函数OptionsResolverspoolResolver美元{spoolResolver美元->setdefaults.([“类型”= >“文件”'小路'= >'/ path / to / spool']);spoolResolver美元->setAllowedValues“类型”[“文件”“记忆”]);spoolResolver美元->setAllowedTypes'小路'“字符串”);});}公共函数sendMail从美元美元,{如果“记忆”===.这个美元->选项[“羽毛”] [“类型”]){/ /……}}}梅勒美元=邮箱([“羽毛”= >[“类型”= >“记忆”),]);

嵌套选项还支持必需选项、验证(类型、值)和值的规范化。如果嵌套选项的默认值依赖于父级中定义的另一个选项,则添加第二个选项封闭的论点访问它们:

班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{美元的解析器->setDefault“沙盒”错误的);美元的解析器->setDefault“羽毛”函数OptionsResolverspoolResolver美元选项$父母{spoolResolver美元->setdefaults.([“类型”= >$父母[“沙盒”]“记忆”“文件”/ /……]);});}}

谨慎

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

同样,父选项可以像普通数组一样访问嵌套选项:

班级邮箱{/ /……公共函数configureOptionsOptionsResolver美元的解析器{美元的解析器->setDefault“羽毛”函数OptionsResolverspoolResolver美元{spoolResolver美元->setdefaults.([“类型”= >“文件”/ /……]);});美元的解析器->setDefault'剖析'函数选项$选项{返回“文件”===.$选项[“羽毛”] [“类型”];});}}

笔记

一个选项被定义为嵌套的事实意味着您必须传递一个值数组来在运行时解析它。

不以为然的选择

5.1新版功能:签名setDeprecated ()方法从setDeprecated(字符串$选项,字符串?美元的消息)setDeprecated(字符串$选项,字符串美元的包,字符串美元的版本,美元的消息)在sy欧宝娱乐app下载地址mfony 5.1。

当某个选项过时或您决定不再维护它时,可以使用setDeprecated ()方法:

美元的解析器->setDefined([的主机名“主机”])//输出以下通用弃用消息://由于acme/package 1.2:选项"hostname"被弃用。->setdeprecated.的主机名'acme / package'“1.2”//您还可以通过自定义弃用消息(%名称%占位符可用)->setdeprecated.的主机名'acme / package'“1.2”'不推荐使用“hostname”选项,而是使用“主机”。;

笔记

只有在某个位置使用该选项时,才会触发弃用消息,用户的值由用户提供,或者在延迟选项和癌症的封闭中进行选项。

笔记

在您在自己的库中使用您不推荐使用的选项时,可以通过错误的作为第二个论点offsetget()方法以不触发弃用警告。

你也可以传递一个闭包,它返回一个字符串(弃用消息)或一个空字符串来忽略弃用,而不是传递消息。这个闭包只用于弃用选项中允许的一些类型或值:

美元的解析器->setDefault“加密”->setDefault“端口”->setAllowedTypes“端口”[“零”'int'])->setdeprecated.“端口”'acme / package'“1.2”函数选项$选项美元的价值{如果===.美元的价值{返回不赞成将"null"传递给选项"port",而是传递一个整数。;}//弃用也可能依赖于另一个选项如果“ssl”===.$选项[“加密”]&&456= = !美元的价值{返回当“加密”选项设置为“ssl”时,不建议传递与“456”不同的端口。;}返回'';});

笔记

仅当用户提供该选项时,才会触发基于该值的弃用。

此闭包在验证选项之后和在解析选项时将其规范化之前接收选项的值作为参数。

链接选项配置

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

/ /……班级邀请邮件{/ /……公共函数configureOptionsOptionsResolver美元的解析器{/ /……美元的解析器->定义“主机”->要求()->默认的“smtp.example.org”->允许赠品“字符串”->信息' IP地址或主机名');美元的解析器->定义“交通”->要求()->默认的“交通”->允许的价值([“发送邮件”“邮件”“smtp”]);}}

5.1新版功能:定义()信息()Symfony 5.1中介绍了这些方法。欧宝娱乐app下载地址

性能调整

对于当前实现,configureOptions()方法的每个实例都将被调用邮箱类。根据选项配置的数量和创建的实例的数量,这可能会给应用程序增加明显的开销。如果这种开销成为一个问题,你可以改变你的代码,每个类只做一次配置:

/ /……班级邮箱{私人的静止的resolversByClass美元=[];保护$选项;公共函数__construct数组$选项=[]){//这是什么类型的邮件,一个邮件,一个google邮件,…?$班级=get_class.这个美元);//在此类之前执行的configureOptions()?如果!!收取自我::resolversByClass美元[$班级))){自我::resolversByClass美元[$班级]=OptionsResolver();这个美元->configureOptions自我::resolversByClass美元[$班级]);}这个美元->选项=自我::resolversByClass美元[$班级]->解决$选项);}公共函数configureOptionsOptionsResolver美元的解析器{/ /……}}

现在,欧宝娱乐app下载地址Symfony \ \ OptionsResolver \ OptionsResolver组件实例将为每个类创建一次,并从此重用。请注意,如果默认选项包含对对象或对象图的引用,那么这可能会导致长时间运行的应用程序中的内存泄漏。如果是这种情况,那么实现一个方法clearOptionsconfig()并定期调用它:

/ /……班级邮箱{私人的静止的resolversByClass美元=[];公共静止的函数clearOptionsConfig(){自我::resolversByClass美元=[];}/ /……}

就是这样!现在,您已经拥有了在代码中处理选项所需的所有工具和知识。

这项工作,包括代码样本,是在一个创作共用BY-SA 3.0执照。