Symfony服務容器及其語法

原文地址:http://www.php.cn/php-weizijiaocheng-415202.html

 

 現在的PHP應用程序都是面向對象開發,所以主要是由對象構成。有的對象可以方便的分發郵件信息而有的可能幫你把信息寫入到數據庫中。在你的應用程序中,你可能創建一個對象用於管理你的產品庫存,或者另外一個對象處理來自第三方API的數據。重要的是現在應用程序要做的這些事情都是被組織到許許多多的對象中來處理它的每一項任務的。

  我們將套路一下Symfony2中一個特殊的PHP對象,它幫助我們實例化,組織和獲取你應用程序彙總的許多對象。這個對象叫做服務容器,它可以幫助你使用標準統一的方式來創建你程序中的對象。它能簡化你的繁雜的初始對象工作,並且擁有超快的執行速度,強調該框架提高了可重用性和降低了代碼耦合。
  因爲所有的Symfony2的類都是用了該容器,所以你需要了解怎樣去擴展,配置和使用對象。
  從大的方面說,服務容器對Symfony2的速度和可擴展性是一個最大的貢獻者。

  最後,配置和使用一個服務容器是非常簡單的。在本章的最後,你將能通過容器和來自第三方Bundle的自定義對象很舒適的創建你自己的對象。你將能夠寫出更加具有重用性,可測試性以及鬆散耦合的代碼。

1、什麼是服務?


  簡而言之,一個服務就是任何一個執行一些全局任務的PHP對象。
  在計算機科學中它是一個通用名字用來描述一個對象被創建用來滿足特定目的。
  每個服務都會應用於你整個應用程序,無論何時你需要他們的時候,他們都能提供。

  你不需要做任何特別的是來製造服務,僅僅寫一個PHP類,在類中定義一些完成某種功能的代碼即可。

  有一個規則,當一個PHP對象被在整個應用程序中全局使用的時候,該PHP對象就是一個服務了。
  一個單獨的Mailer服務用於整個應用程序的郵件信息發送,而許多Message對象被分發這不是服務。
  同樣的,Product對象不是一個服務,但是一個對象能夠持久化Product到數據庫,那麼這個對象就是服務了。

  那麼接下來還有什麼重要的事呢?
  考慮服務的一個優點就是說明你開始考慮從你的應用程序中分離某一功能到一些列的服務。因爲每個服務只做一件工作。
  當你需要的時候你可以很容易的訪問每個服務和使用它們的功能。
  每個服務也必須能很容易的測試和配置,因爲它們是從你應用程序的其他的功能中分離了出來的。
  這種主意叫做面向服務架構。 圍繞着一些列相對獨立的服務構造你的應用程序是一個非常著名和可信任的面向對象實踐。
  這也是能成爲一個優秀開發者的必備。

2、什麼是服務容器?


  一個服務容器,也叫做依賴注入容器,僅僅是一個PHP對象,用於管理服務的實例化。
  比如,假設我們有一個簡單的PHP類分發Email信息。 沒有服務容器,我們必須在使用它的時候手動的創建這個對象:


 
  1. use Acme\HelloBundle\Mailer;

  2. $mailer = new Mailer('sendmail');

  3. $mailer->send('[email protected]',...);

這看上去很容易,假設Mailer類允許我們配置用於分發郵件信息的方法(sendmail,smtp等)。但是如果我們想在別的地方使用mailer服務? 我們又不想重複的在每一個使用Mailer的地方配置它。如果我們需要改變郵件發送的地址怎麼辦?我們需要找到每一個Mailer配置的然後修改代碼。

3、在容器中創建/配置服務:

 

  一個更好的做法是讓服務容器來爲我們創建Mailer對象。爲了能夠實現這一做法,我們必須教會容器怎樣去創建Mailer服務。這些工作是通過配置文件來實現的,配置文件你可以設置成YAML,XML或者PHP格式均可。

YAML格式:


 
  1. #app/config/config.yml

  2. services:

  3. my_mailer:

  4. class: Acme\HelloBundle\Mailer

  5. arguments: [sendmail]

XML格式:


 
  1. <!-- app/config/config.xml-->

  2. <services>

  3. <service id="my_mailer" class="Acme\HelloBundle\Mailer">

  4. <argument>sendmail</argument>

  5. </service>

  6. </services>

