本文首發於 Symfony 服務容器:使用 XML 或 YAML 檔案描述服務,轉載請註明出處。
本文是依賴注入(Depeendency Injection)系列教程的第 5 篇文章,本系列教程主要講解如何使用 PHP 實現一個輕量級服務容器,教程包括:
- 第 1 篇:什麼是依賴注入?
- 第 2 篇:是否需要使用依賴注入容器?
- 第 3 篇:Symfony 服務容器入門
- 第 4 篇:Symfony 服務容器:使用建造者建立服務
- 第 5 篇:Symfony 服務容器:使用 XML 或 YAML 檔案描述服務
- 第 6 篇:效能優化
術語
- Depeendency Injection 譯作 依賴注入
- Depeendency Injection Container 譯作 依賴注入容器
- Container 譯作 容器
- Service Container 譯作 服務容器
- Session 譯作 會話
- Object-Oriented 譯作 物件導向
- mock 譯作 模擬
- anti-patterns 譯作 反模式
- hardcoded 譯作 硬編碼
- dumper 譯作 轉存器
- loader 譯作 載入器
上一篇文章 Symfony 服務容器:使用建造者建立服務 帶領大家學習了使用 spServiceContainerBuilder 類描述待建立的服務功能。今天,我們將學習如何使用 loader 和 dumper 結合 XML 或 YAML 檔案描述待建立服務。
SVN 版本庫有更新,如果您之前有檢出版本庫,請更新。如果還沒有檢出可到 http://svn.symfony-project.co... 檢出(譯註:該版本庫已停止維護)。
Symfony 依賴注入元件提供載入服務的輔助類。預設元件包含兩種載入器:sfServiceContainerLoaderFileXml 用於載入 XML 檔案;sfServiceContainerLoaderFileYaml 用於載入 YAML 檔案。
在講解 XML 和 YAML 配置檔案使用之前,先來看下 Symfony 提供的另外一個依賴注入元件:dumper objects。服務轉存器接收一個容器物件並將該物件轉換成其它格式。當然,這個元件也可以用於 XML 和 YAML 檔案的打包處理。
為了講解 XML 配置檔案使用方法,我們將之前使用 PHP 程式碼描述服務的定義過程,通過使用 sfServiceContainerDumperXml 轉存器 從 container.xml 配置進行定義。
下面是之前定義 Zend_Mail 服務的實現:
<?php
require_once '/PATH/TO/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')))
;
使用下面的程式碼將這個服務容器轉存為 XML 檔案:
$dumper = new sfServiceContainerDumperXml($sc);
file_put_contents('/somewhere/container.xml', $dumper->dump());
「轉存器」類建構函式第一個引數接受一個服務容器,方法 dump() 可以將這個服務容器轉成其它格式。執行正常的話將會生成類似下方資料的 container.xml 檔案:
<container xmlns="http://symfony-project.org/2.0/container">
<parameters>
<parameter key="mailer.username">foo</parameter>
<parameter key="mailer.password">bar</parameter>
<parameter key="mailer.class">Zend_Mail</parameter>
</parameters>
<services>
<service id="mail.transport" class="Zend_Mail_Transport_Smtp" shared="false">
<argument>smtp.gmail.com</argument>
<argument type="collection">
<argument key="auth">login</argument>
<argument key="username">%mailer.username%</argument>
<argument key="password">%mailer.password%</argument>
<argument key="ssl">ssl</argument>
<argument key="port">465</argument>
</argument>
</service>
<service id="mailer" class="%mailer.class%">
<call method="setDefaultTransport">
<argument type="service" id="mail.transport">
</argument></call>
</service>
</services>
</container>
XML 格式支援匿名服務。匿名服務無需定義服務名稱,可直接在使用的上下文環境中定義。當某個服務僅在某個作用域範圍內使用時,使用匿名服務會非常方便:
<service id="mailer" class="%mailer.class%">
<call method="setDefaultTransport">
<argument type="service">
<service class="Zend_Mail_Transport_Smtp">
<argument>smtp.gmail.com</argument>
<argument type="collection">
<argument key="auth">login</argument>
<argument key="username"><span class="hljs-variable">%mailer</span>.username%</argument>
<argument key="password"><span class="hljs-variable">%mailer</span>.password%</argument>
<argument key="ssl">ssl</argument>
<argument key="port"><span class="hljs-number">465</span></argument>
</argument>
</service>
</argument>
</call>
</service>
使用這個 XML 配置檔案也非常簡單,僅需一個 XML 服務載入類即可完成:
require_once '/PATH/TO/sfServiceContainerAutoloader.php';
sfServiceContainerAutoloader::register();
$sc = new sfServiceContainerBuilder();
$loader = new sfServiceContainerLoaderFileXml($sc);
$loader->load('/somewhere/container.xml');
類似於轉存器,「載入器」的建構函式的第一個引數同為一個服務容器,「載入器」的 load() 方法能夠從檔案中讀取配置並完成將服務向「服務容器」的註冊功能。如此便可以正常使用服務容器了。
如果將 XML 轉存器替換為 sfServiceContainerDumperYaml 類,則會以 YAML 檔案生成配置檔案:
require_once '/PATH/TO/sfYaml.php';
$dumper = new sfServiceContainerDumperYaml($sc);
file_put_contents('/somewhere/container.yml', $dumper->dump());
上面的程式碼僅在首次載入 sfYAML 元件(http://svn.symfony-project.co...)時才能正常處理,因為它是服務容器載入器和轉存器必要的依賴。
生成的 YAML 檔案內容如下:
parameters:
mailer.username: foo
mailer.password: bar
mailer.class: Zend_Mail
services:
mail.transport:
class: Zend_Mail_Transport_Smtp
arguments: [smtp.gmail.com, { auth: login, username: %mailer.username%, password: %mailer.password%, ssl: ssl, port: 465 }]
shared: false
mailer:
class: %mailer.class%
calls:
- [setDefaultTransport, [@mail.transport]]
但使用 XML 配置比 YAML 配置有更多優勢:
- 當 XML 檔案被載入時,會使用內建的 services.xsd 檔案進行校驗;
- IDE 可自動補全 XML 檔案;
- XML 檔案相比 YAML 檔案效率更高;
- XML 格式無其它擴充套件依賴(YAML 格式依賴於 sfYAML 元件)。
當然,你也可以一起使用這些載入器和轉存器,將某種格式檔案轉存為另外一種:
// Convert an XML container service definitions file to a YAML one
$sc = new sfServiceContainerBuilder();
$loader = new sfServiceContainerLoaderFileXml($sc);
$loader->load('/somewhere/container.xml');
$dumper = new sfServiceContainerDumperYaml($sc);
file_put_contents('/somewhere/container.yml', $dumper->dump());
簡短截說,這裡不會列出所有 YAML 和 XML 格式的所有可能性。當然,你可以很容易學會如何使用這些轉存器和載入器。
使用 YAML 或 XML 配置檔案,可以讓我們能夠使用 GUI 工具建立服務。同時,也給我們帶來更多樂趣。
其一、也是最重要的一個功能就是提供引入資源的能力。一個資源可以是任何一種配置檔案:
<container xmlns="http://symfony-project.org/2.0/container">
<imports>
<import resource="default.xml">
</import></imports>
<parameters>
</parameters>
<services>
</services>
</container>
imports 節點所配置的檔案需要在其它配置節點之前引入。預設,會從當前檔案目錄查詢這個檔案並引入,你也可以通過「載入器」的第二個引數設定檔案查詢目錄:
$loader = new sfServiceContainerLoaderFileXml($sc, array('/another/path'));
$loader->load('/somewhere/container.xml');
甚至,可以在 XML 配置中,定義 YAML 載入器及 YAML 配置檔名:
<container xmlns="http://symfony-project.org/2.0/container">
<imports>
<import resource="default.yml" class="sfServiceContainerLoaderFileYaml">
</import></imports>
</container>
反過來也一樣:
imports:
- { resource: default.xml, class: sfServiceContainerLoaderFileXml }
imports 提供一種靈活的方式管理服務定義檔案。此外它提供了複用的可能。繼續我們之前說到的「會話」功能。當在測試環境下,會話儲存可能是一個模擬物件;相反,當使用負載均衡需要才多臺 Web 伺服器裡儲存會話資料,可能會使用類似 MySQL 資料庫進行儲存。此時,就需要一種基於配置的解決方案,並依據不同開發環境匯入所需配置:
<container xmlns="http://symfony-project.org/2.0/container">
<parameters>
<parameter key="session.class">sfSessionStorage</parameter>
</parameters>
</container>
<container xmlns="http://symfony-project.org/2.0/container">
<imports>
<import resource="session.xml">
</import></imports>
<parameters>
<parameter key="session.class">sfSessionTestStorage</parameter>
</parameters>
</container>
<container xmlns="http://symfony-project.org/2.0/container">
<imports>
<import resource="session.xml">
</import></imports>
<parameters>
<parameter key="session.class">sfMySQLSessionStorage</parameter>
</parameters>
</container>
使用時也異常簡單:
$loader = new sfServiceContainerLoaderFileXml($sc, array(
'/framework/config/default/',
'/project/config/',
));
$loader->load('/somewhere/session_'.$environment.'.xml');
也許有的朋友在面對 XML 配置檔案時會留下傷心的淚水,因為 XML 檔案也許是世上最難以閱讀的資料格式。有 Symfony 開發經驗的朋友或許已經能夠輕鬆編寫 YAML 格式配置檔案。更高階一些,我們還可以將服務定義從一個檔案中分離出來。我們可以將服務定義在 services.xml 檔案中,並將它所需的引數定義到 parameters.xml 檔案內。或者,在 parameters.yml 檔案中定義所需的引數配置。此外,我們還提供一個內建的 INI 檔案載入器,它能夠從標準 INI 檔案讀取配置引數:
<container xmlns="http://symfony-project.org/2.0/container">
<imports>
<import resource="config.ini" class="sfServiceContainerLoaderFileIni">
</import></imports>
</container>
以上示例僅涉及「載入器」和「轉存器」基本使用,但我希望您已經瞭解到 XML 和 YAML 配置檔案的強大。對於哪些對服務容器及需要載入太多配置檔案的效能持懷疑態度的開發者,下一篇文章或許會讓他們改變自己的觀點。由於下一篇文章是系列文章的終章,我還將討論服務依賴視覺化相關內容。