译文首发于 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 类。