服务容器
编辑本页服务容器
截屏视频
你更喜欢视频教程吗?请查看ob娱乐下载Symfony Fundamentals系列截图.
您的申请是完整的一个“Mailer”对象可以帮助你发送电子邮件,而另一个对象可以帮助你将东西保存到数据库中。几乎一切你的应用“做”实际上是由这些对象之一完成的。每次你安装一个新的包,你就可以访问更多!
在Syob娱乐下载mfony中,调用这些有用的对象服务每个服务都存在于一个非常特殊的对象中服务容器.容器允许您集中构造对象的方式。它使您的生活更轻松,促进强大的架构和超级快!
获取和使用服务
当你启动Symfony应用时,你的容器ob娱乐下载已经包含许多服务。这些就像工具:等着你去利用它们。在你的控制器中,你可以从容器中“请求”一个服务,方法是用服务的类名或接口名作为参数的类型提示。想要日志什么东西吗?没有问题:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/ / src /控制器/ ProductController.php名称空间应用程序\控制器;使用Psr\日志\LoggerInterface;使用ob娱乐下载\包\FrameworkBundle\控制器\AbstractController;使用ob娱乐下载\组件\HttpFoundation\响应;使用ob娱乐下载\组件\路由\注释\路线;类ProductController扩展AbstractController{#(路线(' /产品'))公共函数列表(LoggerInterface$日志记录器):响应{$日志记录器->信息(“看,我刚刚使用了一项服务!”);/ /……}}
还有其他服务吗?通过运行来找到答案:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
$PHP bin/控制台调试:自动装配#这只是一个输出的小样本…可自动连接类型=================以下类和接口可以用作类型-当自动装配时的提示:描述一个记录器实例。Psr\Log\LoggerInterface (logger)控制请求生命周期的请求堆栈。ob娱乐下载RouterInterface是所有Router类必须实现的接口。ob娱乐下载Symfony\Component\Routing\RouterInterface (router.default)
当你在控制器方法中使用这些类型提示时自己的服务, ob娱乐下载Symfony将自动向您传递与该类型匹配的服务对象。
在整个文档中,您将看到如何使用容器中的许多不同服务。
提示
实际上有许多容器中有更多的服务,并且每个服务在容器中都有唯一的id,例如request_stack
或router.default
.要获得完整的列表,可以运行PHP bin/控制台调试:容器
.但大多数时候,你不需要担心这个。看到服务容器.看到如何调试服务容器和列表服务.
在容器中创建/配置服务
你也可以组织你的自己的编码到服务中。例如,假设您需要向用户显示一条随机的、愉快的消息。如果你把这个代码放在控制器中,它就不能被重用。相反,你决定创建一个新类:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/ / src /服务/ MessageGenerator.php名称空间应用程序\服务;类MessageGenerator{公共函数getHappyMessage():字符串{$消息= (“你做到了!”您更新了系统!神奇的!”,“这是我今天看到的最酷的更新之一!”,“伟大的工作!继续前进!”,);$指数=(用于$消息);返回$消息[$指数];}}
恭喜你!您已经创建了您的第一个服务类!你可以立即在你的控制器中使用它:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
/ / src /控制器/ ProductController.php使用应用程序\服务\MessageGenerator;使用ob娱乐下载\包\FrameworkBundle\控制器\AbstractController;使用ob娱乐下载\组件\HttpFoundation\响应;使用ob娱乐下载\组件\路由\注释\路线;类ProductController扩展AbstractController{#(路线(“/产品/新”)]公共函数新(MessageGenerator$messageGenerator):响应{//由于类型提示,容器将实例化一个// new MessageGenerator并将它传递给你!/ /……$消息=$messageGenerator->getHappyMessage ();$这->addFlash (“成功”,$消息);/ /……}}
当你要求MessageGenerator
服务时,容器构造一个新的MessageGenerator
对象并返回它(请参阅下面的边栏)。但如果你从不主动要求,那就从来没有构造:节省内存和速度。作为奖励,MessageGenerator
服务只创建一次:每次请求都会返回相同的实例。
将服务限制在特定的Symfony环境中ob娱乐下载
如果使用的是PHP 8.0或更高版本,则可以使用#(时)
PHP属性在某些环境中只将类注册为服务:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
使用ob娱乐下载\组件\DependencyInjection\属性\当;// SomeClass只在"dev"环境中注册#(当(env:“开发”))类SomeClass{/ /……}//你也可以在同一个类中应用多个When属性#(当(env:“开发”))#(当(env:“测试”))类AnotherClass{/ /……}
向服务中注入服务/配置
如果您需要访问日志记录器
内部服务MessageGenerator
?没问题!创建一个__construct ()
方法。美元记录器
参数的LoggerInterface
type-hint。将此设置为new美元记录器
属性并在以后使用它:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
/ / src /服务/ MessageGenerator.php名称空间应用程序\服务;使用Psr\日志\LoggerInterface;类MessageGenerator{私人$日志记录器;公共函数__construct(LoggerInterface$日志记录器){$这->记录器=$日志记录器;}公共函数getHappyMessage():字符串{$这->日志记录器->信息(“马上就能找到快乐的消息了!”);/ /……}}
就是这样!容器会自动知道如何通过日志记录器
实例化MessageGenerator
.它怎么知道要这么做?自动装配.关键在于LoggerInterface
输入提示__construct ()
方法和自动装配:真
配置在services.yaml
.当您输入参数提示时,容器将自动找到匹配的服务。如果不能,您将看到一个明确的异常和有用的建议。
顺便说一下,这种方法添加依赖到您的__construct ()
方法。依赖注入.
你应该如何知道使用LoggerInterface
对于输入提示?你可以阅读文档中的任何你正在使用的功能,或者通过运行以下命令来获得自动连接的类型提示列表:
12 3 4 5 6 7 8 9 10 11 12 13 14
$PHP bin/控制台调试:自动装配#这只是一个输出的小样本…描述记录器实例。Psr\Log\LoggerInterface (monoo .logger)控制请求生命周期的请求堆栈。ob娱乐下载RouterInterface是所有Router类必须实现的接口。ob娱乐下载Symfony\Component\Routing\RouterInterface (router.default)
处理多业务
假设您还希望在每次进行站点更新时向站点管理员发送电子邮件。为此,你创建了一个新类:
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 35
/ / src /服务/ SiteUpdateManager.php名称空间应用程序\服务;使用应用程序\服务\MessageGenerator;使用ob娱乐下载\组件\梅勒\MailerInterface;使用ob娱乐下载\组件\Mime\电子邮件;类SiteUpdateManager{私人$messageGenerator;私人$梅勒;公共函数__construct(MessageGenerator$messageGenerator, MailerInterface$梅勒){$这->messageGenerator =$messageGenerator;$这->梅勒=$梅勒;}公共函数notifyOfSiteUpdate():保龄球{$happyMessage=$这->messageGenerator->getHappyMessage ();$电子邮件= (新电子邮件())->从(“admin@example.com”)->(“manager@example.com”)->主题(“网站刚刚更新!”)->文本(有人刚刚更新了网站。我们告诉他们:“.$happyMessage);$这->梅勒->发送($电子邮件);/ /……返回真正的;}}
这需要MessageGenerator
而且的梅勒
服务。这没有问题,我们通过类型提示他们的类和接口名来询问他们!现在,这个新服务已经可以使用了。例如,在控制器中,您可以键入提示newSiteUpdateManager
类并使用它:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / src /控制器/ SiteController.php名称空间应用程序\控制器;使用应用程序\服务\SiteUpdateManager;/ /……类SiteController扩展AbstractController{公共函数新(SiteUpdateManager$siteUpdateManager):响应{/ /……如果($siteUpdateManager->notifyOfSiteUpdate ()) {$这->addFlash (“成功”,“通知邮件发送成功。”);}/ /……}}
多亏了自动装配和输入提示__construct ()
,容器创建SiteUpdateManager
对象并将正确的参数传递给它。在大多数情况下,这是完美的。
手动连接参数
但是在少数情况下,服务的参数不能自动连接。例如,假设你想让管理电子邮件可配置:
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
// src/Service/SiteUpdateManager.php //…类SiteUpdateManager{//…+ private $adminEmail;-公共函数__construct(MessageGenerator $ MessageGenerator, MailerInterface $mailer)+公共函数__construct(MessageGenerator $ MessageGenerator, MailerInterface $mailer, string $adminEmail){//…+ $this->adminEmail = $adminEmail;}公共函数notifyOfSiteUpdate(): bool{//…$email = (new email()) //…- - > (manager@example.com)+ - - - > ($ this - > adminEmail)/ /……;/ /……}}
如果你做了这个更改并刷新,你会看到一个错误:
不能自动装配服务"App\ service \SiteUpdateManager":方法"__construct()"的参数"$adminEmail"必须有一个类型提示或显式给出一个值。
这很有道理!容器不可能知道你想在这里传递什么值。没问题!在你的配置中,你可以显式地设置这个参数:
- YAML
- XML
- PHP
12 3 4 5 6 7 8 9 10 11 12 13
#配置/ services.yaml服务:#……和以前一样#与之前相同App \:资源:“. . / src /”排除:“. . / src / {DependencyInjection,实体,Kernel.php} '#显式配置服务应用程序服务\ \ SiteUpdateManager:参数:$ adminEmail:“manager@example.com”
多亏了这一点,容器才会通过manager@example.com
到adminEmail美元
的观点__construct
当创建SiteUpdateManager
服务。其他参数仍然是自动连接的。
但是,这不是很脆弱吗?幸运的是,不!如果你重命名adminEmail美元
对其他事物的争论。mainEmail美元
-当你重新加载下一页时,你会得到一个明确的异常(即使该页没有使用此服务)。
服务参数
除了保存服务对象外,容器还保存名为参数.关于Symfony配置的主要文章解释了ob娱乐下载配置参数详细说明,并显示了所有的类型(字符串,布尔,数组,二进制和PHP常量参数)。
但是,还有另一种类型的参数与服务相关。在YAML配置中,以@
被视为服务的ID,而不是常规字符串。在XML配置中,使用type = "服务"
类型作为参数,在PHP配置中使用服务()
功能:
- YAML
- XML
- PHP
1 2 3 4 5 6 7 8 9 10 11
#配置/ services.yaml服务:应用程序服务\ \ MessageGenerator:参数:#这不是一个字符串,而是一个名为“logger”服务的引用。-“@logger”如果字符串参数的值以“@”开头,则需要转义通过添加另一个“@”来#它,这样Symfony就不会认为它是ob娱乐下载一个服务#下面的例子将被解析为字符串'@securepassword'# -“@@securepassword”
使用容器的参数访问器方法来处理容器参数是很简单的:
1 2 3 4 5 6 7 8
//检查是否定义了参数(参数名区分大小写)$容器->hasParameter (“mailer.transport”);//获取参数值$容器->getParameter (“mailer.transport”);//添加一个新参数$容器->setParameter (“mailer.transport”,“发送邮件”);
谨慎
用过的.
符号是aob娱乐下载Symfony公约使参数更易于阅读。参数是平面键值元素,它们不能被组织成嵌套数组
请注意
只能在容器编译之前设置参数,而不能在运行时设置。要了解有关编译容器的更多信息,请参见编译容器.
选择特定的服务
的MessageGenerator
前面创建的服务需要LoggerInterface
论点:
12 3 4 5 6 7 8 9 10 11 12 13 14 15
/ / src /服务/ MessageGenerator.php名称空间应用程序\服务;使用Psr\日志\LoggerInterface;类MessageGenerator{私人$日志记录器;公共函数__construct(LoggerInterface$日志记录器){$这->记录器=$日志记录器;}/ /……}
然而,有多个容器中的服务LoggerInterface
,例如日志记录器
,monolog.logger.request
,monolog.logger.php
等。容器如何知道使用哪一个?
在这些情况下,容器通常配置为自动选择其中一个服务日志记录器
在这种情况下(阅读更多关于为什么在自动定义服务依赖关系(自动装配)).但是,你可以控制它并传入一个不同的记录器:
- YAML
- XML
- PHP
1 2 3 4 5 6 7 8 9 10 11
#配置/ services.yaml服务:#……代码和之前一样#显式配置服务应用程序服务\ \ MessageGenerator:参数:“@”符号很重要:它告诉容器#你想传递id为' monoog .logger.request'的服务#而不仅仅是*string* ' monoo .logger.request'日志:美元“@monolog.logger.request”
这告诉容器美元记录器
参数__construct
是否应该使用id为的服务monolog.logger.request
.
的完整列表所有容器中可能的服务,运行:
1
$PHP bin/控制台调试:容器
根据名称或类型绑定参数
你也可以使用绑定
关键字按名称或类型绑定特定参数:
- YAML
- XML
- PHP
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21 22
#配置/ services.yaml服务:_defaults:绑定:#将此值传递给任何服务的任何$adminEmail参数在这个文件中定义的#(包括控制器参数)$ adminEmail:“manager@example.com”#将此服务传递给任意$requestLogger参数该文件中定义的# service$ requestLogger:“@monolog.logger.request”#为任何LoggerInterface类型传递此服务该文件中定义的# servicePsr \ Log \ LoggerInterface:“@monolog.logger.request”#可选地,您可以定义参数的名称和类型来匹配字符串$ adminEmail:“manager@example.com”Psr \ \ LoggerInterface日志$ requestLogger:“@monolog.logger.request”可迭代的规则:美元tagged_iterator !app.foo.rule#……
通过把绑定
关键在_defaults
的值任何理由任何该文件中定义的服务!你可以通过名称绑定参数(例如:adminEmail美元
),按类型(例如:Psr \ \ LoggerInterface日志
)或两者兼有(例如:Psr \ Log \ LoggerInterface requestLogger美元
).
的绑定
配置也可以应用于特定的服务,或者在一次加载多个服务时(例如:服务容器).
自动连线选项
以上,services.yaml
文件自动装配:真
在_defaults
节,使其应用于该文件中定义的所有服务。的类型中输入提示参数__construct ()
方法和容器将自动向您传递正确的参数。这整个条目都是围绕自动装配编写的。
有关自动装配的更多详细信息,请查看自动定义服务依赖关系(自动装配).
autoconfigure选项
以上,services.yaml
文件可以使用autoconfigure:真
在_defaults
节,使其应用于该文件中定义的所有服务。通过此设置,容器将自动将某些配置应用到您的服务,基于您的服务类.这是最常用的自动标记你的服务。
例如,要创建Twig扩展,您需要创建一个类,将其注册为服务,以及标签它与twig.extension
.
但是,与可以使用autoconfigure:真
,你不需要标签。事实上,如果你在用默认的服务。yaml配置,你不需要这样做任何东西:服务将自动加载。然后,可以使用autoconfigure
将添加twig.extension
标签为你,因为你的类实现了树枝\ \ ExtensionInterface延伸
.感谢自动装配
,您甚至可以添加构造函数参数而不需要任何配置。
检测服务定义
的线头:容器
命令检查注入到服务中的参数是否与它们的类型声明匹配。在将应用程序部署到生产环境之前运行它是很有用的(例如在持续集成服务器中):
1
$PHP bin/console lint:容器
在编译容器时检查所有服务参数的类型可能会损害性能。这就是为什么这种类型检查是在编译器通过被称为CheckTypeDeclarationsPass
默认禁用,仅在执行线头:容器
命令。如果您不介意性能损失,可以在应用程序中启用编译器传递。
公共服务与私人服务
默认情况下,定义的每个服务都是私有的。当服务为私有时,不能直接从使用的容器访问它集装箱- > get ()
.作为最佳实践,您应该只创建私人服务,您应该使用依赖项注入来获取服务,而不是使用集装箱- > get ()
.
如果您需要懒惰地获取服务,而不是使用公共服务,您应该考虑使用服务定位器.
但是,如果你做需要将服务设为公共,则重写公共
设置:
- YAML
- XML
- PHP
1 2 3 4 5 6 7
#配置/ services.yaml服务:#……代码和之前一样#显式配置服务应用程序服务\ \ PublicService:公众:真正的
同时使用资源导入多个服务
方法可以同时导入多个服务资源
关键。例如,默认的Symfony配置包含:ob娱乐下载
- YAML
- XML
- PHP
1 2 3 4 5 6 7 8 9
#配置/ services.yaml服务:#……和以前一样#使src/中的类可用作服务#这将为每个类创建一个服务,其id为全限定类名App \:资源:“. . / src /”排除:“. . / src / {DependencyInjection,实体,Kernel.php} '
提示
的值资源
而且排除
选项可以是任何有效的一团模式.
这可用于快速将许多类作为服务提供,并应用一些默认配置。的id
每个服务的全限定类名。你可以通过下面的id(类名)覆盖任何被导入的服务服务容器).如果你重写一个服务,没有一个选项(例如。公共
)从导入继承(但覆盖的服务做仍然继承_defaults
).
你也可以排除
特定的路径。方法中的性能略有提高,但这是可选的dev
环境:排除的路径不会被跟踪,因此修改它们不会导致容器被重新构建。
请注意
等等,这是不是意味着每一个类src /
是否注册为服务?甚至是模型课程?事实上,没有。只要您将导入的服务保持为私人,所有班级src /
这是不显式地作为服务使用会自动从最终容器中删除。实际上,导入意味着所有的类都是“可用的”使用作为服务,而不需要手动配置。
使用相同命名空间的多个服务定义
如果你使用YAML配置格式定义服务,PHP命名空间被用作每个配置的键,所以你不能在同一个命名空间下为类定义不同的服务配置:
- YAML
- XML
- PHP
1 2 3 4 5
#配置/ services.yaml服务:应用程序域\ \:资源:“. . / src /域/ *”#……
为了拥有多个定义,请添加名称空间
选项,并使用任何唯一的字符串作为每个服务配置的键:
1 2 3 4 5 6 7 8 9 10 11
#配置/ services.yaml服务:command_handlers:名称空间:应用程序域\ \资源:“. . / src /域/ * / CommandHandler '标签:(command_handler)event_subscribers:名称空间:应用程序域\ \资源:“. . / src /域/ * / EventSubscriber '标签:(event_subscriber)
显式配置服务和参数
自动加载服务而且自动装配是可选的。即使您使用它们,也可能在某些情况下希望手动连接服务。例如,假设您想要注册2为SiteUpdateManager
类-每个类都有不同的管理电子邮件。在这种情况下,每个服务都需要有一个唯一的服务id:
- YAML
- XML
- PHP
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
#配置/ services.yaml服务:#……这是服务的idsite_update_manager.superadmin:类:应用\ \ SiteUpdateManager服务#你仍然可以使用自动装配:我们只是想展示它看起来像没有自动装配:假#手动连接所有参数参数:-“@App \ \ MessageGenerator服务”-“@mailer”-“superadmin@example.com”site_update_manager.normal_users:类:应用\ \ SiteUpdateManager服务自动装配:假参数:-“@App \ \ MessageGenerator服务”-“@mailer”-“contact@example.com”#创建一个别名,这样-默认情况下-如果你输入-hint SiteUpdateManager# site_update_manager将使用超级管理员应用程序服务\ \ SiteUpdateManager:“@site_update_manager.superadmin”
在这种情况下,两个注册服务:site_update_manager.superadmin
而且site_update_manager.normal_users
.多亏了别名,如果你输入提示SiteUpdateManager
第一个(site_update_manager.superadmin
)将获通过。如果你想通过第二关,你就得这么做手动连接服务.
谨慎
如果有的话不创建别名和从src/加载所有服务,然后三个服务已经创建(自动服务+您的两个服务),自动加载的服务将在您输入-hint时被传递(默认情况下)SiteUpdateManager
.这就是为什么创建别名是一个好主意。