PHP代碼格式:


 
  1. //app/config/config.php

  2. use Symfony\Component\DependencyInjection\Definition;

  3. $container->setDefinition('my_mailer',new Definition(

  4. 'Acme\HelloBundle\Mailer',

  5. array('sendmail')

  6. ));

  當Symfony2 初始化時,它會默認根據應用程序配置(app/config/config.yml)創建服務容器。真正的服務容器配置文件是AppKernel::registerContainerConfiguration()方法加載的一個環境特定配置文件,config_dev.yml 用於開發階段,config_prod.yml用於運營階段。

4、在控制器中使用服務:


        Symfony2配置啓動後,Acme\HelloBundle\Mailer的對象實例就能夠通過服務容器使用了。服務容器可以在任何傳統的Symfony2的controller中使用,通過簡寫方法 get()調用。


 
  1. class HelloController extends Controller

  2. {

  3. //...

  4. public function sendEmailAction()

  5. {

  6. //...

  7. $mailer = $this->get('my_mailer');

  8. $mailer->send('[email protected]',...);

  9. }

  10. }

當我們從服務容器中獲取Mailer實例時,使用my_mailer. 容器會創建這個對象並返回它。有另外一個好處,服務直到用到它時纔會創建。這樣能夠節省內存提高程序效率。

5、服務容器的參數配置

 

  通過服務容器創建一個新服務很簡單,參數的設置會使定義更加有組織性和有適應性。

YAML格式:


 
  1. #app/config/config.yml

  2. parameters:

  3. my_mailer.class: Acme\HelloBundle\Mailer

  4. my_mailer.transport: sendmail

  5.  
  6. services:

  7. my_mailer:

  8. class: %my_mailer.class%

  9. arguments: [%my_mailer.transport%]

XML格式:


 
  1. <!-- app/config/config.xml -->

  2. <parameters>

  3. <parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>

  4. <parameter key="my_mailer.transport">sendmail</parameter>

  5. </parameters>

  6.  
  7. <services>

  8. <service id="my_mailer" class="%my_mailer.class%">

  9. <argument>%my_mailer.transport%</argument>

  10. </service>

  11. </services>

PHP代碼格式:


 
  1. // app/config/config.php

  2. use Symfony\Component\DependencyInjection\Definition;

  3.  
  4. $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer');

  5. $container->setParameter('my_mailer.transport', 'sendmail');

  6.  
  7. $container->setDefinition('my_mailer', new Definition(

  8. '%my_mailer.class%',

  9. array('%my_mailer.transport%')

  10. ));

上面配置文件中,%% 是參數定義方式,當容器創建完成後就會查找參數定義,如果%參數或者變量作爲字符串一部分時,需要添加另一個%進行轉換:

<argument type="string">http://symfony.com/?foo=%%s&bar=%%d</argument>

  參數的目的就是把信息傳入服務,當然,不定義任何參數也是沒有問題的。

  定義參數會具有某些優勢:
    在一個參數鍵parameters中分離和組織服務的所有可選項。
    參數值可以用於多個服務的定義。
    在一個bundle中創建一個服務,使用參數可以很容易的在你的應用程序中進行自定義化服務。

  當然,用不用參數完全取決於你的決定。高質量的第三方bundles總是使用參數,當他們把服務放入容器中使其更具可配置性。當然,在你的應用程序中你可能不需要這樣的配置性。

6、數組參數配置:

 

  參數不一定都是普通字符串,也有可能是數組。如果是寫在XML格式的配置文件中,那麼你需要爲數組參數定義type="collection" 屬性。

YAML格式:


 
  1.  
  2. # app/config/config.yml

  3. parameters:

  4. my_mailer.gateways:

  5. - mail1

  6. - mail2

  7. - mail3

  8. my_multilang.language_fallback:

  9. en:

  10. - en

  11. - fr

  12. fr:

  13. - fr

  14. - en

XML格式:


 
  1. <!-- app/config/config.xml -->

  2. <parameters>

  3. <parameter key="my_mailer.gateways" type="collection">

  4. <parameter>mail1</parameter>

  5. <parameter>mail2</parameter>

  6. <parameter>mail3</parameter>

  7. </parameter>

  8. <parameter key="my_multilang.language_fallback" type="collection">

  9. <parameter key="en" type="collection">

  10. <parameter>en</parameter>

  11. <parameter>fr</parameter>

  12. </parameter>

  13. <parameter key="fr" type="collection">

  14. <parameter>fr</parameter>

  15. <parameter>en</parameter>

  16. </parameter>

  17. </parameter>

  18. </parameters>

