服务容器

编辑本页

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

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

服务容器

现代PHP应用程序充满了对象。一个对象可能有助于电子邮件消息的传递,而另一个对象可能允许您将信息持久化到数据库中。在您的应用程序中,您可以创建一个对象来管理产品库存,或者另一个对象来处理来自第三方API的数据。关键是现代应用程序要做很多事情,并被组织成处理每个任务的许多对象。

本章是关于Symfony中一个特殊的PHP对象,它可以帮助你实例化、组织和检索应用程序的许多欧宝娱乐app下载地址对象。这个对象称为服务容器,它允许您在应用程序中标准化和集中构造对象的方式。容器使您的生活变得更轻松,速度超快,并强调了促进可重用和解耦代码的体系结构。由于所有Symfony核心类都欧宝娱乐app下载地址使用容器,您将学习如何扩展、配置和使用Symfony中的任何对象。在很大程度上,服务容器是Symfony的速度和可扩展性的最大贡献者。欧宝娱乐app下载地址

最后,配置和使用服务容器非常简单。在本章结束时,您将能够通过容器创建自己的对象,并从任何第三方包中定制对象。您将开始编写更具可重用性、可测试性和去耦性的代码,这仅仅是因为服务容器使编写好代码变得如此容易。

提示

如果你想在读完本章后了解更多,请查看DependencyInjection组件文档欧宝体育电话

什么是服务?

简单地说,a服务是任何执行某种“全局”任务的PHP对象。它是计算机科学中一个有目的的通用名称,用于描述为特定目的(例如发送电子邮件)而创建的对象。只要您需要每个服务提供的特定功能,就会在整个应用程序中使用它。创建服务不需要做任何特殊的事情:只需用一些代码编写一个PHP类来完成特定的任务。祝贺您,您已经创建了一个服务!

请注意

通常,如果PHP对象在应用程序中被全局使用,那么它就是一个服务。一个单一的梅勒服务在全球范围内用于发送电子邮件消息,而许多消息它传递的对象是服务。同样,一个产品对象不是服务,而是持久存在的对象产品对象。一个服务。

那有什么大不了的?考虑“服务”的好处是,您可以开始考虑将应用程序中的每个功能片段分离为一系列服务。由于每个服务只做一项工作,因此您可以轻松地访问每个服务并在需要时使用其功能。每个服务也可以更容易地测试和配置,因为它与应用程序中的其他功能是分开的。这个想法叫做面向服务的体系结构并不是Symfony甚至PHP所独有的。欧宝娱乐app下载地址围绕一组独立的服务类来构造应用程序是一种众所周知且值得信赖的面向对象的最佳实践。在几乎任何语言中,这些技能都是成为优秀开发人员的关键。

什么是服务容器?

一个服务容器(或依赖注入容器)是一个简单的PHP对象,用于管理服务(即对象)的实例化。

例如,假设您有一个传递电子邮件消息的简单PHP类。如果没有服务容器,你必须在需要的时候手动创建对象:

1 2 3 4
使用AppBundle梅勒梅勒梅勒(“发送邮件”);梅勒->发送(“ryan@example.com”,……);

这很简单。假想的梅勒类允许您配置用于传递电子邮件消息的方法(例如。sendmailsmtp等等)。但是如果您想在其他地方使用邮件服务呢?您当然不希望重复邮件器配置每一个你需要使用的时间梅勒对象。如果您需要更改运输sendmailsmtp在应用程序的任何地方?你需要找到你创造的每一个地方梅勒服务并改变它。

在容器中创建/配置服务

更好的答案是让服务容器创建梅勒对象。为了成功,你必须容器如何创建梅勒服务。这是通过配置来完成的,可以在YAML、XML或PHP中指定:

  • YAML
  • XML
  • PHP
1 2 3 4 5
# app / config / services.yml服务:app.mailer:类:AppBundle \梅勒参数:(发送邮件)

请注意

当Symf欧宝娱乐app下载地址ony初始化时,它使用应用程序配置(应用程序/配置/ config.yml默认情况下)。所加载的确切文件由AppKernel: registerContainerConfiguration ()方法,该方法加载特定于环境的配置文件(例如:config_dev.ymldev环境或config_prod.yml刺激).

的实例AppBundle \梅勒类现在可以通过服务容器使用。容器可以在任何传统的Symfony控制器中使用,您可以通过欧宝娱乐app下载地址get ()快捷方法:

