如何操作“服务定义”对象
最重要文档 译注:本文乍看之下并不复杂,但却活用在所有第三方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
,这意味着你的文件在每次请求中只被包容一次。