> Symfony中文手册 > 如何在Bundle内加载服务配置

如何在Bundle内加载服务配置

在Symfony中,你会发现你自己使用了很多的服务。这些服务在你应用程序的app/config/目录中被注册。但当你想要解耦这个Bundle(译注,如:成为第三方bundle 或者分离出来的bundle),让它们使用在其他项目中时,你就会想要让 bundle 本身拥有服务配置。这篇文章将会教你如何实现这个目标。

创建一个Extension(扩展)类 ¶

为了加载服务配置,你要为你的Bundle创建一个依赖注入(DI)扩展。这个类有一些约定,以便symfony自动被检测到。但你稍后会看到如何将它更改为你自己喜好的样子。默认情况下,扩展必须遵守以下约定:

它必须位于 bundle 的 DependencyInjection 命名空间之中;

它的名称要和 bundle 的一样,只是后缀由 Bundle 换成了 Extension(例如 AppBundle 的 Extension(扩展) 类就会叫做 AppExtension,同时 AcmeHelloBundle 就会被叫做 AcmeHelloExtension)。

Extension(扩展)类应该实现 ExtensionInterface,但通常你只需要继承Extension类:

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.PHP
namespace Acme\HelloBundle\DependencyInjection;
 
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
 
class AcmeHelloExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        // ... you'll load the files here later
    }
}

手动注册一个Extension (扩展)类 ¶

当没有遵守这些约定时,你必须手动注册你的 Extension 类。要做到这一点,你应该重写Bundle::getContainerExtension()方法来返回extension(扩展)实例:

1
2
3
4
5
6
7
8
9
10
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
 
class AcmeHelloBundle extends Bundle
{
    public function getContainerExtension()
    {
        return new UnconventionalExtensionClass();
    }
}

由于新的Extension (扩展)类名没有遵循命名约定,你要需要重写Extension::getAlias()来返回正确的DI 别名。DI 别名是用来标识容器中的 bundle 的(如,在app/config/config.yml 文件中)。默认情况下,通过移除Extension后缀并转换这个类名加入下划线来完成(例如,AcmeHelloExtension别名是acme_hello)。

使用load()方法 ¶

load()方法里,与extension(扩展)有关的所有服务和参数都将被加载。这个方法没有得到实际的容器实例,但是是一个副本。这个容器只有从实际容器中取来的参数。在加载服务和参数之后,副本就会合并复制到实际的容器,来保证所有的服务和参数也被添加到实际的容器之中。

load()方法里,您可以使用PHP代码注册服务定义,但更常见的做法是把这些定义放到配置文件中(可使用 Yaml, XML 或者 PHP)。幸运的是,您可以在extension(扩展中)使用文件加载器!

例如,假设你有一个名为services.xml的文件在你bundle的Resources/config目录,你的加载方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
 
// ...
public function load(array $configs, ContainerBuilder $container)
{
    $loader = new XmlFileLoader(
        $container,
        new FileLocator(__DIR__.'/../Resources/config')
    );
    $loader->load('services.xml');
}

你可以用其他加载器YamlFileLoaderPhpFileLoaderIniFileLoader

IniFileLoader只能用于加载参数,它只能将参数作为字符串加载。

如果你删除用来做服务定义的默认文件(如, app/config/services.yml),也一定要确保从app/config/config.yml中移除了它的imports 键。

使用配置去改变这个服务 ¶

Extension这个类也是处理 bundle 特定配置(例如 app/config/config.yml 中的配置)。想阅读更多关于它的信息,请看“如何给Bundle创建友好的配置”

添加要编译的类 ¶

Symfony在缓存目录创建了一个大的classes.php文件,集合了每一个请求所使用的PHP类的内容。他减少了I/O操作,并提高了应用程序的性能。

你的Bundle也能添加他们自己的类到这个文件,多亏了addClassesToCompile() 方法。把要编译的类定义为一个全限定类名的数组:

1
2
3
4
5
6
7
8
9
10
11
// ...
public function load(array $configs, ContainerBuilder $container)
{
    // ...
 
     $this->addClassesToCompile(array(
        'AppBundle\\Manager\\UserManager',
        'AppBundle\\Utils\\Slugger',
        // ...
    ));
}

如果一些类从其他类继承,他所有的父类将自动被包含到要编译类的列表。

注意,这种技术在某些情况下不能使用:

  • 当类包含注释,比如控制器有@Route注释和实体有@ORM@Assert 注释,因为它们被加入到classes.php,php反射获取的文件位置已经变化了;

  • 当类使用了__DIR____FILE__常量,因为当从classes.php文件加载这些类时,他们的值会发生改变。