跳到内容

如何创建服务别名并将服务标记为私有

编辑此页

将服务标记为公开/私有

在定义服务时,可以将其设置为公开私有。如果一个服务是公开的,这意味着你可以在运行时直接从容器中访问它。例如,doctrine 服务是一个公开服务

1
2
// only public services can be accessed in this way
$doctrine = $container->get('doctrine');

但通常情况下,服务是通过依赖注入来访问的。在这种情况下,这些服务需要是公开的。

因此,除非你明确需要通过 $container->get() 直接从容器中访问服务,否则最佳实践是将你的服务设置为私有。实际上,所有服务默认都是私有的。

你也可以在每个服务的基础上控制 public 选项

1
2
3
4
5
6
# config/services.yaml
services:
    # ...

    App\Service\Foo:
        public: true

也可以通过 #[Autoconfigure] 属性将服务定义为公开的。此属性必须直接用于你要配置的服务的类上

1
2
3
4
5
6
7
8
9
10
// src/Service/Foo.php
namespace App\Service;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;

#[Autoconfigure(public: true)]
class Foo
{
    // ...
}

私有服务很特别,因为它们允许容器优化它们是否以及如何被实例化。这提高了容器的性能。它也给你更好的错误信息:如果你尝试引用一个不存在的服务,即使有问题的代码在那页上没有运行,当你刷新任何页面时,你也会得到一个清晰的错误。

现在服务是私有的,你绝不能直接从容器中获取服务

1
2
3
use App\Service\Foo;

$container->get(Foo::class);

因此,如果你不想直接从你的代码中访问服务,则可以将服务标记为私有。然而,如果一个服务已被标记为私有,你仍然可以为其创建别名(见下文)以访问此服务(通过别名)。

别名

有时你可能想使用快捷方式来访问某些服务。你可以通过为它们创建别名来实现,而且,你甚至可以为非公开服务创建别名。

1
2
3
4
5
6
7
8
9
10
11
// src/Mail/PhpMailer.php
namespace App\Mail;

// ...
use Symfony\Component\DependencyInjection\Attribute\AsAlias;

#[AsAlias(id: 'app.mailer', public: true)]
class PhpMailer
{
    // ...
}

这意味着当直接使用容器时,你可以通过请求 app.mailer 服务来访问 PhpMailer 服务,就像这样

1
$container->get('app.mailer'); // Would return a PhpMailer instance

提示

在 YAML 中,你也可以使用快捷方式来为服务创建别名

1
2
3
4
# config/services.yaml
services:
    # ...
    app.mailer: '@App\Mail\PhpMailer'

提示

当使用 #[AsAlias] 属性时,如果类只实现了一个接口,你可以省略传递 id 参数。MailerInterface 将会是 PhpMailer 的别名

1
2
3
4
5
6
7
8
9
10
11
12
// src/Mail/PhpMailer.php
namespace App\Mail;

// ...
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
use Symfony\Component\Mailer\MailerInterface;

#[AsAlias]
class PhpMailer implements MailerInterface
{
    // ...
}

弃用服务别名

如果你决定弃用服务别名(因为它已过时或你决定不再维护它),你可以弃用它的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.mailer:
    alias: 'App\Mail\PhpMailer'

    # this outputs the following generic deprecation message:
    # Since acme/package 1.2: The "app.mailer" service alias is deprecated. You should stop using it, as it will be removed in the future
    deprecated:
        package: 'acme/package'
        version: '1.2'

    # you can also define a custom deprecation message (%alias_id% placeholder is available)
    deprecated:
        package: 'acme/package'
        version: '1.2'
        message: 'The "%alias_id%" alias is deprecated. Do not use it anymore.'

现在,每次使用此服务别名时,都会触发弃用警告,建议你停止或更改对此别名的使用。

该消息实际上是一个消息模板,它将 %alias_id% 占位符的出现替换为服务别名 ID。你必须在你的模板中至少有一个 %alias_id% 占位符。

匿名服务

在某些情况下,你可能希望阻止服务被用作其他服务的依赖项。这可以通过创建匿名服务来实现。这些服务就像常规服务,但它们不定义 ID,并且在它们被使用的地方创建。

以下示例展示了如何将匿名服务注入到另一个服务中

1
2
3
4
5
6
# config/services.yaml
services:
    App\Foo:
        arguments:
            - !service
                class: App\AnonymousBar

注意

匿名服务继承配置中定义的默认值提供的定义。因此,当执行匿名服务时,你需要显式地将服务标记为自动装配或自动配置,例如:inline_service(Foo::class)->autowire()->autoconfigure()

将匿名服务用作工厂看起来像这样

1
2
3
4
# config/services.yaml
services:
    App\Foo:
        factory: [ !service { class: App\FooFactory }, 'constructFoo' ]

弃用服务

一旦你决定弃用服务的使用(因为它已过时或你决定不再维护它),你可以弃用它的定义

1
2
3
4
5
6
# config/services.yaml
App\Service\OldService:
    deprecated:
        package: 'vendor-name/package-name'
        version: '2.8'
        message: The "%service_id%" service is deprecated since vendor-name/package-name 2.8 and will be removed in 3.0.

现在,每次使用此服务时,都会触发弃用警告,建议你停止或更改对此服务的使用。

该消息实际上是一个消息模板,它将 %service_id% 占位符的出现替换为服务的 ID。你必须在你的模板中至少有一个 %service_id% 占位符。

注意

弃用消息是可选的。如果未设置,Symfony 将显示此默认消息:"%service_id%" 服务已弃用。你应该停止使用它,因为它很快将被移除。

提示

强烈建议你定义自定义消息,因为默认消息过于通用。一个好的消息应该说明此服务何时被弃用,将维护到何时,以及要使用的替代服务(如果有)。

对于服务装饰器(参见如何装饰服务),如果定义不修改弃用状态,它将从被装饰的定义继承状态。

这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可协议获得许可。
目录
    版本