PHP代碼格式:


 
  1. // app/config/config.php

  2. use Symfony\Component\DependencyInjection\Definition;

  3.  
  4. $container->setParameter('my_mailer.gateways', array('mail1', 'mail2', 'mail3'));

  5. $container->setParameter('my_multilang.language_fallback',

  6. array('en' => array('en', 'fr'),

  7. 'fr' => array('fr', 'en'),

  8. ));

7、導入其它容器配置資源:

 

  在這裏我們把服務配置文件看成資源。這裏就是想說明一個事實,幾乎所有的配置資源都是文件,如YAML,XML,PHP等。
  Symfony2 非常靈活,它的配置可以放到任何地方,比如數據庫更或者外部的一個webservice。

  服務容器默認情況下用一個單一的配置資源創建(app/config/config.yml)。其它所有的服務配置必須從這個文件中一次或者多次導入。這包括Symfony2核心配置和第三方bundle配置。這給你的應用程序在服務上有了相對的靈活性。

  擴展的服務配置可以通過兩種方式導入:第一,我們最常用的是 imports 命令。接下來我們會介紹第二種方法,它是更靈活並且是導入第三方bundles中的服務配置的首選。

 

1)通過imports導入配置

  到目前爲止,我們已經把my_mailer服務的配置直接定義到了應用程序配置文件(app/config/config.yml)中了。當然,因爲Mailer本身就在AcmeHelloBundle中,其實把my_mailer的容器定義放到bundle中也可以。

  首先,我們把my_mailer的容器定義移到一個新的容器資源文件中把它放到AcmeHelloBundle之外。如果Resources或者Resources/config 目錄不存在,我們創建它。

YAML格式:


 
  1. # src/Acme/HelloBundle/Resources/config/services.yml

  2. parameters:

  3. my_mailer.class: Acme\HelloBundle\Mailer

  4. my_mailer.transport: sendmail

  5.  
  6. services:

  7. my_mailer:

  8. class: %my_mailer.class%

  9. arguments: [%my_mailer.transport%]

XML格式:


 
  1. <!-- src/Acme/HelloBundle/Resources/config/services.xml -->

  2. <parameters>

  3. <parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>

  4. <parameter key="my_mailer.transport">sendmail</parameter>

  5. </parameters>

  6.  
  7. <services>

  8. <service id="my_mailer" class="%my_mailer.class%">

  9. <argument>%my_mailer.transport%</argument>

  10. </service>

  11. </services>

PHP代碼格式:


 
  1. // src/Acme/HelloBundle/Resources/config/services.php

  2. use Symfony\Component\DependencyInjection\Definition;

  3.  
  4. $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer');

  5. $container->setParameter('my_mailer.transport', 'sendmail');

  6.  
  7. $container->setDefinition('my_mailer', new Definition(

  8. '%my_mailer.class%',

  9. array('%my_mailer.transport%')

  10. ));

  配置定義的本身沒有發生變化,只是挪了個位置。當然,服務容器不知道我們的資源文件新位置。幸運的是我們可以利用imports 鍵在應用程序的配置文件中很容易的導入它們。

YAML格式:


 
  1. # app/config/config.yml

  2. imports:

  3. - { resource: @AcmeHelloBundle/Resources/config/services.yml }

XML格式:


 
  1. <!-- app/config/config.xml -->

  2. <imports>

  3. <import resource="@AcmeHelloBundle/Resources/config/services.xml"/>

  4. </imports>

PHP代碼格式:


 
  1. // app/config/config.php

  2. $this->import('@AcmeHelloBundle/Resources/config/services.php');

  imports命令允許你的應用程序從其他地方獲取服務容器的配置資源(一般是從一些bundle中)。resource的位置,對於文件資源,是絕對路徑。@AcmeHello語法決定了AcmeHelloBundle 的路徑。這使得你指定資源路徑的時候不用擔心以後移動AcmeHelloBundle到別的目錄的問題。

2)使用容器擴展導入配置

  在使用Symfony2 開發過程中,你將經常用到imports命令來從你創建的bundle中導入容器配置。而第三方的bundle容器配置,包括Symfony2的核心服務在內,通常使用另外一種方法,它更靈活更易於配置。

  它是如何工作的呢?
  實際上,每個bundle在定義自己的服務配置是都是跟到目前爲止我看到的是一樣的。換句話說,一個bundle使用一個或者多個配置資源文件(通常是XML)來指定bundle所需要的參數和服務。然而,我們不直接在配置文件中使用imports命令導入它們,而是僅僅在bundle中調用一個服務容器擴展來爲我們做同樣的工作。

