Symfony2 學習筆記之服務容器

huidaoli發表於2014-08-06

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

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

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

什麼是服務?
簡而言之,一個服務就是任何一個執行一些全域性任務的PHP物件。
在電腦科學中它是一個通用名字用來描述一個物件被建立用來滿足特定目的。
每個服務都會應用於你整個應用程式,無論何時你需要他們的時候,他們都能提供。

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

有一個規則,當一個PHP物件被在整個應用程式中全域性使用的時候,該PHP物件就是一個服務了。
一個單獨的Mailer服務用於整個應用程式的郵件資訊傳送,而許多Message物件被分發這不是服務。
同樣的,Product物件不是一個服務,但是一個物件能夠持久化Product到資料庫,那麼這個物件就是服務了。

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

什麼是服務容器?
一個服務容器,也叫做依賴注入容器,僅僅是一個PHP物件,用於管理服務的例項化。
比如,假設我們有一個簡單的PHP類分發Email資訊。 沒有服務容器,我們必須在使用它的時候手動的建立這個物件:

use Acme\HelloBundle\Mailer;
$mailer = new Mailer('sendmail');
$mailer->send('ryan@foobar.net',...);

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

在容器中 建立/配置服務

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

YAML格式:

#app/config/config.yml
services:
    my_mailer:
        class:     Acme\HelloBundle\Mailer
        arguments: [sendmail]

XML格式:

<!-- app/config/config.xml-->
<services>
    <service id="my_mailer" class="Acme\HelloBundle\Mailer">
        <argument>sendmail</argument>
    </service>
</services>

PHP程式碼格式:

//app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
$container->setDefinition('my_mailer',new Definition(
    'Acme\HelloBundle\Mailer',
        array('sendmail')
));

當Symfony2 初始化時,它會預設根據應用程式配置(app/config/config.yml)建立服務容器。真正的服務容器配置檔案是AppKernel::registerContainerConfiguration()方法載入的一個環境特定配置檔案,config_dev.yml 用於開發階段,config_prod.yml用於運營階段。
Symfony2配置啟動後,Acme\HelloBundle\Mailer的物件例項就能夠通過服務容器使用了。服務容器可以在任何傳統的Symfony2的controller中使用,通過簡寫方法 get()呼叫。

複製程式碼
class HelloController extends Controller
{
   //...
   public function sendEmailAction()
   {
      //...
      $mailer = $this->get('my_mailer');
      $mailer->send('ryan@foobar.net',...);
   }
}
複製程式碼

當我們從服務容器中獲取Mailer例項時,使用my_mailer. 容器會建立這個物件並返回它。有另外一個好處,服務直到用到它時才會建立。這樣能夠節省記憶體提高程式效率。

 

服務引數:

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

YAML格式:

複製程式碼
#app/config/config.yml
parameters:
   my_mailer.class:     Acme\HelloBundle\Mailer
   my_mailer.transport: sendmail

services:
   my_mailer:
      class:     %my_mailer.class%
      arguments: [%my_mailer.transport%]
複製程式碼

XML格式:

複製程式碼
<!-- app/config/config.xml -->
<parameters>
    <parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>
    <parameter key="my_mailer.transport">sendmail</parameter>
</parameters>

<services>
    <service id="my_mailer" class="%my_mailer.class%">
        <argument>%my_mailer.transport%</argument>
    </service>
</services>
複製程式碼

PHP程式碼格式:

複製程式碼
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;

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

$container->setDefinition('my_mailer', new Definition(
    '%my_mailer.class%',
    array('%my_mailer.transport%')
));
複製程式碼

上面配置檔案中,%% 是引數定義方式,當容器建立完成後就會查詢引數定義,如果%引數或者變數作為字串一部分時,需要新增另一個%進行轉換:

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

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

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

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

 

陣列引數:

引數不一定都是普通字串,也有可能是陣列。如果是寫在XML格式的配置檔案中,那麼你需要為陣列引數定義type="collection" 屬性。

YAML格式:

# app/config/config.yml
parameters:
    my_mailer.gateways:
        - mail1
        - mail2
        - mail3
    my_multilang.language_fallback:
        en:
            - en
            - fr
        fr:
            - fr
            - en

XML格式:

複製程式碼
<!-- app/config/config.xml -->
<parameters>
    <parameter key="my_mailer.gateways" type="collection">
        <parameter>mail1</parameter>
        <parameter>mail2</parameter>
        <parameter>mail3</parameter>
    </parameter>
    <parameter key="my_multilang.language_fallback" type="collection">
        <parameter key="en" type="collection">
            <parameter>en</parameter>
            <parameter>fr</parameter>
        </parameter>
        <parameter key="fr" type="collection">
            <parameter>fr</parameter>
            <parameter>en</parameter>
        </parameter>
    </parameter>