1 2 3 4 5 6 7 8 9 10 11
HelloController扩展控制器/ /……公共函数sendEmailAction()/ /……梅勒->get (“app.mailer”);梅勒->发送(“ryan@foobar.net”,……);}}

当你要求app.mailer服务,则容器构造对象并返回该对象。这是使用服务容器的另一个主要优点。即,服务是从来没有一直构建到需要为止。如果定义了服务,但从未在请求中使用它,则永远不会创建该服务。这样可以节省内存并提高应用程序的速度。这也意味着定义大量服务对性能的影响很小或没有影响。永远不会构造从未使用过的服务。

作为奖励,梅勒服务只创建一次,并且每次请求服务时返回相同的实例。这几乎总是您需要的行为(它更加灵活和强大),但稍后您将了解如何配置在“如何使用scope“食谱文章。

请注意

在本例中,控制器扩展了Symfony的基本控制器,它允许您访问服务容器本身。欧宝娱乐app下载地址然后可以使用得到方法定位和检索app.mailer服务容器中的服务。您还可以定义您的控制器即服务.这有点高级,但不是必需的,但它允许您只向控制器中注入所需的服务。

服务参数

通过容器创建新服务(即对象)非常简单。参数使服务定义更加有组织和灵活:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8
# app / config / services.yml参数:app.mailer.transport:sendmail服务:app.mailer:类:AppBundle \梅勒参数:[' % app.mailer.transport % ')

最终的结果和以前一模一样,只是不同而已如何您定义了服务。通过附上app.mailer.transport百分比字符串()符号,容器就知道要寻找具有该名称的形参。在构建容器时,它会查找每个参数的值,并在服务定义中使用它。

请注意

如果要使用以。开头的字符串@sign作为YAML文件中的一个参数值(例如一个非常安全的邮件密码),你需要通过添加另一个来转义它@符号(这只适用于YAML格式):

1 2 3 4
# app / config / parameters.yml参数:#这将被解析为字符串'@securepass'mailer_password:“@@securepass”

请注意

形参或参数中的百分号作为字符串的一部分,必须用另一个百分号进行转义:

1
<论点类型“字符串”>http://欧宝娱乐app下载地址www.oldmanjams.com/?foo=%%s,酒吧= % % d论点>

谨慎

你可能会收到ScopeWideningInjectionException当通过请求服务作为参数。要更好地理解这个问题并学习如何解决它,请参阅烹饪书文章如何使用scope

参数的目的是将信息提供给服务。当然,在不使用任何参数的情况下定义服务并没有什么问题。然而,参数有几个优点:

  • 分离和组织所有服务“选项”下的单一参数关键的;
  • 参数值可用于多个服务定义;
  • 在包中创建服务时(稍后将创建),使用参数可以方便地在应用程序中自定义服务。

使用或不使用参数的选择由您决定。高质量的第三方捆绑包总是使用参数可以使存储在容器中的服务更具可配置性。然而,对于应用程序中的服务,您可能不需要参数的灵活性。

数组参数

参数也可以包含数组值。看到参数简介

导入其他容器配置资源

提示

在本节中,业务配置文件被称为资源.这是为了强调这样一个事实:虽然大多数配置资源都是文件(例如YAML、XML、PHP),但Symfony非常灵活,可以从任何地方加载配置(例如数据库,甚至通过外部web服务)。欧宝娱乐app下载地址

服务容器是使用单一配置资源(应用程序/配置/ config.yml默认情况下)。所有其他服务配置(包括核心Symfony和第三方包配置)必须以某种方式从该文件中导入。欧宝娱乐app下载地址这为应用程序中的服务提供了绝对的灵活性。

外部服务配置可以通过两种不同的方式导入。第一个方法(通常用于从创建的包导入容器配置)是通过进口指令。第二种方法虽然稍微复杂一些,但提供了更大的灵活性,通常用于导入第三方捆绑包配置。继续读下去,了解更多关于这两种方法的信息。

导入配置进口

到目前为止,你已经把你的app.mailer直接在应用程序配置文件中定义服务容器(例如:应用程序/配置/ config.yml).当然,自从梅勒类本身存在于AcmeHelloBundle中,因此将app.mailer包中的容器定义也是如此。

首先,移动app.mailer容器定义到AcmeHelloBundle中的一个新的容器资源文件中。如果资源资源/配置目录不存在,创建它们。

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8
# src / Acme / HelloBundle /资源/ config / services.yml参数:app.mailer.transport:sendmail服务:app.mailer:类:AppBundle \梅勒参数:[' % app.mailer.transport % ')

