跳到内容

如何在 Bundle 内部加载服务配置

编辑此页

Bundle 创建的服务不是在应用程序使用的主 config/services.yaml 文件中定义的,而是在 Bundle 本身中定义的。本文解释了如何使用 Bundle 目录结构创建和加载服务文件。

有两种不同的方法可以做到这一点

  1. 在主 Bundle 类中加载你的服务:这对于新 Bundle 以及遵循推荐目录结构的 Bundle 来说是推荐的做法;
  2. 创建一个扩展类来加载服务配置文件:这是传统的做法,但现在仅推荐给遵循旧目录结构的 Bundle。

直接在你的 Bundle 类中加载服务

在扩展 AbstractBundle 类的 Bundle 中,你可以定义 loadExtension() 方法,从配置文件中加载服务定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ...
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class AcmeHelloBundle extends AbstractBundle
{
    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        // load an XML, PHP or YAML file
        $container->import('../config/services.xml');

        // you can also add or replace parameters and services
        $container->parameters()
            ->set('acme_hello.phrase', $config['phrase'])
        ;

        if ($config['scream']) {
            $container->services()
                ->get('acme_hello.printer')
                    ->class(ScreamingPrinter::class)
            ;
        }
    }
}

此方法的工作方式类似于下面解释的 Extension::load() 方法,但它使用了一个新的更简单的 API 来定义和导入服务配置。

注意

Extension::load() 中的 $configs 参数相反,$config 参数已经由 AbstractBundle 合并和处理。

注意

loadExtension() 仅在编译时调用。

创建扩展类

这是在 Bundle 中加载服务定义的传统方法。对于新的 Bundle,建议在主 Bundle 类中加载你的服务,但传统的创建扩展类的方法仍然有效。

依赖注入扩展被定义为一个遵循以下约定的类(稍后你将学习如何在需要时跳过它们)

  • 它必须位于 Bundle 的 DependencyInjection 命名空间中;
  • 它必须实现 ExtensionInterface,这通常通过扩展 Extension 类来实现;
  • 名称等于 Bundle 名称,其中 Bundle 后缀替换为 Extension(例如,AcmeBundle 的扩展类将被称为 AcmeExtension,而 AcmeHelloBundle 的扩展类将被称为 AcmeHelloExtension)。

这就是 AcmeHelloBundle 的扩展应该如何 виглядати

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;

class AcmeHelloExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container): void
    {
        // ... you'll load the files here later
    }
}

手动注册扩展类

当不遵循约定时,你将必须手动注册你的扩展。为此,你应该覆盖 Bundle::getContainerExtension() 方法以返回扩展的实例

1
2
3
4
5
6
7
8
9
10
11
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;

class AcmeHelloBundle extends Bundle
{
    public function getContainerExtension(): ?ExtensionInterface
    {
        return new UnconventionalExtensionClass();
    }
}

此外,当新的扩展类名不遵循命名约定时,你还必须覆盖 Extension::getAlias() 方法以返回正确的 DI 别名。DI 别名是在容器中引用 Bundle 的名称(例如,在 config/packages/ 文件中)。默认情况下,这是通过删除 Extension 后缀并将类名转换为下划线来完成的(例如,AcmeHelloExtension 的 DI 别名是 acme_hello)。

使用 load() 方法

load() 方法中,所有与此扩展相关的服务和参数都将被加载。此方法不会获取实际的容器实例,而是获取副本。此容器仅具有来自实际容器的参数。加载服务和参数后,副本将合并到实际容器中,以确保所有服务和参数也添加到实际容器中。

load() 方法中,你可以使用 PHP 代码来注册服务定义,但更常见的是将这些定义放在配置文件中(使用 YAML、XML 或 PHP 格式)。

例如,假设你的 Bundle 的 config/ 目录中有一个名为 services.xml 的文件,你的 load() 方法如下所示

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

// ...
public function load(array $configs, ContainerBuilder $container): void
{
    $loader = new XmlFileLoader(
        $container,
        new FileLocator(__DIR__.'/../../config')
    );
    $loader->load('services.xml');
}

其他可用的加载器是 YamlFileLoaderPhpFileLoader

使用配置来更改服务

Extension 也是处理该特定 Bundle 的配置的类(例如,config/packages/<bundle_alias>.yaml 中的配置)。要了解更多信息,请参阅“如何为 Bundle 创建友好的配置”文章。

添加要编译的类

Bundle 可以提示 Symfony 哪些类包含注解,以便在生成应用程序缓存以提高整体性能时编译它们。在 addAnnotatedClassesToCompile() 方法中定义要编译的带注解的类列表

1
2
3
4
5
6
7
8
9
10
11
12
13
public function load(array $configs, ContainerBuilder $container): void
{
    // ...

    $this->addAnnotatedClassesToCompile([
        // you can define the fully qualified class names...
        'Acme\\BlogBundle\\Controller\\AuthorController',
        // ... but glob patterns are also supported:
        'Acme\\BlogBundle\\Form\\**',

        // ...
    ]);
}

注意

如果某个类从其他类扩展,则其所有父类都会自动包含在要编译的类列表中。

模式使用 Composer 生成的 classmap 转换为实际的类命名空间。因此,在使用这些模式之前,你必须执行 Composer 的 dump-autoload 命令来生成完整的 classmap。

警告

当要编译的类使用 __DIR____FILE__ 常量时,不能使用此技术,因为当从 classes.php 文件加载这些类时,它们的值会发生变化。

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