上篇讲述了利用继承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文件描述服务。