定义本身没有变,只是位置变了。当然,服务容器不知道新的资源文件。方法可以轻松地导入资源文件进口输入应用程序配置。

  • YAML
  • XML
  • PHP
1 2 3
# app / config / config.yml进口:-资源:“@AcmeHelloBundle /资源/ config / services.yml”

请注意

由于解析参数的方式,您不能使用它们动态地在导入中构建路径。这意味着以下内容是行不通的:

  • YAML
  • XML
  • PHP
1 2 3
# app / config / config.yml进口:-资源:' % kernel.root_dir % / parameters.yml '

进口指令允许你的应用程序包含来自任何其他位置的服务容器配置资源(最常见的是来自bundle)。的资源对于文件,位置是资源文件的绝对路径。特殊的@AcmeHelloBundle语法解析AcmeHelloBundle包的目录路径。这可以帮助您指定资源的路径,而不必担心稍后是否将AcmeHelloBundle移动到不同的目录。

通过容器扩展导入配置

在Symfony中开发时,最常用的是欧宝娱乐app下载地址进口指令从专门为应用程序创建的包中导入容器配置。第三方捆绑包容器配置(包括Symfony核心服务)通常使用另一种更灵活、更容易在应用程序中配置的方法加载。欧宝娱乐app下载地址

下面是它的工作原理。在内部,每个包都定义了它的服务,就像您目前看到的那样。也就是说,一个包使用一个或多个配置资源文件(通常是XML)来指定该包的参数和服务。方法,而不是直接从应用程序配置中导入这些资源进口指令,您可以简单地调用服务容器扩展在为您工作的包中。服务容器扩展是由包作者创建的PHP类,用于完成两件事:

  • 导入为bundle配置服务所需的所有服务容器资源;
  • 提供语义的、直接的配置,这样就可以在不与包的服务容器配置的平面参数交互的情况下配置包。

换句话说,服务容器扩展为您的包配置服务。稍后您将看到,该扩展为配置bundle提供了一个合理的高级接口。

以FrameworkBundle (Symfony框架的核心包)为例。欧宝娱乐app下载地址在应用程序配置中出现以下代码会调用FrameworkBundle中的服务容器扩展:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7
# app / config / config.yml框架:秘密:xxxxxxxxxx形式:真正的csrf_protection:真正的路由器:资源:“% kernel.root_dir % / config / routing.yml”#……

解析配置时,容器查找可以处理框架配置指令。所讨论的扩展位于FrameworkBundle中,将被调用,并加载FrameworkBundle的服务配置。如果你移除框架关键字从您的应用程序配置文件,核心Symfony服务将不会被加载。欧宝娱乐app下载地址关键是您处于控制之中:Symfony框架不包含任何魔法或执行任何您无法控制的操作。欧宝娱乐app下载地址

当然,除了简单地“激活”FrameworkBundle的服务容器扩展,您还可以做更多的事情。每个扩展都允许您轻松地定制包,而不必担心如何定义内部服务。

在这种情况下,扩展允许您自定义error_handlercsrf_protection路由器配置等等。在内部,FrameworkBundle使用这里指定的选项来定义和配置特定于它的服务。这个包负责创建所有必要的内容参数而且服务对于服务容器,同时仍然允许轻松定制大部分配置。作为奖励,大多数服务容器扩展也足够智能,可以执行验证—通知您缺少的选项或错误的数据类型。

在安装或配置包时,请参阅包的文档,了解如何安装和配置包的服务。欧宝体育电话核心包可用的选项可以在参考指南

请注意

属性,服务容器仅识别参数服务,进口指令。任何其他指令都由服务容器扩展处理。

如果您想在自己的包中公开用户友好的配置,请阅读“如何在一个包内加载服务配置“食谱。

引用(注入)服务

到目前为止,还是原版app.mailerService很简单:它的构造函数中只有一个参数,这很容易配置。正如您将看到的,当您需要创建依赖于容器中的一个或多个其他服务的服务时,容器的真正功能才得以实现。

举个例子,假设您有一个新的服务,欧宝app在哪里找NewsletterManager,这有助于管理电子邮件的准备和发送到一组地址。当然app.mailerService已经非常擅长发送电子邮件,所以你将在内部使用它欧宝app在哪里找NewsletterManager来处理消息的实际传递。这个假装类可能看起来像这样:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ / src / AppBund欧宝app在哪里找le /通讯/ NewsletterManager.php名称空间AppBundle欧宝app在哪里找通讯使用AppBundle梅勒欧宝app在哪里找NewsletterManager受保护的梅勒公共函数__construct(梅勒梅勒->梅勒=梅勒;}/ /……

