如何操作“服务定义”对象

3.4 版本
维护中的版本

最重要文档 译注:本文乍看之下并不复杂,但却活用在所有第三方bundle中(也包括Symfony框架本身)。实践本文就是贯穿整个DIC的过程,无限灵活,难度很高。写扩展时必须。

服务定义,是描述容器应该如何构建服务的指令。定义本身,并非你的程序所需之真正服务。

取得和设置某服务的定义 

有一些有用的方法用来操作服务定义。

确定一个service的id是否存在:

1
$container->hasDefinition($serviceId);

如果一个特定的服务定义存在的话,当你想对该服务做些什么的话,下面方法是有用的:

1
$container->getDefinition($serviceId);

或:

1
$container->findDefinition($serviceId);

后者不像 getDefinition() 那样可以解析假名(alias),如果$serviceId参数是一个假名的话,你会得到它背后的定义。

服务定义本身是对象,所以你通过上述方法取出一个定义并且改变它,则这些对象会反射到容器中。如果你要在容器中创建一个新的服务定义,可以使用:

1
2
3
4
use Symfony\Component\DependencyInjection\Definition;
 
$definition = new Definition('Acme\Service\MyService');
$container->setDefinition('acme.my_service', $definition);

注册服务定义极为简单,容器提供了一个快捷方法叫作 register()

1
$container->register('acme.my_service', 'Acme\Service\MyService');

操作一个服务定义 

创建一个新定义 

除了(从容器中)取出和操作定义,你也可以通过 Definition 类来创建一个全新定义:

类 

Definition 类的第一个可选参数,是FQCN类名,即,当服务被从容器中取出时,会返回一个对象,该对象就是这个类的实例。

1
2
3
use Symfony\Component\DependencyInjection\Definition;
 
$definition = new Definition('Acme\Service\MyService');

如果 Definition 类在实例化时,还不知道服务的类名,以后再利用 setClass() 方法来设置也是可以的:

1
$definition->setClass('Acme\Service\MyService');

找出服务定义所属的类是哪个:

1
2
$class = $definition->getClass();
// $class = 'Acme\Service\MyService'

构造器参数 

Definition 类的第二个可选参数,是一个数组,当服务从容器中被取出来时,它要作为参数传给服务对象的构造器。

1
2
3
4
5
6
use Symfony\Component\DependencyInjection\Definition;
 
$definition = new Definition(
    'Acme\Service\MyService',
    array('argument1' => 'value1', 'argument2' => 'value2')
);

如果 Definition 类在实例化时,还不知道构造参数,或者你想添加新参,使用 addArgument() 方法,它可以把新参数添加到参数数组的最后:

1
$definition->addArgument($argument);

你可以用下述方法得到服务定义的构造器参数,它是个数组:

1
$definition->getArguments();

或得到指定位置的那个参数:

1
2
3
$definition->getArgument($index);
// e.g. $definition->getArgument(0) for the first argument
// 例如,$index取0值,则是获得第一个参数

构造器所需的参数可以是字符串、数组,以及用参数%parameter_name%引用的服务:

1
$definition->addArgument('%kernel_debug%');

如果参数是另一个服务,不要用get()方法来获取它,因为这在定义服务的过程中是不可用的(译注:那只是定义,不能用作构造参数中的对象实例)。使用 Reference 类来得到该服务的引用,这样才能在服务容器构建完成后得以使用。

1
2
3
4
5
use Symfony\Component\DependencyInjection\Reference;
 
// ...
 
$definition->addArgument(new Reference('service_id'));

用类似的方式你可以替换已经存在的构造器参数,通过index索引实现:

1
$definition->replaceArgument($index, $argument);

你可以使用一个“参数数组”,来替换构造器中的所有参数(或当构造器没有参数时):

1
$definition->setArguments($arguments);

方法调用 

如果你面对的服务使用的是Setter注入,你可以在服务定义中操作这些方法。

你可以得到所有setter方法的数组:

1
$definition->getMethodCalls();

向该服务中添加一个方法时:

1
$definition->addMethodCall($method, $arguments);

这里的 $mehtod 是setter方法的名字,而 $arguments 是调用该方法时所需参数的数组。参数可以是字符串、数组、参数、或service ids(如同前面构造器参数那种)。

你可以替换任何已存在的方法,通过添加新的方法数组进去:

1
$definition->setMethodCalls($methodCalls)

还有更多的例子是以特殊方式来操作配置信息中的服务定义的,这些PHP代码段你可以在本章的 使用“工厂”来创建服务利用父服务来管理常规依赖 两个小节中找到。

这里讲的改变服务定义的方法,只适用于容器在被编译之前。一旦容器被编译,你就不能再操作服务定义。请参阅后面的 编译服务容器 以了解更多。

包容文件 

在某些场合,你需要在服务被加载进来之前,包容另外一个文件。这时,你可以用 setFile() 方法:

1
$definition->setFile('/src/path/to/file/foo.php');

注意Symfony在内部使用PHP的require_once,这意味着你的文件在每次请求中只被包容一次。

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)