理解PHP依赖注入容器(dependency injection container)系列(四) Symfony服务容器:利用Builder创建服务

上篇讲述了利用继承sfServiceContainer类,为容器提供简单易用的接口调用。接下来我们会讲到如何进一步利用sfServiceContainerBuilder 类描述服务和配置。
sfServiceContainerBuilder是继承sfServiceContainer类的(每一个容器类如sfServiceContainer类都实现了sfServiceContainerInterface接口),它使开发者可以通过一种简洁易用的方式来描述服务。

服务的描述是通过注册服务定义完成的。每个服务定义描述了一个服务:从使用构造函数传递参数的类,和一堆其他的配置属性(见下面的sfservicedefinition边栏)

再回到Zend_Mail这个例子,现在我们要把其中的硬编码去除掉,并利用builder类动态创建它

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()方法我们完成了对服务的创建。只需要给出service name和class name,该方法返回sfServiceDefinition实例。

服务定义在内部是通过sfservicedefinition对像实现的。也可以手工创建,再直接利用容器的setservicedefinition()方法登记。

sfServiceDefinition对像实现了一个接口并且提供了几个能对服务进行配置的方法,在上面的例子中,我们使用了这几个方法:

  • addArgument():给服务的构造函数传递一个参数。
  • setShared():在当前容器中,该服务是否唯一(默认情况下为true)
  • addMethodCall():设置服务被创建后触发的委托事件。第一个参数是要委托调用的方法,第二个参数是向方法传递的参数数组

sfServiceDefinition 的相关方法:

  • setConstructor():设置创建服务时使用的静态方法,用来替换标准的new __construct方式(一般用在对像工厂)
  • setClass:设置服务类
  • setArguments(): 为构造函数设置多个参数(顺序很关键)
  • addArgument(): 为构造函数设置一个参数
  • setMethodCalls():设置服务被创建后触发的委托事件方法簇,按造顺序执行。
  • addMethodCall():设置服务被创建后触发的委托事件,允许多次添加同一个方法。
  • setFile(): 设置一个文件包含路径,该文件将在创建服务前被引用include(在服务没有实现自动加载文件时有用)
  • setShared():在当前容器中,该服务是否唯一(默认情况下为true)
  • setConfigurator():设置委托方法,在服务配置完成后调用。

现在引用一个服务是通过实例化sfServiceReference类来实现的(如:new sfServiceReference(‘mail.transport’) .)。这个特殊的类可以根据参数动态的创建服务。
在注册过程中,服务并没有真正被创建,仅仅是描述了如何获取并配置该服务。只有在真正使用服务时,服务才会被实例化。这就是说,你可以在任何顺序任何位置创建注册你的服务,而不用担心服务依赖的对像是否在它之前注册。这也意味着后面注册的服务如果名字相同,则可以覆盖前面的服务。在测试时,需要重写一些方法时这不失为一个好的办法。

sfServiceContainerBuilder类实现了标准sfServiceContainerInterface接口。来看一下接口定义:

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);
}

我们使用容器可以不用改变代码:

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

$mailer = $sc->mailer;

sfServiceContainerBuilder 可以描述如何实例化、配置对像。上面的Zend_Mail例子已经展示了这一切,现在我们看另一个来源于Symfony的例子,这个例子用到了sfUser类:

$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;

容器参数的存储对像是通过数组传递的时侯,如果我们要使用某个参数,就要用到占位符(addArgument(‘%storage.options%’))

下一篇将介绍如何用 XML or YAML文件描述服务。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值