导读
这一系列的文章讲述了依赖注入概念和一个轻量级的PHP实现。
目录
- Part 1: 什么是依赖注入
- Part 2: 你是否需要依赖注入容器
- Part 3: Symfony服务容器介绍
- Part 4: Symfony服务容器: 使用Builder构建服务(待翻译)
- Part 5: Symfony服务容器: 使用XML或YAML描述服务(待翻译)
- Part 6: 速度的需要(待翻译)
正文
这个系列到目前为止,我们已经介绍了依赖注入的一般概念。前两篇介绍性的文章可以帮助我们更好的理解本篇文章及接下来的几篇文章中要讲述的依赖注入实现的内容。现在我们开始讨论Symfony 2服务容器的实现。
Symfony通过一个叫做sfServiceContainer的类来管理依赖注入容器。这是一个非常轻量的类,它实现了我们上一篇文章讨论过的基本功能。
Symfony服务容器可以作为独立的组件在其官方Subversion库来获取:点击。
值得注意的是:这个组件仍然处于繁重的开发阶段,这意味着目前的实现有可能会发生改变。
按照Symfony的定义,一个服务可以是任意的被容器管理的对象。在上篇文章的Zend_Mail示例中,我们有两个服务:mailer服务和mail__transport服务:
class Container
{
static protected $shared = array();
protected $parameters = array();
public function __construct(array $parameters = array())
{
$this->parameters = $parameters;
}
public function getMailTransport()
{
return new Zend_Mail_Transport_Smtp('smtp.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类,我们就可以稍微简化一下上面的代码:
class Container extends sfServiceContainer
{
static protected $shared = array();
protected function getMailTransportService()
{
return new Zend_Mail_Transport_Smtp('smtp.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;
}
}
这还不够好,但是可以给我们提供一个更加清晰有力的接口。这是主要的修改:
- 方法名以Service为后缀。按照约定,获得服务实例的方法名的前缀是get,后缀是Service。每一个服务都有唯一的标识符,就是去掉前缀和后缀的方法名的下划线版本。通过定义方法getMailTransportService(),我们定义了一个名为mail_transport的服务。
- 方法的属性是protected。稍后我们会看到在容器内部如何检索这些服务。
- 容器可以被当作数组来获取相应参数的值($this['mailer.class'])。
服务标识符必须是唯一的,并且只能包含字母,数字,下划线和点号。点号可以用来定义容器内部的“命名空间”(例如mail.mailer和mail.transport)。
我们来看一下如何使用这一新的容器类:
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;
由于Container类继承了sfServiceContainer类,我们可以享用一个更加清爽的接口:
-
通过统一的接口访问服务
if ($sc->hasService('mailer')) { $mailer = $sc->getService('mailer'); } $sc->setService('mailer', $mailer);
-
更快捷的访问服务的方式是:通过类属性
if (isset($sc->mailer)) { $mailer = $sc->mailer; } $sc->mailer = $mailer;
-
参数可以通过统一的接口访问
if (!$sc->hasParameter('mailer_class')) { $sc->setParameter('mailer_class', 'Zend_Mail'); } echo $sc->getParameter('mailer_class'); // Override all parameters of the container $sc->setParameters($parameters); // Adds parameters $sc->addParameters($parameters);
-
更便捷的访问参数的方式是:把容器当作数组
if (!isset($sc['mailer.class'])) { $sc['mailer.class'] = 'Zend_Mail'; } $mailerClass = $sc['mailer.class'];
-
也可迭代遍历容器的所有服务
foreach ($sc as $id => $service) { echo sprintf("Service %s is an instance of %s.\n", $id, get_class($service)); }
即使你仍然需要自己做大量基础工作并且复制大量的代码,在有少量的服务需要管理时,使用sfServiceContainer类仍然是非常有帮助的。但是,当要管理的服务开始暴涨时,我们就需要更好的方式来描述这些服务了。
这也是为什么大多数时候你不会直接使用sfServiceContainer类。然而做为Symfony依赖注入容器实现的基石,依然值得投入一些时间来搞清楚它。
在下篇文章中我们要讨论一下sfServiceContainerBuilder类,它使得服务定义的过程变得简单。
转载注明出处:
http://zengfeng.info/2016/05/symfony服务容器介绍
网友评论