</parameters>
複製程式碼

PHP程式碼格式:

複製程式碼
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;

$container->setParameter('my_mailer.gateways', array('mail1', 'mail2', 'mail3'));
$container->setParameter('my_multilang.language_fallback',
                         array('en' => array('en', 'fr'),
                               'fr' => array('fr', 'en'),
                        ));
複製程式碼

 

匯入其它容器配置資源:

在這裡我們把服務配置檔案看成資源。這裡就是想說明一個事實,幾乎所有的配置資源都是檔案,如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格式:

複製程式碼
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
    my_mailer.class:      Acme\HelloBundle\Mailer
    my_mailer.transport:  sendmail

services:
    my_mailer:
        class:        %my_mailer.class%
        arguments:    [%my_mailer.transport%]
複製程式碼

XML格式:

複製程式碼
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
    <parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>
    <parameter key="my_mailer.transport">sendmail</parameter>
</parameters>

<services>
    <service id="my_mailer" class="%my_mailer.class%">
        <argument>%my_mailer.transport%</argument>
    </service>
</services>
複製程式碼

PHP程式碼格式:

複製程式碼
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;

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

$container->setDefinition('my_mailer', new Definition(
    '%my_mailer.class%',
    array('%my_mailer.transport%')
));
複製程式碼


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

YAML格式:

# app/config/config.yml
imports:
    - { resource: @AcmeHelloBundle/Resources/config/services.yml }

XML格式:

<!-- app/config/config.xml -->
<imports>
    <import resource="@AcmeHelloBundle/Resources/config/services.xml"/>
</imports>

PHP程式碼格式:

// app/config/config.php
$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格式:

複製程式碼
# app/config/config.yml
framework:
    secret:          xxxxxxxxxx
    charset:         UTF-8
    form:            true
    csrf_protection: true
    router:        { resource: "%kernel.root_dir%/config/routing.yml" }
    # ...
複製程式碼

XML格式:

複製程式碼
<!-- app/config/config.xml -->
<framework:config charset="UTF-8" secret="xxxxxxxxxx">
    <framework:form />
    <framework:csrf-protection />
    <framework:router resource="%kernel.root_dir%/config/routing.xml" />
    <!-- ... -->
</framework>
複製程式碼

PHP程式碼格式:

複製程式碼
// app/config/config.php
$container->loadFromExtension('framework', array(
    'secret'          => 'xxxxxxxxxx',
    'charset'         => 'UTF-8',
    'form'            => array(),
    'csrf-protection' => array(),
    'router'          => array('resource' => '%kernel.root_dir%/config/routing.php'),
    // ...
));
複製程式碼

當這個配置被解析時,容器會查詢一個可以處理framework配置命令的擴充套件。這個擴充套件在FrameworkBundle中,它會被呼叫來為FrameworkBundle載入服務配置。如果你從你的配置檔案中完全去掉framework鍵,Symfony 核心服務將不會被載入。這完全是由你控制的。

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

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

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


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

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

複製程式碼
namespace Acme\HelloBundle\Newsletter;

use Acme\HelloBundle\Mailer;

class NewsletterManager
{
    protected $mailer;

    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}
複製程式碼

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

public function sendNewsletterAction()
{
    $mailer = $this->get('my_mailer');
    $newsletter = new Acme\HelloBundle\Newsletter\NewsletterManager($mailer);
    // ...
}


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

YAML格式:

複製程式碼
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
    # ...
    newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager

services:
    my_mailer:
        # ...
    newsletter_manager:
        class:     %newsletter_manager.class%
        arguments: [@my_mailer]
複製程式碼

XML格式:

複製程式碼
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
    <!-- ... -->
    <parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter>
</parameters>

<services>
    <service id="my_mailer" ... >
      <!-- ... -->
    </service>
    <service id="newsletter_manager" class="%newsletter_manager.class%">
        <argument type="service" id="my_mailer"/>
    </service>
</services>
複製程式碼

PHP程式碼格式:

複製程式碼
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

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

$container->setDefinition('my_mailer', ... );
$container->setDefinition('newsletter_manager', new Definition(
    '%newsletter_manager.class%',
    array(new Reference('my_mailer'))
));
複製程式碼

在YAML配置檔案中,@my_mailer告訴容器去查詢一個名叫my_mailer的服務物件並把它傳遞給NewsletterManager的建構函式。在這種情況下,指定的服務my_mailer必須存在。如果不存在,將會丟擲異常。你可以把你的依賴標記為可選,接下來說明。

 

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

複製程式碼
namespace Acme\HelloBundle\Newsletter;

use Acme\HelloBundle\Mailer;

class NewsletterManager
{
    protected $mailer;

