Symfony 服務容器:使用建造者建立服務

本文首發於 Symfony 服務容器:使用建造者建立服務,轉載請註明出處。

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


術語

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

Symfony 服務容器入門 一文中,咱們學習瞭如何經過繼承 sfServiceContainer 類實現一個更增強大的自定義服務容器。這篇文件將會進一步講解,如何經過使用 PHP 代碼來設置服務配置選項供 spServiceContainerBuilder 建造者類建立服務。html

SVN 版本庫有更新。若是您已經檢出版本,能夠執行更新操做。若是尚未檢出,可從 http://svn.symfony-project.co... 檢出版本(譯註:不用試了,這個文章寫做年代比較久遠已經再也不維護 SVN 版本了)。

spServiceContainerBuilder 一樣繼承自 sfServiceContainer 基類,它提供了設置服務配置選項接口供開發者使用。數組

服務容器接口

全部「服務容器」類都繼承自 sfServiceContainerInterface 接口:性能優化

<?php
interface sfServiceContainerInterface
{
    public function setParameters(array $parameters);
    public function addParameters(array $parameters);
    public function getParameters();
    public function getParameter($name);
    public function setParameter($name, $value);
    public function hasParameter($name);
    public function setService($id, $service);
    public function getService($id);
    public function hasService($name);
}

服務的描述經過註冊服務定義來完成。每一個服務定義都描述了一個服務:從要使用的類、到傳遞給構造函數的參數、以及一堆其餘配置屬性(請參見下文 sfServiceDefinition)。session

接下來刪除掉 Zend_Mail 中全部硬編碼代碼,並使用構建器動態重寫:svn

<?php

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

$sc = new sfServiceContainerBuilder();

$sc->
  register('mail.transport', 'Zend_Mail_Transport_Smtp')->
  addArgument('smtp.gmail.com')->
  addArgument(array(
    'auth'     => 'login',
    'username' => '%mailer.username%',
    'password' => '%mailer.password%',
    'ssl'      => 'ssl',
    'port'     => 465,
  ))->
  setShared(false)
;

$sc->
  register('mailer', '%mailer.class%')->
  addMethodCall('setDefaultTransport', array(new sfServiceReference('mail.transport')))
;

經過調用 register() 方法建立一個 sfServiceDefinition 實例,方法接收服務名和類名兩個參數。函數

服務定義在內部將實例化爲 sfServiceDefinition 實例。也能夠手動建立一個實例,並使用服務容器 setServiceDefinition() 方法直接註冊它。

定義對象支持鏈式操做,同時提供配置服務所需的方法。在上面的例子中,咱們使用瞭如下幾個方法:工具

  • addArgument():向待建立服務的構造函數添加參數;
  • setShared():是否啓用單例模式建立服務(默認爲:true)。
  • addMethodCall():建立服務後的回調方法。第二個參數是傳遞給該方法的參數數組。

這樣一個 sfServiceReference 實例被添加到咱們所需使用的服務裏中。當這個特殊的類被調用時將被動態的替換成所須要的服務實例。性能

在註冊階段實際上沒有建立服務實例,只是定義了服務的描述。這些服務只有在您真正想要使用它們時纔會建立。這意味着您能夠以任何順序註冊服務,而無需考慮它們之間的依賴關係。這也意味着您能夠經過從新註冊具備相同名稱的服務來覆蓋現有的服務定義。同時,這種定義服務的方法也更易於測試。學習

sfServiceDefinition 類

這個類有諸多可用於修改建立和配置的方法:

  • setConstructor():設置建立服務的靜態方法,而不是經過 new 關鍵字實例對象(結合工廠方法效果更佳);
  • setClass():設置服務類名;
  • setArguments():設置傳遞給構造函數的參數(注意數組中元素的順序);
  • addArgument():爲構造函數添加一個參數;
  • setMethodCalls():設置服務建立後回調方法組。調用順序同設置順序相同;
  • addMethodCall():添加服務建立後的回調方法。若是須要,您能夠屢次向同一個方法添加個調用;
  • setFile():設置待建立服務裏的引入文件名(若是未啓用自動加載機制,須要使用該方法引入文件);
  • setShared():是否啓用單例模式建立服務(默認爲:true);
  • setConfigurator():設置服務配置完成後的回調函數。

因爲 sfServiceContainerBuilder 類實現了 sfServiceContainerInterface 接口,因此能夠像以前同樣使用容器中的方法:

<?php
$sc->addParameters(array(
  'mailer.username' => 'foo',
  'mailer.password' => 'bar',
  'mailer.class'    => 'Zend_Mail',
));

$mailer = $sc->mailer;

sfServiceContainerBuilder 類可以描述任何對象是如何實例化和配置的。咱們已經在 Zend_Mail 實例中使用了容器,下面是另外一個相關示例:

$sc = new sfServiceContainerBuilder(array(
  'storage.class'        => 'sfMySQLSessionStorage',
  'storage.options'      => array('database' => 'session', 'db_table' => 'session'),
  'user.class'           => 'sfUser',
  'user.default_culture' => 'en',
));

$sc->register('dispatcher', 'sfEventDispatcher');

$sc->
  register('storage', '%storage.class%')->
  addArgument('%storage.options%')
;

$sc->
  register('user', '%user.class%')->
  addArgument(new sfServiceReference('dispatcher'))->
  addArgument(new sfServiceReference('storage'))->
  addArgument(array('default_culture' => '%user.default_culture%'))->
;

$user = $sc->user;
在這個 Symfony 示例中,即便存儲對象將一個選項數組做爲參數,咱們也會傳遞一個字符串佔位符(addArgument('%storage.options%'))。容器功能強大到支持傳遞一個置爲佔位符的數組。

以上就是今天的所有內容。使用 PHP 代碼來描述服務很是簡單並且功能強大。它爲您提供了一個建立容器的工具,無需複製過多的代碼,並抽象了對象實例化過程和配置。在下一篇文章中,咱們將看到如何使用 XML 或 YAML 文件來描述服務。敬請期待吧!

若是以爲本文對您有幫助,請隨手點贊加收藏,謝謝!

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

相關文章
相關標籤/搜索