一個服務容器擴展bundle的作者創建的是一個PHP類,它主要完成兩件事情:
  爲該bundle配置服務導入需要的所有的服務容器資源。
  提供語法上簡介配置,讓bundle能夠直接被配置,而不需要再與bundle的服務容器配置參數交互。

  換句話說,一個服務容器擴展會幫你配置好它的bundle所需的服務。

  讓我們看看FrameworkBundle是如何做的。
  FrameworkBundle是Symfony2框架bundle,下面的代碼顯示了在你的應用程序配置中調用FrameworkBundle中的服務容器擴展。

YAML格式:


 
  1. # app/config/config.yml

  2. framework:

  3. secret: xxxxxxxxxx

  4. charset: UTF-8

  5. form: true

  6. csrf_protection: true

  7. router: { resource: "%kernel.root_dir%/config/routing.yml" }

  8. # ...

XML格式:


 
  1. <!-- app/config/config.xml -->

  2. <framework:config charset="UTF-8" secret="xxxxxxxxxx">

  3. <framework:form />

  4. <framework:csrf-protection />

  5. <framework:router resource="%kernel.root_dir%/config/routing.xml" />

  6. <!-- ... -->

  7. </framework>

PHP代碼格式:


 
  1. // app/config/config.php

  2. $container->loadFromExtension('framework', array(

  3. 'secret' => 'xxxxxxxxxx',

  4. 'charset' => 'UTF-8',

  5. 'form' => array(),

  6. 'csrf-protection' => array(),

  7. 'router' => array('resource' => '%kernel.root_dir%/config/routing.php'),

  8. // ...

  9. ));

  當這個配置被解析時,容器會查找一個可以處理framework配置命令的擴展。這個擴展在FrameworkBundle中,它會被調用來爲FrameworkBundle加載服務配置。如果你從你的配置文件中完全去掉framework鍵,Symfony 核心服務將不會被加載。這完全是由你控制的。

  當然,你可以做更多,而不只是激活FrameworkBundle的服務容器擴展。每個擴展都允許你很容易的個性化bundle,而不用關係其內部服務是怎麼定義的。

  比如你可以個性化charset,error_handler,csrf_protection,router等配置。實際上,FrameworkBundle使用的這裏指定的項目來配置服務於它自身的服務配置。bundle負責爲服務容器創建所有需要的parameters和services,同時依然允許大量的配置可以被個性化。作爲一個額外的好處,大部分服務容器擴展能夠執行校驗,通知你那些選項丟失或者數據類型不正確。當安裝或者配置一個bundle時應該看看bundle的說明,瞭解一下如何安裝和配置它需要的服務。

  注意:服務容器天生能夠識別parameters,services 和imports命令,其它的命令則需要服務容器擴展來處理。

 

8、引用(注入)服務:


  到目前爲止,我們創建的my_mailer服務非常簡單:它僅僅在它的構造函數中接受一個參數,非常容易配置。只有當我們需要創建一個服務而它又依賴於一個或者多個其它服務時,我們才能看到服務容器的真正威力。

1)構造函數式依賴注入:

  讓我們來看個例子:
  假設我們有個新服務,NewsletterManager,它用於管理準備和分發一個郵件信息到一組地址。當然,my_mailer已經能夠發送郵件信息了,所以我們將在NewsletterManager內部使用它。
  假設它的類內容如下:


 
  1. namespace Acme\HelloBundle\Newsletter;

  2.  
  3. use Acme\HelloBundle\Mailer;

  4.  
  5. class NewsletterManager

  6. {

  7. protected $mailer;

  8.  
  9. public function __construct(Mailer $mailer)

  10. {

  11. $this->mailer = $mailer;

  12. }

  13.  
  14. // ...

  15. }

  上面的例子代碼中沒有使用服務容器,我們可以很容易的在controller內創建一個新的NewsletterManager實例。


 
  1. public function sendNewsletterAction()

  2. {

  3. $mailer = $this->get('my_mailer');

  4. $newsletter = new Acme\HelloBundle\Newsletter\NewsletterManager($mailer);

  5. // ...

  6. }

