服务容器

编辑该页面

警告:你浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 5.1,不再维护。

这个页面的更新版本Symf欧宝娱乐app下载地址ony 6.2(当前的稳定版本)。

服务容器

截屏视频

你喜欢视频教程?检查欧宝娱乐app下载地址Symfony基本面视频系列

您的应用程序完整的有用的对象:一个“梅勒”对象可能会帮助您发送电子邮件,而另一个对象可能会帮助你保存到数据库中。几乎一切应用程序“确实”实际上是由这些对象之一。每一次你安装一个新的包,你获得更多!

在Sy欧宝娱乐app下载地址mfony中,这些被称为有用的对象服务和每个服务的生活在一个非常特殊的对象称为服务容器。容器允许你集中对象的构造方式。它使你的生活更容易,促进一个强壮的架构和超级快!

获取和使用服务

当你开始一个Symfony应用,你的容器欧宝娱乐app下载地址已经包含了许多服务。这些都是像工具:等你来利用它们。在控制器中,可以“问”服务的容器类型提示一个论点与服务的类或接口的名称。想要日志什么东西吗?没有问题:

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

提供哪些服务?发现通过运行:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
美元php bin /控制台调试:自动装配#这是*小*样本的输出……描述了一个logger实例。Psr \ Log \ LoggerInterface (monolog.logger)请求堆栈控制请求的生命周期。欧宝娱乐app下载地址Symfony \组件\ HttpFoundation \ RequestStack (request_stack)接口会话。欧宝娱乐app下载地址Symfony组件\ \ HttpFoundation \会议\ SessionInterface(会话)RouterInterface所有路由器类必须实现的接口。欧宝娱乐app下载地址Symfony \组件\ \ RouterInterface路由(router.default) […]

当你使用这些类型提示你的控制器方法或在你自己的服务,S欧宝娱乐app下载地址ymfony会自动通过你的服务对象匹配类型。

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

提示

实际上有许多更多的服务容器,容器中的每个服务都有一个惟一的id,会话router.default。对于一个完整的列表,您可以运行php bin /控制台调试:容器。但是大多数时候,你不需要担心这个。看到服务容器。看到如何调试服务容器&列表服务

创建/配置服务的容器

