支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
如果你打开你的程序配置文件(通常是 app/config/config.yml
),你将看到若干不同的配置区块,比如 framework
,twig
和 doctrine
。这些配置信息中的每一个,都配置了一个特定 bundle,使你能从一个高层级(high level)来定义有关选项,进而使 bundle 基于你的设置,来实现所有低层级(low-level)、复杂的改变。
举例来说,下面的配置告诉FrameworkBundle去启用表单集成,它涉及到相当多的服务的定义以及其他相关组件的集成:
1 2 | framework:
form: true |
1 2 3 4 5 6 7 8 9 10 11 12 | <?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony
http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config>
<framework:form />
</framework:config>
</container> |
1 2 3 | $container->loadFromExtension('framework', array(
'form' => true,
)); |
基本想法是不让用户覆写个体参数,而是让他们只配置少量、特别创建的选项。作为 bundle 开发者,后面对这些配置信息进行解析,并在一个 "Extension" class 内部加载正确的服务和参数。
作为例子,假设你要创建一个社会化(social)的 bundle,提供 Twitter 等的集成。为了让你的 bundle 能够复用,你必须令 client_id
和 client_secret
等变量成为可配置的。你的 bundle 配置信息看起来就像是这样:
1 2 3 4 5 | # app/config/config.yml
acme_social:
twitter:
client_id: 123
client_secret: your_secret |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- app/config/config.xml -->
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:acme-social="http://example.org/dic/schema/acme_social"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<acme-social:config>
<twitter client-id="123" client-secret="your_secret" />
</acme-social:config>
<!-- ... -->
</container> |
1 2 3 4 5 | // app/config/config.php
$container->loadFromExtension('acme_social', array(
'client_id' => 123,
'client_secret' => 'your_secret',
)); |
Seealso
更多关于 extension 的内容在 如何在Bundle内加载服务配置 文中。
Tip
如果一个 bundle 提供了一个 Extension 类(扩展类),那么你不应该覆写源自该 bundle 的任何服务容器参数(parameters)。思路是,如果一个扩展类存在,每一个应可配置的设置选项,都应该在那个类所提供的配置信息中出现。换句话说,扩展(extension)类定义了所有公共配置选项(public configuration settings),这些选项可以保证(你的 bundle 的)向下兼容性。
Seealso
在 dependency injection 容器中处理参数时可参考 在依赖注入类内部使用参数。
最重要的一点,你要创建一个扩展(extension)类,就像 如何在Bundle内加载服务配置 所解释的那样。
只要用户在配置文件中包含了 acme_social
键(DI 容器系统中的别名)时,它下面的配置信息就会被添加到一个配置信息的数组中,然后传入你的 extension 类的 load()
方法中(Symfony 会自动把 XML 和 YAML 数据转换为数组)。
根据上前面小节的配置示例,传给 load()
方法中的数组可能是下面这样:
请注意,这是一个数组的数组,而不是一个简单的扁平的配置值的数组。就这么设计的,因为它允许Symfony解析各自的配置资源。举例来说,如果acme_social
出现在其他的配置文件中,比如config_dev.yml
,它们会有不同的值,里面的数组看起来应该是这样:
两个数组的顺序取决于哪一个排在前面。
但别担心!Symfony 的 Config 组件将帮助你合并这些值、提供默认值、并对用户的非法配置进行给出验证错误。以下就是其工作流程。在 DependencyInjection
目录中创建一个 Configuration
类,并构建一个树(tree),来定义你的 bundle配置 之结构。
Configuration
类处在理示例中的配置信息时可能像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // src/Acme/SocialBundle/DependencyInjection/Configuration.php
namespace Acme\SocialBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('acme_social');
$rootNode
->children()
->arrayNode('twitter')
->children()
->integerNode('client_id')->end()
->scalarNode('client_secret')->end()
->end()
->end() // twitter
->end()
;
return $treeBuilder;
}
} |
Seealso
Configuration
类可能远比这里演示的复杂,它可以支持“prototype”节点、高级验证、基于 XML 的标准化(normalization)以及高级合并。在 Config组件文档 中你可以了解到更多内容。通过查看一些核心的配置类,比如 FrameworkBundle Configuration 和 TwigBundle Configuration,也可以看到该类的实际运作情况。
现在可以在扩展的 load()
方法中使用配置类以合并配置并强制验证(如,若有额外的选项被加入进来,则会抛出一个异常):
1 2 3 4 5 6 7 8 9 10 11 | // src/Acme/SocialBundle/DependencyInjection/AcmeSocialExtension.php
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// you now have these 2 config keys
// $config['twitter']['client_id'] and $config['twitter']['client_secret']
} |
processConfiguration()
方法使用了你已经在 Configuration
类中定义的配置树进行验证(validate)、标准化(normlize)以及把全部配置数组合并在一起。
现在,你可以使用 $config
变量来修改一个你的 bundle 所提供的某个服务了。例如,假设 bundle 中有下例的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- src/Acme/SocialBundle/Resources/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="acme.social.twitter_client" class="Acme\SocialBundle\TwitterClient">
<argument></argument> <!-- will be filled in with client_id dynamically -->
<argument></argument> <!-- will be filled in with client_secret dynamically -->
</service>
</services>
</container> |
在你的扩展中,你可以加载它,并动态设置其参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // src/Acme/SocialBundle/DependencyInjection/AcmeSocialExtension.php
// ...
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources/config'));
$loader->load('services.xml');
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$def = $container->getDefinition('acme.social.twitter_client');
$def->replaceArgument(0, $config['twitter']['client_id']);
$def->replaceArgument(1, $config['twitter']['client_secret']);
} |
Tip
当你要提供一些配置选项时,不同于在扩展中次次调用 processConfiguration()
,你还可使用 ConfigurableExtension
来自动完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
class AcmeHelloExtension extends ConfigurableExtension
{
// note that this method is called loadInternal and not load
// 注意,此方法名为 loadInternal 而不再是 load
protected function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
// ...
}
} |
这个类使用了 getConfiguration()
方法来获取 Configuration 实例。
如果你有多个相互依赖的 bundle,让一个 Extension
类去修改传入到其他 bundle 的 Extension
类中的配置信息,将是非常有用的,就好像末级开发者真的把那个配置信息放置在他们的 app/config/config.yml
文件中一样。这可以使用一个预置扩展(prepend extension)来完成。参考 如何简化多个Bundle的配置 以了解更多。
config:dump-reference
命令会在命令控制台中以 Yaml 格式来剥离出一个 bundle 的默认配置。
只要你的 bundle 的配置信息位于标准位置(YourBundle\DependencyInjection\Configuration
)并且未使用构造器,它就会自动工作。如果你想做一些不同的事情,你的 Extension
必须要覆写 Extension::getConfiguration()
方法并返回一个你自己的 Configuration
实例。
Symfony允许人们使用三种不同格式的配置:Yaml, XML 和 PHP。当使用 Config 组件时,Yaml 和 PHP 使用相同的语法并且默认被支持的。要支持 XML 就需要你做更多的事情了。但是当与其他人分享你的 bundle 时,推荐你遵循下述步骤。
配置组件默认提供了一些方法,以令它能够正确处理 XML 配置信息。参阅组件文档的“Normalization”。然而,你也可以做一些可选的事,这将提升使用 XML 配置信息的体验。
在XML中,XML命名空间 用于确定哪些元素属于特定 bundle 的配置内容。命名空间从 Extension::getNamespace() 方法中返回。默认时,bundle 的命名空间就是一个URL(它并不必须是一个有效的链接且不一定需要存在)。默认情况下,bundle的命名空间是 http://example.org/dic/schema/DI_ALIAS
,在这里 DI_ALIAS
是扩展的DI别名(译注:在容器中的名称)。你可能想要将其换成更加专业的链接:
1 2 3 4 5 6 7 8 9 10 11 12 | // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
// ...
class AcmeHelloExtension extends Extension
{
// ...
public function getNamespace()
{
return 'http://acme_company.com/schema/dic/hello';
}
} |
XML有一个非常有用的特性叫做 XML schema。它将允许你在 XML Schema Definition (一个 xsd 文件)中描述所有的可能的元素以及属性连同它们的值。XSD 文件被 IDE 用于代码自动完成,也被 Config 组件用来验证元素。
为了使用 schema,XML配置文件中必须提供一个 xsi:schemaLocation
属性以指向一个特定的作为 XML 命名空间的 XSD 文件。其位置总是从 XML 命名空间开始。然后,XML 命名空间会被 Extension::getXsdValidationBasePath()
方法返回的 XSD 验证的基本路径(validattion base path)所取代。接下来,此 namespace 会被“从 base path 到文件自身”的其他路径所跟随。
按照惯例,XSD 文件保存在 Resources/config/schema
,但你可以把它放置在任何地方。你应该把此路径作为基本路径(base path)来返回:
1 2 3 4 5 6 7 8 9 10 11 12 | // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
// ...
class AcmeHelloExtension extends Extension
{
// ...
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
} |
假设 XSD 文件叫做 hello-1.0.xsd
,schema 的位置就是 http://acme_company.com/schema/dic/hello/hello-1.0.xsd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!-- app/config/config.xml -->
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:acme-hello="http://acme_company.com/schema/dic/hello"
xsi:schemaLocation="http://acme_company.com/schema/dic/hello
http://acme_company.com/schema/dic/hello/hello-1.0.xsd">
<acme-hello:config>
<!-- ... -->
</acme-hello:config>
<!-- ... -->
</container> |
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。