服务容器

编辑本页

警告:您正在浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 5.3,现已不再维护。

本页的更新版本用于Sy欧宝娱乐app下载地址mfony 6.2(当前稳定版本)。

服务容器

截屏视频

你更喜欢视频教程吗?请查看欧宝娱乐app下载地址Symfony Fundamentals系列截图

您的申请是完整的一个“Mailer”对象可以帮助你发送电子邮件,而另一个对象可以帮助你将东西保存到数据库中。几乎一切你的应用“做”实际上是由这些对象之一完成的。每次你安装一个新的包,你就可以访问更多!

在Sy欧宝娱乐app下载地址mfony中,调用这些有用的对象服务每个服务都存在于一个非常特殊的对象中服务容器.容器允许您集中构造对象的方式。它使您的生活更轻松,促进强大的架构和超级快!

获取和使用服务

当你启动Symfony应用时,你的容器欧宝娱乐app下载地址已经包含许多服务。这些就像工具:等着你去利用它们。在你的控制器中,你可以从容器中“请求”一个服务,方法是用服务的类名或接口名作为参数的类型提示。想要日志什么东西吗?没有问题:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / src /控制器/ ProductController.php名称空间应用程序控制器使用Psr日志LoggerInterface使用欧宝娱乐app下载地址组件HttpFoundation响应使用欧宝娱乐app下载地址组件路由注释路线ProductController/ * * *@Route(“产品”)* /公共函数列表(LoggerInterface日志记录器响应日志记录器->信息(“看,我刚刚使用了一项服务!”);/ /……}}

还有其他服务吗?通过运行来找到答案:

12 3 4 5 6 7 8 9 10 11 12 13 14
PHP bin/控制台调试:自动装配#这只是一个输出的小样本…描述记录器实例。Psr\Log\LoggerInterface (monoo .logger)控制请求生命周期的请求堆栈。欧宝娱乐app下载地址RouterInterface是所有Router类必须实现的接口。欧宝娱乐app下载地址Symfony\Component\Routing\RouterInterface (router.default)

当你在控制器方法中使用这些类型提示时自己的服务, 欧宝娱乐app下载地址Symfony将自动向您传递与该类型匹配的服务对象。

在整个文档中,您将看到如何使用容器中的许多不同服务。

提示