你也可以组织你的自己的代码服务。例如,假设您需要向您的用户显示一个随机的,快乐的消息。如果你把这段代码在你的控制器,它不能被重用。相反,您决定创建一个新类:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/ / src /服务/ MessageGenerator.php名称空间应用程序\服务;MessageGenerator{公共函数getHappyMessage():字符串{美元消息= (“你做到了!你更新系统!神奇的!”,这是最酷的更新我\ ' ve整天看到!”,“伟大的工作!继续前进!”,);美元指数=(用于美元消息);返回美元消息(美元指数];}}

恭喜你!您创建了您的第一个服务类!你可以用它立即内部控制器:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src /控制器/ ProductController.php使用应用程序\服务\MessageGenerator;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;/ * * *@Route(“/产品/新”)* /公共函数(MessageGenerator美元messageGenerator):响应{/ /由于type-hint,容器将实例化一个/ /新MessageGenerator并将其传递给你!/ /……美元消息=美元messageGenerator- >getHappyMessage ();美元- >addFlash (“成功”,美元消息);/ /……}

当你问的MessageGenerator服务,构造一个新的容器MessageGenerator下面的对象,并返回该对象(参见侧栏)。但是如果你从来没有要求服务,它是从来没有构造:节省内存和速度。作为奖励,MessageGenerator只有创建服务一次:返回相同的实例每次询问。

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

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

提示

的值资源排除可以是任何有效的选项一团模式。的值排除一团模式的选择也可以是一个数组。

由于这种配置,您可以自动使用任何类的src /目录服务,无需手动配置。之后,您将了解更多关于这个服务容器

如果你想手动线服务,这是完全可能的:明白了服务容器

注入服务/配置服务

如果你需要访问日志记录器从内部服务MessageGenerator吗?没问题!创建一个__construct ()方法美元记录器论点的LoggerInterfacetype-hint。设置一个新的美元记录器财产和使用它后:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/ / src /服务/ MessageGenerator.php名称空间应用程序\服务;使用Psr\日志\LoggerInterface;MessageGenerator{私人美元日志记录器;公共函数__construct(LoggerInterface美元日志记录器){美元- >记录器=美元日志记录器;}公共函数getHappyMessage():字符串{美元- >日志记录器- >信息(要找到一个快乐的消息!”);/ /……}}

就是这样!容器将自动知道通过日志记录器服务实例化时MessageGenerator。它是如何知道要做到这一点吗?自动装配。的关键是LoggerInterfacetype-hint在你__construct ()方法和自动装配:真配置在services.yaml。type-hint论点时,容器会自动找到匹配服务。如果不能,你会看到一个明确的例外,一个有用的建议。

顺便说一下,这将依赖项添加到您的方法__construct ()方法被调用依赖注入

你应该知道如何使用LoggerInterfacetype-hint吗?你可以读任何特性的文档你使用,或得到一个autowireable列表类型提示通过运行:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
美元php bin /控制台调试:自动装配#这是*小*样本的输出……描述了一个logger实例。Psr \ Log \ LoggerInterface (monolog.logger)请求堆栈控制请求的生命周期。欧宝娱乐app下载地址Symfony \组件\ HttpFoundation \ RequestStack (request_stack)接口会话。欧宝娱乐app下载地址Symfony组件\ \ HttpFoundation \会议\ SessionInterface(会话)RouterInterface所有路由器类必须实现的接口。欧宝娱乐app下载地址Symfony \组件\ \ RouterInterface路由(router.default) […]

处理多个服务

假设你还想邮件站点管理员每次网站更新。要做到这一点,你创建一个新类:

1 2 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 32 33 34 35
/ / src /服务/ SiteUpdateManager.php名称空间应用程序\服务;使用应用程序\服务\MessageGenerator;使用欧宝娱乐app下载地址\组件\梅勒\MailerInterface;使用欧宝娱乐app下载地址\组件\Mime\电子邮件;SiteUpdateManager{私人美元messageGenerator;私人美元梅勒;公共函数__construct(MessageGenerator美元messageGenerator,MailerInterface美元梅勒){美元- >messageGenerator =美元messageGenerator;美元- >梅勒=美元梅勒;}公共函数notifyOfSiteUpdate():bool{美元happyMessage=美元- >messageGenerator- >getHappyMessage ();美元电子邮件= (电子邮件())- >从(“admin@example.com”)- >(“manager@example.com”)- >主题(“网站更新正好!”)- >文本(“某人只是网站更新。我们告诉他们:‘美元happyMessage);美元- >梅勒- >发送(美元电子邮件);/ /……返回真正的;}}

这需要MessageGenerator梅勒服务。没问题,我们问他们通过类型提示类和接口的名字!现在,可以使用这个新服务。在一个控制器,例如,你可以type-hint新SiteUpdateManager类并使用它:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ / src /控制器/ SiteController.php名称空间应用程序\控制器;/ /……使用应用程序\服务\SiteUpdateManager;公共函数(SiteUpdateManager美元siteUpdateManager){/ /……如果(美元siteUpdateManager- >notifyOfSiteUpdate ()) {美元- >addFlash (“成功”,通知邮件发送成功。);}/ /……}

由于自动装配和类型提示__construct (),容器创建SiteUpdateManager对象并将其传递正确的论点。在大多数情况下,这是完美的。

手动布线参数

但有一些情况下,当一个参数不能autowired的服务。例如,假设您想要管理电子邮件可配置:

1 2 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 /服务/ SiteUpdateManager.php/ /……类SiteUpdateManager {/ /……+私人adminEmail美元;公共函数__construct (MessageGenerator MessageGenerator美元,美元MailerInterface梅勒)+公共职能__construct (MessageGenerator MessageGenerator美元,美元MailerInterface梅勒,字符串adminEmail美元){/ /……+ $ this - > adminEmail = $ adminEmail;}公共职能notifyOfSiteUpdate (): bool {/ /……电子邮件=美元(新邮件())/ /……- - > (manager@example.com)+ - > ($ this - > adminEmail)/ /……;/ /……}}

如果你把这种变化和更新,您将看到一个错误:

不能自动装配服务“AppServiceSiteUpdateManager”:参数“adminEmail美元”的方法“__construct()”必须有一个明确type-hint或被赋予一个值。

这是有道理的!没有办法,你想通过这里容器知道价值。没问题!在您的配置,您可以显式地设置这个参数:

  • YAML
  • XML
  • PHP
1 2 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服务。其他参数仍将autowired的。

但是,这不是脆弱吗?幸运的是,不!如果您重命名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”#如果字符串参数的值从‘@’开始,你需要逃跑#通过添加另一个‘@’所以Symfony并不认为这是一个服务欧宝娱乐app下载地址#下面的例子将解析的字符串“@securepassword”#——“@@securepassword”

处理容器参数直接使用容器的访问器方法参数:

1 2 3 4 5 6 7 8
/ /检查如果定义一个参数(参数名称区分大小写)美元容器- >hasParameter (“mailer.transport”);/ /得到一个参数的值美元容器- >getParameter (“mailer.transport”);/ /添加一个新参数美元容器- >setParameter (“mailer.transport”,“发送邮件”);

谨慎

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

请注意

你只能设置一个参数容器编译之前,而不是在运行时。更多地了解编译容器编译的容器

选择一个特定的服务

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

1 2 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是‘monolog.logger.request’,#而不仅仅是*字符串*“monolog.logger.request”日志:美元“@monolog.logger.request”

这告诉容器美元记录器参数__construct应该使用服务id是谁的monolog.logger.request

的完整列表所有可能的服务容器,运行:

1
美元php bin /控制台调试:容器

通过名称或类型绑定参数

您还可以使用绑定关键字绑定特定参数的名字或类型:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
#配置/ services.yaml服务:_defaults:绑定:# adminEmail美元将该值传递给任何理由任何服务#在这个文件中定义的(包括控制器参数)$ adminEmail:“manager@example.com”#该服务传递给任何requestLogger美元的理由#在这个文件中定义的服务$ requestLogger:“@monolog.logger.request”#通过这个服务对于任何LoggerInterface type-hint任何#在这个文件中定义的服务Psr \ Log \ LoggerInterface:“@monolog.logger.request”#选择您可以定义的参数的名称和类型匹配字符串$ adminEmail:“manager@example.com”Psr \ \ LoggerInterface日志$ requestLogger:“@monolog.logger.request”iterable规则:美元tagged_iterator !app.foo.rule#……

通过将绑定关键在_defaults,您可以指定的值任何理由任何在这个文件中定义的服务!你可以绑定参数的名字(如。adminEmail美元),按类型(如。Psr \ \ LoggerInterface日志)或(如。Psr \ Log \ LoggerInterface requestLogger美元)。

绑定配置还可以应用于特定的服务或装船时很多服务(即。服务容器)。

自动装配选项

以上,services.yaml文件自动装配:真_defaults节,适用于所有在该文件中定义的服务。这个设置,你可以type-hint参数__construct ()方法,你的服务和容器会自动将你正确的参数。整个条目已经写在自动装配。

关于自动装配的更多细节,请查看自动定义服务依赖关系(自动装配)

可以使用autoconfigure选项

以上,services.yaml文件可以使用autoconfigure:真_defaults节,适用于所有在该文件中定义的服务。经过这样设置后,容器会自动应用某些配置到你的服务,根据您的服务的。这主要是用于自动标记你的服务。

例如,创建一个分支扩展,您需要创建一个类,它作为一个服务注册,和标签它与twig.extension

但是,与可以使用autoconfigure:真,你不需要标签。事实上,如果你使用默认的服务。yaml配置,你不需要做任何东西:服务将自动加载。然后,可以使用autoconfigure将添加twig.extension标签你,因为你的类实现树枝\ \ ExtensionInterface延伸。感谢自动装配,你甚至可以添加构造函数参数没有任何配置。

产品毛羽服务定义

线头:容器命令检查参数注入服务匹配他们的类型声明。是有用的运行将应用程序部署到生产环境之前(例如你的持续集成服务器):

1
美元php bin /控制台线头:容器

检查所有服务的类型参数时容器编译会损害性能。这就是为什么这类型检查的实现编译器通过被称为CheckTypeDeclarationsPass默认情况下是禁用和启用只有当执行线头:容器命令。如果你不介意性能损失,使编译器通过在您的应用程序。

公共和私人服务

每个服务定义默认是私人的。当一个服务是私人的,你不能访问它直接从容器使用$容器- > get ()。作为一项最佳实践,您应该只创建私人服务,您应该使用依赖注入而不是使用获取服务$容器- > get ()

如果您需要获取服务延迟,而不是使用公共服务你应该考虑使用服务定位器

但是,如果你需要公开一个服务,覆盖公共设置:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7
#配置/ services.yaml服务:#……同样的代码#显式配置服务应用程序服务\ \ PublicService:公众:真正的

5.1

Symfon欧宝娱乐app下载地址y 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 3欧宝娱乐app下载地址.3之前,所有服务和(通常)参数显式配置:这是不可能的负载自动服务自动装配不常见得多。

这两种功能是可选的。即使你使用它们,可能会有一些情况下,你想要手动线服务。例如,假设你想注册2服务SiteUpdateManager类——每一个都有不同的管理邮件。在这种情况下,每个需要有一个独特的服务id:

  • YAML
  • XML
  • PHP
1 2 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”#创建一个别名,因此,默认情况下,如果你type-hint SiteUpdateManager,# site_update_manager。超级管理员将使用几个应用程序服务\ \ SiteUpdateManager:“@site_update_manager.superadmin”

在这种情况下,两个服务注册:site_update_manager.superadminsite_update_manager.normal_users。由于别名,如果你type-hintSiteUpdateManager第一个(site_update_manager.superadmin)将被通过。如果你想通过第二个,你需要手动线服务

谨慎

如果你做创建别名和从src /加载所有服务,然后三个服务创建(自动服务+你的两个服务)和自动加载服务将被传递——默认情况下,当你type-hintSiteUpdateManager。这就是为什么创建别名是一个好主意。

这项工作,包括代码示例,许可下Creative Commons冲锋队3.0许可证。