    public function setMailer(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}
複製程式碼

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

YAML格式:

複製程式碼
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
    # ...
    newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager

services:
    my_mailer:
        # ...
    newsletter_manager:
        class:     %newsletter_manager.class%
        calls:
            - [ setMailer, [ @my_mailer ] ]
複製程式碼

XML格式:

複製程式碼
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
    <!-- ... -->
    <parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter>
</parameters>

<services>
    <service id="my_mailer" ... >
      <!-- ... -->
    </service>
    <service id="newsletter_manager" class="%newsletter_manager.class%">
        <call method="setMailer">
             <argument type="service" id="my_mailer" />
        </call>
    </service>
</services>
複製程式碼

PHP程式碼格式:

複製程式碼
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

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

$container->setDefinition('my_mailer', ... );
$container->setDefinition('newsletter_manager', new Definition(
    '%newsletter_manager.class%'
))->addMethodCall('setMailer', array(
    new Reference('my_mailer')
));
複製程式碼


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

YAML格式:

複製程式碼
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
    # ...

services:
    newsletter_manager:
        class:     %newsletter_manager.class%
        arguments: [@?my_mailer]
複製程式碼

XML格式:

複製程式碼
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->

<services>
    <service id="my_mailer" ... >
      <!-- ... -->
    </service>
    <service id="newsletter_manager" class="%newsletter_manager.class%">
        <argument type="service" id="my_mailer" on-invalid="ignore" />
    </service>
</services>
複製程式碼

PHP程式碼格式:

複製程式碼
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerInterface;

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

$container->setDefinition('my_mailer', ... );
$container->setDefinition('newsletter_manager', new Definition(
    '%newsletter_manager.class%',
    array(new Reference('my_mailer', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))
));
複製程式碼

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

public function __construct(Mailer $mailer = null)
{
    // ...
}

 

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

複製程式碼
public function indexAction($bar)
{
    $session = $this->get('session');
    $session->set('foo', $bar);

    // ...
}
複製程式碼

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

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

複製程式碼
namespace Acme\HelloBundle\Newsletter;

use Symfony\Component\Templating\EngineInterface;

class NewsletterManager
{
    protected $mailer;

    protected $templating;

    public function __construct(\Swift_Mailer $mailer, EngineInterface $templating)
    {
        $this->mailer = $mailer;
        $this->templating = $templating;
    }

    // ...
}
複製程式碼

配置服務容器:

YAML格式:

services:
    newsletter_manager:
        class:     %newsletter_manager.class%
        arguments: [@mailer, @templating]

XML格式:

<service id="newsletter_manager" class="%newsletter_manager.class%">
    <argument type="service" id="mailer"/>
    <argument type="service" id="templating"/>
</service>

PHP程式碼格式:

複製程式碼
$container->setDefinition('newsletter_manager', new Definition(
    '%newsletter_manager.class%',
    array(
        new Reference('mailer'),
        new Reference('templating')
    )
));
複製程式碼

 

高階容器配置:

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

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

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

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

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

配置形式如下:

YAML格式:

services:
   foo:
     class: Acme\HelloBundle\Foo
     public: false

XML格式:

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

PHP程式碼格式:

$definition = new Definition('Acme\HelloBundle\Foo');
$definition->setPublic(false);
$container->setDefinition('foo', $definition);

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

$container->get('foo');

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

 

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

YAML格式:

services:
   foo:
     class: Acme\HelloBundle\Foo
   bar:
     alias: foo

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo"/>
<service id="bar" alias="foo" />

PHP程式碼格式:

$definition = new Definition('Acme\HelloBundle\Foo');
$container->setDefinition('foo', $definition);

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

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

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

 

要求必備檔案:
有些情況下,你需要在載入服務前包含其它檔案,這時你可以使用file命令實現:

YAML格式:

services:
   foo:
     class: Acme\HelloBundle\Foo\Bar
     file: %kernel.root_dir%/src/path/to/file/foo.php

PHP程式碼格式:

$definition = new Definition('Acme\HelloBundle\Foo\Bar');
$definition->setFile('%kernel.root_dir%/src/path/to/file/foo.php');
$container->setDefinition('foo', $definition);

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo\Bar">
    <file>%kernel.root_dir%/src/path/to/file/foo.php</file>
</service>

注意:Symfony將在內部呼叫PHP函式require_once,這就意味著你的檔案將每個請求都會被包括一次。

 

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

YAML格式:

services:
    foo.twig.extension:
        class: Acme\HelloBundle\Extension\FooExtension
        tags:
            -  { name: twig.extension }

XML格式:

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

PHP程式碼格式:

$definition = new Definition('Acme\HelloBundle\Extension\FooExtension');
$definition->addTag('twig.extension');
$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

相關文章