如何创建服务别名并将服务标记为私有
将服务标记为公开/私有
在定义服务时,可以将其设置为公开或私有。如果一个服务是公开的,这意味着你可以在运行时直接从容器中访问它。例如,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%"
服务已弃用。你应该停止使用它,因为它很快将被移除。
提示
强烈建议你定义自定义消息,因为默认消息过于通用。一个好的消息应该说明此服务何时被弃用,将维护到何时,以及要使用的替代服务(如果有)。
对于服务装饰器(参见如何装饰服务),如果定义不修改弃用状态,它将从被装饰的定义继承状态。