這種方式是好的,但是如果我們決定NewsletterManager類需要第二個或者第三個構造函數參數呢?
  我們可以重寫代碼並重新命名該類來實現,可是我們需要找到所有的使用NewsletterManager的地方修改它。這是很痛苦的事情,這時候服務容器就成了一個很誘人的選擇。

YAML格式:


 
  1. # src/Acme/HelloBundle/Resources/config/services.yml

  2. parameters:

  3. # ...

  4. newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager

  5.  
  6. services:

  7. my_mailer:

  8. # ...

  9. newsletter_manager:

  10. class: %newsletter_manager.class%

  11. arguments: [@my_mailer]

XML格式:


 
  1. <!-- src/Acme/HelloBundle/Resources/config/services.xml -->

  2. <parameters>

  3. <!-- ... -->

  4. <parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter>

  5. </parameters>

  6.  
  7. <services>

  8. <service id="my_mailer" ... >

  9. <!-- ... -->

  10. </service>

  11. <service id="newsletter_manager" class="%newsletter_manager.class%">

  12. <argument type="service" id="my_mailer"/>

  13. </service>

  14. </services>

PHP代碼格式:


 
  1. // src/Acme/HelloBundle/Resources/config/services.php

  2. use Symfony\Component\DependencyInjection\Definition;

  3. use Symfony\Component\DependencyInjection\Reference;

  4.  
  5. // ...

  6. $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager');

  7.  
  8. $container->setDefinition('my_mailer', ... );

  9. $container->setDefinition('newsletter_manager', new Definition(

  10. '%newsletter_manager.class%',

  11. array(new Reference('my_mailer'))

  12. ));

  在YAML配置文件中,@my_mailer告訴容器去查找一個名叫my_mailer的服務對象並把它傳遞給NewsletterManager的構造函數。在這種情況下,指定的服務my_mailer必須存在。如果不存在,將會拋出異常。你可以把你的依賴標記爲可選,接下來說明。

 

2)可選setter依賴注入:


  通過構造函數參數方式注入一個依賴在依賴已經存在並可用的情況下是一個完美的方式。但是當一個類的依賴是可選的時候,setter注入就成了更好的選擇。


 
  1. namespace Acme\HelloBundle\Newsletter;

  2.  
  3. use Acme\HelloBundle\Mailer;

  4.  
  5. class NewsletterManager

  6. {

  7. protected $mailer;

  8.  
  9. public function setMailer(Mailer $mailer)

  10. {

  11. $this->mailer = $mailer;

  12. }

  13.  
  14. // ...

  15. }

相應的配置上只需要一點改動:

YAML格式:


 
  1. # src/Acme/HelloBundle/Resources/config/services.yml

  2. parameters:

  3. # ...

  4. newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager

  5.  
  6. services:

  7. my_mailer:

  8. # ...

  9. newsletter_manager:

  10. class: %newsletter_manager.class%

  11. calls:

  12. - [ setMailer, [ @my_mailer ] ]

XML格式:


 
  1. <!-- src/Acme/HelloBundle/Resources/config/services.xml -->

  2. <parameters>

  3. <!-- ... -->

  4. <parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter>

  5. </parameters>

  6.  
  7. <services>

  8. <service id="my_mailer" ... >

  9. <!-- ... -->

  10. </service>

  11. <service id="newsletter_manager" class="%newsletter_manager.class%">

  12. <call method="setMailer">

  13. <argument type="service" id="my_mailer" />

  14. </call>

  15. </service>

  16. </services>

PHP代碼格式:


 
  1. // src/Acme/HelloBundle/Resources/config/services.php

  2. use Symfony\Component\DependencyInjection\Definition;

  3. use Symfony\Component\DependencyInjection\Reference;

  4.  
  5. // ...

  6. $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager');

  7.  
  8. $container->setDefinition('my_mailer', ... );

  9. $container->setDefinition('newsletter_manager', new Definition(

  10. '%newsletter_manager.class%'

  11. ))->addMethodCall('setMailer', array(

  12. new Reference('my_mailer')

  13. ));

 

3)設置可選引用依賴注入:


  有時候,你的應用可能有一個可選的依賴,這就意味着這個依賴對於你的服務運行不是必須的。上面例子中,my_mailer服務必須存在,所以沒有它是會拋出異常。我們來修改newsletter_manager服務定義,讓這個依賴變爲可選依賴。這樣當它存在是容器會注入它,如果不存在時,什麼也不做。

