Symfony 服務容器入門

譯文首發於 Symfony 服務容器入門,轉載請註明出處。

本文是依賴注入(Depeendency Injection)系列教程的第 3 篇文章,本系列教程主要講解如何使用 PHP 實現一個輕量級服務容器,教程包括:php


術語

  • Depeendency Injection 譯做 依賴注入
  • Depeendency Injection Container 譯做 依賴注入容器
  • Container 譯做 容器
  • Service Container 譯做 服務容器
  • Session 譯做 會話
  • Object-Oriented 譯做 面向對象
  • mock 譯做 模擬
  • anti-patterns 譯做 反模式
  • hardcoded 譯做 硬編碼

從本系列的開篇到如今咱們基本仍是圍繞「依賴注入」的基本概念展開的。前兩篇入門的文章對於理解本文及後續教程相當重要。如今,是時候該去探索 Symfony 2 服務容器是如何實現這個主題了。html

Symfony 中的「依賴注入容器」定義的類名爲「sfServiceContainer」。這是一個很是輕量級的類,實現了 [上一篇]() 文章中講解到的基本功能。數組

Symfony 服務容器能夠到官方 Svn 版本庫中得到: http://svn.symfony-project.co...。注意, Symfony 組件依舊保持更新,這也意味着它的實現可能與本文有所出入。(譯註: @todo)

在 Symfony 中,任何服務的實例都有容器管理。前一篇文章中提到的 Zend_Mail 實例中,就須要使用到兩個服務:mailer 服務和 mail_transport 服務。性能優化

<?php

class Container
{
    static protected $sharged = array();

    protected $parameters = array();

    public function __construct(array $parameters = array())
    {
        $this->parameters = $parameters;
    }

    public function getMailTransport()
    {
        return new Zend_Mail_Transport_Smtp('smpt.gmail.com', array(
            'auth'     => 'login',
            'username' => $this->parameters['mailer.username'],
            'password' => $this->parameters['mailer.password'],
            'ssl'      => 'ssl',
            'port'     => 465,
        ));
    }

    public function getMailer()
    {
        if (isset(self::$shared['mailer'])) {
            return self::$shared['mailer'];
        }

        $class = $this->parameters['mailer.class'];

        $mailer = new $class();
        $mailer->setDefaultTransport($this->getMailTransport());

        return self::$shared['mailer'] = $mailer;
    }
}

讓容器類 Container 繼承 sfServiceContainer 類的話,能夠有效精簡代碼:ide

<?php

class Container extends sfServiceContainer
{
    static protected $shared = array();

    protected function getMailTransportService()
    {
        return new Zend_Mail_Transport_Smtp('smpt.gmail.com', array(
            'auth'     => 'login',
            'username' => $this['mailer.username'],
            'password' => $this['mailer.password'],
            'ssl'      => 'ssl',
            'port'     => 465,
        ));
    }

    protected function getMailerService()
    {
        if (isset(self::$shared['mailer']))
        {
            return self::$shared['mailer'];
        }

        $class = $this['mailer.class'];

        $mailer = new $class();
        $mailer->setDefaultTransport($this->getMailTransportService());

        return self::$shared['mailer'] = $mailer;
    }
}

彷佛與以前相差無幾,但經過繼承 spServiceProvider 的容器類擁有更多功能而且代碼更整潔。這裏列幾點主要的異同點:svn

  • 定義的方法名加上了 Service 後綴名。依據慣例優先原則,一個服務方法的定義由 get 前綴和 Service 綴名共同組成。每一個服務同時定義惟一的標識符,標識符命名規則爲去除先後綴的方法名而且採用「下劃線命名法」命名。好比咱們在容器中定義一個 getMailTransportServer() 方法,容器同時會定義一個名爲 mail_transport 的服務標識符。
  • 全部定義的方法改成 protected 可見範圍修飾符。稍後會講解如何從容器獲取相關服務。
  • 容器能夠像數組同樣直接獲取參數值($this['mailer.class'])。

一個服務標識符必須惟一,而且僅能夠包含字母、數字、下劃線和 .(英文點號). 號在容器內的功能相似於「命名空間」(如 mail.mailermail.transport 實例)。性能

接下來是如何使用新的容器類:優化

<?php
require_once 'PATH/TO/sf/lib/sfServiceContainerAutoloader.php';
sfServiceContainerAutoloader::register();

$sc = new Container(array(
  'mailer.username' => 'foo',
  'mailer.password' => 'bar',
  'mailer.class'    => 'Zend_Mail',
));

$mailer = $sc->mailer;

如今,因爲咱們繼承 spServiceContainer 容器類,咱們能夠使用更爲整潔的接口功能:ui

  • 服務能夠有統一的接口訪問:
<?php

if ($sc->hasService('mailer')) {
    $mailer = $sc->getService('mailer'); 
}

$sc->setService('mailer', $mailer);
  • 或者,直接經過類的成員變量獲取服務:
<?php
if (isset($sc->mailer)) {
    $mailer = $sc->mailer;
}

$sc->mailer = $mailer;
  • 參數名也能經過統一的接口訪問:
<?php
if (!$sc->hasParameter('mailer_class')) {
    $sc->setParameter('mailer_class', 'Zend_Mail');
}

echo $sc->getParameter('mailer_class');

// 重寫容器全部參數
$sc->setParameters($parameters);

// 向容器添加參數
$sc->addParameters($parameters);
  • 或者,直接經過類的成員變量已相似數組的方式獲取:
<?ph
if (!isset($sc['mailer.class'])) {
    $sc['mailer.class'] = 'Zend_Mail';
}

$mailerClass = $sc['mailer.class'];
  • 還能夠迭代獲取容器內全部服務:
<?php
foreach ($sc as $id => $service)
{
    echo sprintf("Service %s is an instance of %s.\n", $id, get_class($service));
}

當項目容器須要管理不太多的服務時,經過繼承 spServiceContainer 類是很是明智的選擇;即便,這樣依舊須要處理大量的基礎工做或直接從已有項目中複製代碼過來。而當系統引入大量的服務時,咱們就須要使用更好的方法來組織和管理這些服務。this

這就是爲何多數時候咱們並不會直接使用 spServiceContainer 類的緣由。可是咱們花這個時間來說解 spServiceContainer 類的用法的理由是,它是 Symfony 依賴注入容器實現的基石。

下一篇文章,咱們未來看看能夠簡化服務定義過程的 sfServiceContainerBuilder 類。

原文: http://fabien.potencier.org/i...

相關文章
相關標籤/搜索