支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
当服务被定义时,你通常希望能够在应用程序中去访问这些定义。这时的服务被称为public
(公开)。例如,使用DoctrineBundle时,在容器中被注册的doctrine
服务就是public服务。这表示你可以从容器中取出它,利用get()方法:
1 | $doctrine = $container->get('doctrine'); |
在一些例子中,某个服务的存在,仅仅是为了被注入到另一个服务中,而并不需要像上面那样从container中直接取出。
在这些例子中,为了得到些许性能提升,你应该设置服务为非公开(private):
1 2 3 4 | services:
foo:
class: Example\Foo
public: false |
1 2 3 4 5 6 7 8 9 | <?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="foo" class="Example\Foo" public="false" />
</services>
</container> |
1 2 3 4 5 | use Symfony\Component\DependencyInjection\Definition;
$definition = new Definition('Example\Foo');
$definition->setPublic(false);
$container->setDefinition('foo', $definition); |
令私有服务变得特殊的是,如果它们只被注入一次,它们就会被从“服务”转化为“inlined instantiations行内实例”(如:new PrivateThing()
),因为这可以提升容器性能。
现在,服务变为私有,你不应该直接从容器中出取它:
1 | $container->get('foo'); |
上述代码能否工作,取决于这个服务是否可以成为行内实例(inlined instantiations)。简单说就是:一个服务可以被标记为私有,如果你不希望它从你的代码中被直接访问到。
然而,如果一个服务被标记为私有,你仍可利用假名(alias,见下例)来访问该服务(通过假名)。
服务默认都是公开的。
译注:本小节乃是文档遗产,有的概念尚存在于最新版框架中,有的却已经被弱化或等待删除。虽然翻译日期久远,但从原理上讲,这部分内容不忍删除,在此列出仅供参考。
合成服务的最新文档以独立章节如何注入实例到容器中重新登场。
合成服务,是指那些注入到容器中,而不是被容器创建出来的服务。
例如,你正在使用HttpKernel组件,配合DependencyInjection组件,然后,当进入请求范围(request scope)之内时,request
服务被注入到ContainerAwareHttpKernel::handle()方法中。当没有request请求时,这个类并不存在,所以该服务并不能包含在容器配置信息中。同样,该服务也与程序中的任何一个子请求(subrequest)不同。
为了创建合成服务,添加synthetic
键并取值为true
:
1 2 3 | services:
request:
synthetic: true |
1 2 3 4 5 6 7 8 9 | <?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="request" synthetic="true" />
</services>
</container> |
1 2 3 4 5 | use Symfony\Component\DependencyInjection\Definition;
$container
->setDefinition('request', new Definition())
->setSynthetic(true); |
以上显示,只有synthetic
选项被设置,所有其他选项,都只能用于配置那些“被容器创建”的服务。因为request服务不是由容器创建,所以其他选项被忽略。
现在,你可以把你的类实例注入到容器以使其成为request服务,通过Container::set
方法:
1 2 | // ...
$container->set('request', new MyRequest(...)); |
有时你需要使用快捷方式来访问某些服务。你可以使用假名来实现,甚至,你可以对非公有(non-public)服务使用假名。
1 2 3 4 5 | services:
foo:
class: Example\Foo
bar:
alias: foo |
1 2 3 4 5 6 7 8 9 10 11 | <?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="foo" class="Example\Foo" />
<service id="bar" alias="foo" />
</services>
</container> |
1 2 3 4 5 | use Symfony\Component\DependencyInjection\Definition;
$container->setDefinition('foo', new Definition('Example\Foo'));
$containerBuilder->setAlias('bar', 'foo'); |
这意味着,当直接使用容器时,可以通过请求bar
服务来访问到foo
服务:
1 | $container->get('bar'); // Would return the foo service 返回的是foo服务 |
在YAML中,你可以直接使用对假名使用简便方式:
1 2 3 4 | services:
foo:
class: Example\Foo
bar: '@foo' |
译注:本小节经过Symfony文档梳理,现已转移到其他章节中,参考这里
也许有这样的使用场景,你需要在某个服务自身被加载之前,包容另一个文件进来。此时你可以直接使用file键。
1 2 3 4 | services:
foo:
class: Example\Foo\Bar
file: "%kernel.root_dir%/src/path/to/file/foo.php" |
1 2 3 4 5 6 7 8 9 10 11 | <?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="foo" class="Example\Foo\Bar">
<file>%kernel.root_dir%/src/path/to/file/foo.php</file>
</service>
</services>
</container> |
1 2 3 4 5 | use Symfony\Component\DependencyInjection\Definition;
$definition = new Definition('Example\Foo\Bar');
$definition->setFile('%kernel.root_dir%/src/path/to/file/foo.php');
$container->setDefinition('foo', $definition); |
要注意Symfony在内部是调用PHP的requuire_once语法,这表明你的文件在页面每次请求中只被包容一次。
一旦你决定不再推荐使用某个服务(比如它已过期,或者你决定不再对其进行任何维护),你可以在定义中deprecate它:
1 2 3 | acme.my_service:
class: ...
deprecated: The "%service_id%" service is deprecated since 2.8 and will be removed in 3.0. |
1 2 3 4 5 6 7 8 9 10 11 | <?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="acme.my_service" class="...">
<deprecated>The "%service_id%" service is deprecated since 2.8 and will be removed in 3.0.</deprecated>
</service>
</services>
</container> |
1 2 3 4 5 6 7 | $container
->register('acme.my_service', '...')
->setDeprecated(
true,
'The "%service_id%" service is deprecated since 2.8 and will be removed in 3.0.'
)
; |
现在,每当这个服务被使用时,都会触发一条“不建议使用”的警告,建议用户停止或调整对该服务的使用。
提示信息利用了模板,它可以替换%service_id%
占位符为真实服务的id。你必须让%service_id%
在你的模板中出现至少一次。
“不建议”信息是可选的。如果不设,Symfony会显示默认信息:The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.
。
强烈建议你使用一个自定义的“不建议”信息,因为默认的模板太泛泛了。一个好的通知应包括:该服务何时被弱化、何时可能恢复维护,以及可用的备选服务(如果有的话)。
对于服务的装饰者(service decorators,见上文)来说,如果定义没有修改deprecated状态,decorator将从被装修者的定义中继承这个状态。
一个服务可能会有“un-deprecated”能力,仅当该服务被以PHP来声明时。
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。