不使用服务容器,您可以创建一个新的欧宝app在哪里找NewsletterManager从控制器内部很容易:

1 2 3 4 5 6 7 8 9 10
使用AppBundle欧宝app在哪里找通讯欧宝app在哪里找NewsletterManager/ /……公共函数send欧宝app在哪里找NewsletterAction()梅勒->get (“app.mailer”);欧宝app在哪里找通讯欧宝app在哪里找NewsletterManager (梅勒);/ /……

这种方法很好,但是如果您稍后决定欧宝app在哪里找NewsletterManager类需要第二个或第三个构造函数参数?如果您决定重构代码并重命名类呢?在这两种情况下,都需要找到欧宝app在哪里找NewsletterManager实例化并修改它。当然,服务容器给了你一个更吸引人的选择:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8
# app / config / services.yml服务:app.mailer:#……app.欧宝app在哪里找newsletter_manager:类:AppBundle 欧宝app在哪里找\通讯\ NewsletterManager参数:(“@app.mailer”)

在YAML中,特别的@app.mailer语法告诉容器查找名为app.mailer的构造函数欧宝app在哪里找NewsletterManager.但是,在本例中,是指定的服务app.mailer必须存在。如果没有,则抛出异常。您可以将依赖项标记为可选—这将在下一节中讨论。

使用引用是一种非常强大的工具,它允许您创建具有良好定义的依赖关系的独立服务类。在本例中,app.欧宝app在哪里找newsletter_manager服务需要app.mailer服务才能发挥作用。当您在服务容器中定义此依赖项时,容器将负责实例化类的所有工作。

可选依赖项:Setter注入

以这种方式向构造函数中注入依赖项是确保依赖项可用的极好方法。如果你有一个类的可选依赖,那么“setter注入”可能是一个更好的选择。这意味着使用方法调用而不是通过构造函数注入依赖项。这个类看起来是这样的:

12 3 4 5 6 7 8 9 10 11 12 13 14 15
名称空间AppBundle欧宝app在哪里找通讯使用AppBundle梅勒欧宝app在哪里找NewsletterManager受保护的梅勒公共函数setMailer(梅勒梅勒->梅勒=梅勒;}/ /……

通过setter方法注入依赖只需要改变语法:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8 9
# app / config / services.yml服务:app.mailer:#……app.欧宝app在哪里找newsletter_manager:类:AppBundle 欧宝app在哪里找\通讯\ NewsletterManager电话:-[setMailer,[“@app.mailer”]]

请注意

本节中介绍的方法称为“构造函数注入”和“setter注入”。Symf欧宝娱乐app下载地址ony服务容器还支持“属性注入”。

可选引用

有时,您的某个服务可能具有可选依赖项,这意味着您的服务不需要依赖项才能正常工作。在上面的例子中,app.mailer服务必须存在,否则将引发异常。通过修改app.欧宝app在哪里找newsletter_manager服务定义,您可以将此引用设为可选的,有两种策略可以做到这一点。

将Missing Dependencies设置为null

您可以使用策略来显式地将参数设置为如果服务不存在:

  • XML
  • PHP
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<!--app/config/services.xml --><??> . xml version="1.0" encoding="UTF-8"<容器xmlns“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services”xmlns: xsi“http://www.w3.org/2001/XMLSchema-instance”xsi: schemaLocation“http://欧宝娱乐app下载地址www.oldmanjams.com/schema/dic/services //www.oldmanjams.com/schema/dic/services/services-1.0.xsd”><服务><服务id“app.mailer”><!--...-->服务><服务id“app.欧宝app在哪里找newsletter_manager”“AppBundle 欧宝app在哪里找\通讯\ NewsletterManager”><论点类型“服务”id“app.mailer”on-invalid“零”/>服务>服务>容器>

请注意

YAML驱动程序目前不支持“null”策略。

忽略缺失的依赖项

忽略缺失的依赖项的行为与“null”行为相同,除非是在方法调用中使用,在这种情况下,方法调用本身将被删除。

在下面的例子中,如果服务存在,容器将使用方法调用注入服务,如果不存在,则删除方法调用:

  • YAML
  • XML
  • PHP
1 2 3 4 5
# app / config / services.yml服务:app.欧宝app在哪里找newsletter_manager:类:AppBundle 欧宝app在哪里找\通讯\ NewsletterManager参数:(“@ app.mailer ?”)