实际上有许多容器中有更多的服务,并且每个服务在容器中都有唯一的id,例如request_stackrouter.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 15 16 17 18
/ / src /控制器/ ProductController.php使用应用程序服务MessageGenerator使用欧宝娱乐app下载地址组件HttpFoundation响应使用欧宝娱乐app下载地址组件路由注释路线/ * * *@Route(“/产品/新”)* /公共函数(MessageGeneratormessageGenerator响应//由于类型提示,容器将实例化一个// new MessageGenerator并将它传递给你!/ /……消息messageGenerator->getHappyMessage ();->addFlash (“成功”消息);/ /……

当你要求MessageGenerator服务时,容器构造一个新的MessageGenerator对象并返回它(请参阅下面的边栏)。但如果你从不主动要求,那就从来没有构造:节省内存和速度。作为奖励,MessageGenerator服务只创建一次:每次请求都会返回相同的实例。

文档假设欧宝体育电话您正在使用以下服务配置,这是新项目的默认配置:

  • YAML
  • XML
  • PHP
12 3 4 5 6 7 8 9 10 11 12 13 14
#配置/ services.yaml服务:# this*文件中服务的默认配置_defaults:自动装配:真正的#自动在服务中注入依赖项。可以使用autoconfigure:真正的#自动注册你的服务为命令,事件订阅者等。#使src/中的类可用作服务#这将为每个类创建一个服务,其id为全限定类名App \:资源:“. . / src / *”排除:“. . / src / {DependencyInjection、实体、测试Kernel.php}’#……

提示

的值资源而且排除选项可以是任何有效的一团模式.的值排除Option也可以是glob模式的数组。

类中的任何类都可以自动使用src /目录即服务,无需手动配置。稍后,您将在服务容器

如果您更喜欢手动连接您的服务,这是完全可能的:参见服务容器

将服务限制在特定的Symfony环境中欧宝娱乐app下载地址

5.3

#(时)属性是在Symfony 5.3中引入的。欧宝娱乐app下载地址

如果使用的是PHP 8.0或更高版本,则可以使用#(时)PHP属性在某些环境中只将类注册为服务:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
使用欧宝娱乐app下载地址组件DependencyInjection属性// SomeClass只在"dev"环境中注册#(当(env:“开发”))SomeClass/ /……//你也可以在同一个类中应用多个When属性#(当(env:“开发”))#(当(env:“测试”))AnotherClass/ /……

向服务中注入服务/配置

如果您需要访问日志记录器内部服务MessageGenerator?没问题!创建一个__construct ()方法。美元记录器参数的LoggerInterfacetype-hint。将此设置为new美元记录器属性并在以后使用它:

12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
/ / src /服务/ MessageGenerator.php名称空间应用程序服务使用Psr日志LoggerInterfaceMessageGenerator私人日志记录器公共函数__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)控制请求生命周期的请求堆栈。欧宝娱乐app下载地址RouterInterface是所有Router类必须实现的接口。欧宝娱乐app下载地址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使用欧宝娱乐app下载地址组件梅勒MailerInterface使用欧宝娱乐app下载地址组件Mime电子邮件SiteUpdateManager私人messageGenerator私人梅勒公共函数__construct(MessageGeneratormessageGenerator, 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公共函数(SiteUpdateManagersiteUpdateManager/ /……如果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.comadminEmail美元的观点__construct当创建SiteUpdateManager服务。其他参数仍然是自动连接的。

但是,这不是很脆弱吗?幸运的是,不!如果你重命名adminEmail美元对其他事物的争论。mainEmail美元-当你重新加载下一页时,你会得到一个明确的异常(即使该页没有使用此服务)。

服务参数

除了保存服务对象外,容器还保存名为参数.关于Symfony配置的主要文章解释了欧宝娱乐app下载地址配置参数详细说明,并显示了所有的类型(字符串,布尔,数组,二进制和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就不会认为它是欧宝娱乐app下载地址一个服务#下面的例子将被解析为字符串'@securepassword'# -“@@securepassword”

使用容器的参数访问器方法来处理容器参数是很简单的:

1 2 3 4 5 6 7 8
//检查是否定义了参数(参数名区分大小写)容器->hasParameter (“mailer.transport”);//获取参数值容器->getParameter (“mailer.transport”);//添加一个新参数容器->setParameter (“mailer.transport”“发送邮件”);

谨慎

用过的符号是a欧宝娱乐app下载地址Symfony公约使参数更易于阅读。参数是平面键值元素,它们不能被组织成嵌套数组

请注意

只能在容器编译之前设置参数,而不能在运行时设置。要了解有关编译容器的更多信息,请参见编译容器

选择特定的服务

MessageGenerator前面创建的服务需要LoggerInterface论点:

12 3 4 5 6 7 8 9 10 11 12 13 14 15
/ / src /服务/ MessageGenerator.php名称空间应用程序服务使用Psr日志LoggerInterfaceMessageGenerator私人日志记录器公共函数__construct(LoggerInterface日志记录器->记录器=日志记录器;}/ /……

然而,有多个容器中的服务LoggerInterface,例如日志记录器monolog.logger.requestmonolog.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:公众:真正的

5.1

从Symfo欧宝娱乐app下载地址ny 5.1开始,不再可以通过类型提示自动装配服务容器Psr \ \ ContainerInterface容器

同时使用资源导入多个服务

方法可以同时导入多个服务资源关键。例如,默认的Symfony配置包含:欧宝娱乐app下载地址

  • 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)

显式配置服务和参数

在Symfony 欧宝娱乐app下载地址3.3之前,所有服务和(通常)参数都是显式配置的:这是不可能的自动加载服务而且自动装配不太常见。

这两个特性都是可选的。即使您使用它们,也可能在某些情况下希望手动连接服务。例如,假设您想要注册2SiteUpdateManager类-每个类都有不同的管理电子邮件。在这种情况下,每个服务都需要有一个唯一的服务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.这就是为什么创建别名是一个好主意。

此工作,包括代码示例,是根据创作共用BY-SA 3.0许可证。
欧宝娱乐app下载地址Symfony 5.3支持通过JoliCode