支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
在 Symfony 中,你会发现自己使用了很多的服务。这些服务可以在你程序的 app/config/
目录中被注册。但当你想要解耦这个 bundle 以便它们用在其他项目中时,你就会想让服务配置存于 bundle 本身。本文将教会你如何实现。
为了加载服务配置,你要为你的 bundle 创建一个依赖注入(DI)扩展。这个类有一些约定,以便能够自动被检测到。但稍后你会看到如何将它更改为你自己偏好的样子。默认情况下,Extension 类必须遵守以下约定:
它必须位于 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 类。要这么做到,你应该覆写 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()
方法里,你可以使用 PHP 代码来注册服务定义,但更常见的做法是把这些定义放到配置文件中(可使用 Yaml, XML 或者 PHP)。幸运的是,你可以在 extension 中使用 file loader!
例如,假设你有一个名为 services.xml
的文件位于你 bundle 中的 Resources/config
目录,你的 load()
方法如下:
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');
} |
可用的加载器有 YamlFileLoader
,PhpFileLoader
和 IniFileLoader
。
Note
IniFileLoader
只能用于加载参数,且只能将参数作为字符串加载。
Caution
如果你删除了包含服务定义的默认文件(如, app/config/services.yml
),也要确保把它从 app/config/config.yml
中的 imports
键下给删除。
Extension 类也是处理特定 bundle 之配置的类(如 app/config/config.yml
中的配置信息)。更多内容参考 如何为Bundle创建友好的配置。
Note
addClassesToCompile()
方法在 Symfony 3.3 不建议再使用,且会在 Symfony 4.0 中删除。如果你要使用本方法并与 Symfony 4.0 能够兼容,调用之前先检查这个方法是否存在。
Symfony 在缓存目录创建了一个很大的 classes.php
文件,聚合了每次请求中都要用到的 PHP 类的内容。它减少了 I/O 吞吐操作并提高了程序性能。
3.2新增
addAnnotatedClassesToCompile()
方法自 Symfony 3.2 起被引入。
你的 bundle 也能添加他们自己的类到这个文件,多亏了 addClassesToCompile()
方法和 addAnnotatedClassesToCompile()
方法。把要编译的类定义为一个 FQCN 完整类名的数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | use AppBundle\Manager\UserManager;
use AppBundle\Utils\Slugger;
// ...
public function load(array $configs, ContainerBuilder $container)
{
// ...
// this method can't compile classes that contain PHP annotations
// 此方法不可编译包含了 PHP annotation 的类
$this->addClassesToCompile(array(
UserManager::class,
Slugger::class,
// ...
));
// add here only classes that contain PHP annotations
// 只可把包含了 PHP annotation 的类添加到此处
$this->addAnnotatedClassesToCompile(array(
'AppBundle\\Controller\\DefaultController',
// ...
));
} |
Note
如果有一些类是从其他的类继承过来的,它们的父类都将自动地包含到待编译的类的列表。
3.2新增 使用 patterns 来添加待编译类的选项自 Symfony 3.2 起被引入。
待编译的类可通过使用 file path patterns 进行添加:
利用 Composer 所生成的 classmap,patterns 被转换成真实类的命名空间。因此,在使用这些 patterns 之前,你必须执行 Composer 的 dump-autoload
以生成完整的 classmap。
Caution
这技巧不可用在使用了 __DIR__
或 __FILE__
常量的待编译类中,因为它们的值将在这些类从 classes.php
文件中加载时发生改变。
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。