在YAML中,特别的@吗?语法告诉服务容器依赖项是可选的。当然,欧宝app在哪里找NewsletterManager也必须重写以允许可选的依赖:

1 2 3 4
公共函数__construct(梅勒梅勒= null)/ /……

核心Sym欧宝娱乐app下载地址fony和第三方捆绑服务

由于Symf欧宝娱乐app下载地址ony和所有第三方捆绑包通过容器配置和检索它们的服务,您可以轻松地访问它们,甚至在您自己的服务中使用它们。为了简单起见,Symfony默认情况下不要求必欧宝娱乐app下载地址须将控制器定义为服务。此外,Symfony将整个欧宝娱乐app下载地址服务容器注入到控制器中。例如,为了处理用户会话上的信息存储,Symfony提供了一个欧宝娱乐app下载地址会话服务,你可以在一个标准控制器中访问它,如下所示:

1 2 3 4 5 6 7
公共函数indexAction酒吧会话->get (“会话”);会话->集(“foo”酒吧);/ /……

在Sy欧宝娱乐app下载地址mfony中,您将经常使用Symfony核心或其他第三方包提供的服务来执行任务,例如呈现模板(模板),发送电子邮件(梅勒),或存取有关请求的资料(请求).

您可以更进一步,在为应用程序创建的服务中使用这些服务。首先修改欧宝app在哪里找NewsletterManager才能使用真正的Symfony欧宝娱乐app下载地址梅勒服务(而不是假装app.mailer).还将模板引擎服务传递给欧宝app在哪里找NewsletterManager这样它就可以通过模板生成电子邮件内容:

12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21
/ / src / AppBund欧宝app在哪里找le /通讯/ NewsletterManager.php名称空间AppBundle欧宝app在哪里找通讯使用欧宝娱乐app下载地址组件模板EngineInterface欧宝app在哪里找NewsletterManager受保护的梅勒受保护的模板公共函数__construct(\ Swift_Mailer梅勒, EngineInterface模板->梅勒=梅勒->模板=模板;}/ /……

配置服务容器很简单:

  • YAML
  • XML
  • PHP
1 2 3 4 5
# app / config / services.yml服务:app.欧宝app在哪里找newsletter_manager:类:AppBundle 欧宝app在哪里找\通讯\ NewsletterManager参数:[' @mailer ',“@templating”

app.欧宝app在哪里找newsletter_manager服务现在可以访问核心梅勒而且模板服务。这是创建特定于应用程序的服务的常用方法,可以利用框架内不同服务的功能。

提示

请确保swiftmailer条目出现在应用程序配置中。正如在服务容器,swiftmailerkey从SwiftmailerBundle调用服务扩展,它会注册梅勒服务。

标签

与Web上的博客文章可能被标记为“Symfony”或“PHP”类似,容器中配置的服务也可以被标记。欧宝娱乐app下载地址在服务容器中,标记意味着该服务将用于特定目的。举个例子:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7
# app / config / services.yml服务:foo.twig.extension:类:AppBundle \ \ FooExtension延伸公众:标签:-名称:twig.extension

twig.extensiontag是TwigBundle在配置过程中使用的一个特殊标签。通过给服务这个twig.extension标记,捆绑包知道foo.twig.extension服务应该注册为Twig扩展与Twig。换句话说,Twig找到标记为twig.extension并自动将它们注册为扩展。

因此,标记是一种告诉Symfony或其他第三方捆绑包您的服务欧宝娱乐app下载地址应该由捆绑包以某种特殊方式注册或使用的方法。

有关核心Symfony框架中可用的所有标记的列表,请查看欧宝娱乐app下载地址依赖注入标签.每个标签对您的服务有不同的影响,许多标签需要额外的参数(除了的名字参数)。

调试服务

您可以使用控制台找出在容器中注册了哪些服务。要显示所有服务和每个服务的类,运行:

1
$ PHP应用程序/控制台容器

默认情况下,只显示公共服务,但您也可以查看私有服务:

1
$ PHP应用程序/控制台容器

请注意

如果私有服务仅用作参数一个其他服务,它将不会被显示容器:调试命令,即使在使用——show-private选择。看到内联私有服务欲知详情。

你可以通过指定某个服务的id来获取它的详细信息:

1
$ PHP应用/控制台容器:调试应用.mailer
此工作,包括代码示例,是根据创作共用BY-SA 3.0许可证。