YAML格式:


 
  1. # src/Acme/HelloBundle/Resources/config/services.yml

  2. parameters:

  3. # ...

  4.  
  5. services:

  6. newsletter_manager:

  7. class: %newsletter_manager.class%

  8. arguments: [@?my_mailer]

XML格式:


 
  1. <!-- src/Acme/HelloBundle/Resources/config/services.xml -->

  2.  
  3. <services>

  4. <service id="my_mailer" ... >

  5. <!-- ... -->

  6. </service>

  7. <service id="newsletter_manager" class="%newsletter_manager.class%">

  8. <argument type="service" id="my_mailer" on-invalid="ignore" />

  9. </service>

  10. </services>

PHP代碼格式:


 
  1. // src/Acme/HelloBundle/Resources/config/services.php

  2. use Symfony\Component\DependencyInjection\Definition;

  3. use Symfony\Component\DependencyInjection\Reference;

  4. use Symfony\Component\DependencyInjection\ContainerInterface;

  5.  
  6. // ...

  7. $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager');

  8.  
  9. $container->setDefinition('my_mailer', ... );

  10. $container->setDefinition('newsletter_manager', new Definition(

  11. '%newsletter_manager.class%',

  12. array(new Reference('my_mailer', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))

  13. ));

  在YAML配置文件中,@? 語法標示告訴服務容器該依賴是可選的。當然,NewsletterManager類也需要相應的修改一下構造函數:


 
  1. public function __construct(Mailer $mailer = null)

  2. {

  3. // ...

  4. }

 

4)Symfony核心和第三方bundle服務依賴注入(在控制器Controller中):


  從Symfony2和所有第三方bundles的配置都通過容器獲取他們的服務, 你可以很容易的訪問他們或者在你自己的服務中使用他們。爲了保持簡潔,Symfoy2模式不需要controller也定義成服務。而是Symfony2把整個服務注入到所有的Controller中。比如,處理在用戶Session中存在信息時,Symfony2 提供了一個session服務,你可以在一個標準controller中直接調用:


 
  1. public function indexAction($bar)

  2. {

  3. $session = $this->get('session');

  4. $session->set('foo', $bar);

  5.  
  6. // ...

  7. }

  在Symfony2中,你將經常使用Symfoy或第三方bundles提供的服務來執行任務,比如渲染模板的templating, 發送郵件的mailer訪問請求信息的request等。

我們可以進一步的在我們自己創建的服務中調用這些服務。讓我們修改NewsletterManager使用真正的Symfony2 mailer服務。同時我們還爲其傳入模板引擎,讓它通過一個模板生成郵件內容。


 
  1. namespace Acme\HelloBundle\Newsletter;

  2.  
  3. use Symfony\Component\Templating\EngineInterface;

  4.  
  5. class NewsletterManager

  6. {

  7. protected $mailer;

  8.  
  9. protected $templating;

  10.  
  11. public function __construct(\Swift_Mailer $mailer, EngineInterface $templating)

  12. {

  13. $this->mailer = $mailer;

  14. $this->templating = $templating;

  15. }

  16.  
  17. // ...

  18. }

配置服務容器:

YAML格式:


 
  1. services:

  2. newsletter_manager:

  3. class: %newsletter_manager.class%

  4. arguments: [@mailer, @templating]

XML格式:


 
  1. <service id="newsletter_manager" class="%newsletter_manager.class%">

  2. <argument type="service" id="mailer"/>

  3. <argument type="service" id="templating"/>

  4. </service>

PHP代碼格式:


 
  1. $container->setDefinition('newsletter_manager', new Definition(

  2. '%newsletter_manager.class%',

  3. array(

  4. new Reference('mailer'),

  5. new Reference('templating')

  6. )

  7. ));

 

9、高級容器配置:

  到此我們看到,在容器中定義一個服務非常簡單,通常包含一個服務的配置鍵和一些參數。然而,容器還有許多其它的可用工具幫助標誌(tag)服務爲特定的功能。 以創建更復雜的服務,在容器建立後執行操作。

設置服務爲public/private
  在定義服務的時候,你通常想在應用程序範圍內訪問它們,這些服務叫做public服務。比如doctrine服務在使用DoctrineBundle註冊時就是一個公共服務,你可以按照如下方式訪問:

