譯文首發於 Symfony 服務容器入門,轉載請註明出處。
本文是依賴注入(Depeendency Injection)系列教程的第 3 篇文章,本系列教程主要講解如何使用 PHP 實現一個輕量級服務容器,教程包括:php
從本系列的開篇到如今咱們基本仍是圍繞「依賴注入」的基本概念展開的。前兩篇入門的文章對於理解本文及後續教程相當重要。如今,是時候該去探索 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
一個服務標識符必須惟一,而且僅能夠包含字母、數字、下劃線和 .(英文點號)。. 號在容器內的功能相似於「命名空間」(如 mail.mailer 和 mail.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 類。