$doctrine = $container->get('doctrine');

  然而,在某些情況下你不想一個服務變爲公共的。這種情況通常出現在你創建某個服務只是爲另外一個服務作爲輸入參數時出現。private 私有服務,這些服務在調用的時候只能在參數行內通過 new PrivateFooBar()形式引用。

  簡單的說:一個服務當你不想它被你的代碼直接訪問時,它就是私有的了。

配置形式如下:

YAML格式:


 
  1. services:

  2. foo:

  3. class: Acme\HelloBundle\Foo

  4. public: false

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo" public="false" />

PHP代碼格式:


 
  1. $definition = new Definition('Acme\HelloBundle\Foo');

  2. $definition->setPublic(false);

  3. $container->setDefinition('foo', $definition);

這時候你就不能再進行如此操作了:

$container->get('foo');

注意:服務默認情況下全部是公共的。如果一個服務被配置爲private了,但是你還想引用它,那麼你需要給它定義別名。

 

1)服務的別名:


  當使用核心或者第三方bundles提供的服務時,你可能想用更加方便快捷的形式調用某些服務。你可以通過給他們定義別名來實現,甚至給私有服務定義別名。

YAML格式:


 
  1. services:

  2. foo:

  3. class: Acme\HelloBundle\Foo

  4. bar:

  5. alias: foo

XML格式:


 
  1. <service id="foo" class="Acme\HelloBundle\Foo"/>

  2. <service id="bar" alias="foo" />

PHP代碼格式:


 
  1. $definition = new Definition('Acme\HelloBundle\Foo');

  2. $container->setDefinition('foo', $definition);

  3.  
  4. $containerBuilder->setAlias('bar', 'foo');

這時你可以通過別名來直接調用以前的私有服務了,比如:

$container->get('bar'); // 將返回以前私有服務 foo

 

2)要求必備文件:


  有些情況下,你需要在加載服務前包含其它文件,這時你可以使用file命令實現:

YAML格式:


 
  1. services:

  2. foo:

  3. class: Acme\HelloBundle\Foo\Bar

  4. file: %kernel.root_dir%/src/path/to/file/foo.php

PHP代碼格式:


 
  1. $definition = new Definition('Acme\HelloBundle\Foo\Bar');

  2. $definition->setFile('%kernel.root_dir%/src/path/to/file/foo.php');

  3. $container->setDefinition('foo', $definition);

XML格式:


 
  1. <service id="foo" class="Acme\HelloBundle\Foo\Bar">

  2. <file>%kernel.root_dir%/src/path/to/file/foo.php</file>

  3. </service>

注意:Symfony將在內部調用PHP函數require_once,這就意味着你的文件將每個請求都會被包括一次。

 

3)服務標籤(Tags):


  就像你在網絡上發表博客可以設置標籤“Symfony”或者“PHP”等一樣,你在容器中配置的服務也可以被貼上標籤。一個標籤暗示這個服務是被用於特殊目的的。比如:

YAML格式:


 
  1. services:

  2. foo.twig.extension:

  3. class: Acme\HelloBundle\Extension\FooExtension

  4. tags:

  5. - { name: twig.extension }

XML格式:


 
  1. <service id="foo.twig.extension" class="Acme\HelloBundle\Extension\FooExtension">

  2. <tag name="twig.extension" />

  3. </service>

PHP代碼格式:


 
  1. $definition = new Definition('Acme\HelloBundle\Extension\FooExtension');

  2. $definition->addTag('twig.extension');

  3. $container->setDefinition('foo.twig.extension', $definition);

  這裏的twig.extension 標籤就是一個專用標籤,是TwigBundle在配置時使用的。通過給服務標註這個twig.extension標籤,bundle就會知道foo.twig.extension 服務應該被註冊爲一個Twig的擴展。換句話說,Twig會查找所有標記爲twig.extension的服務並自動把它們註冊爲擴展。

Symfony2核心bundles的可用的標籤:

assetic.filter
assetic.templating.php
data_collector
form.field_factory.guesser
kernel.cache_warmer
kernel.event_listener
monolog.logger
routing.loader
security.listener.factory
security.voter
templating.helper
twig.extension
translation.loader
validator.constraint_validator

 

以上就是有關Symfony2 中類似Spring容器的Services Container的基本知識。

參考URL:http://symfony.com/doc/current/book/service_container.html

參考博客地址:https://www.cnblogs.com/Seekr/archive/2012/06/19/2554934.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章