高级配置技巧
Zend Framework 2应用程序的配置发生在以下几个步骤:
- 初始配置通过Application实例和使用ModuleManager和ServiceManager的种子。在这篇教程中,我们称这个配置:系统配置(System Configuration)
- 当有模块调用时ModuleManager的ConfigListener集合配置和合并它。在这篇教程中,我们称这个配置:应用配置(Application Configuration)
- 一旦从所有模块中聚合配置,ConfigListener还将在指定目录中合并全局应用配置(典型目录是config/autoload/)
在这篇教程中,我们将着眼于恰当的顺序以及如何约束它们。
一、系统配置(System Configuration)
开始模块装载,我们必须告诉Application实例有关有效的模块以及它们在哪里,随意提供一些信息给默认的模块监听器(例如:应用配置在哪里,以及什么文件被装载;是否缓存合并配置放在哪里,等等),随意ServiceManager播种。在本教程中我们称这个配置为系统配置(System Configuration)
当使用应用程序骨架(或者应用程序框架)时,系统配置(System Configuration)默认在config/application.config.php里。看上去类似下面的代码:
<?php
return array(
// This should be an array of module namespaces used in the application.
'modules' => array(
'Application',
),
// These are various options for the listeners attached to the ModuleManager
'module_listener_options' => array(
// This should be an array of paths in which modules reside.
// If a string key is provided, the listener will consider that a module
// namespace, the value of that key the specific path to that module's
// Module class.
'module_paths' => array(
'./module',
'./vendor',
),
// An array of paths from which to glob configuration files after
// modules are loaded. These effectively overide configuration
// provided by modules themselves. Paths may use GLOB_BRACE notation.
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
// Whether or not to enable a configuration cache.
// If enabled, the merged configuration will be cached and used in
// subsequent requests.
//'config_cache_enabled' => $booleanValue,
// The key used to create the configuration cache file name.
//'config_cache_key' => $stringKey,
// Whether or not to enable a module class map cache.
// If enabled, creates a module class map cache which will be used
// by in future requests, to reduce the autoloading process.
//'module_map_cache_enabled' => $booleanValue,
// The key used to create the class map cache file name.
//'module_map_cache_key' => $stringKey,
// The path in which to cache merged configuration.
//'cache_dir' => $stringPath,
// Whether or not to enable modules dependency checking.
// Enabled by default, prevents usage of modules that depend on other modules
// that weren't loaded.
// 'check_dependencies' => true,
),
// Used to create an own service manager. May contain one or more child arrays.
//'service_listener_options' => array(
// array(
// 'service_manager' => $stringServiceManagerName,
// 'config_key' => $stringConfigKey,
// 'interface' => $stringOptionalInterface,
// 'method' => $stringRequiredMethodName,
// ),
// )
// Initial configuration with which to seed the ServiceManager.
// Should be compatible with Zend\ServiceManager\Config.
// 'service_manager' => array(),
);
系统配置有关MVC的点点滴滴在在你系统之前运行并准备好了。配置通常是简报并且相当小巧的。
另外,立即使用的系统配置,并且没有合并任何其它的配置-也就是说它不能被其它模块覆盖。
这引导我们到我们第一个技巧:如何提供特定环境下的系统配置?
二、特定环境系统配置
当你要改变一整套基于环境的模块时会发生什么?或者如果配置缓存基于环境被启用会发生什么?
就是这个原因我们在应用程序框架中提供的默认系统配置是使用PHP的;使用PHP来支持的意思是说通过编程来控制它。
作为一个例子,让我们做以下的要求:
- 我们要求只在开发阶段使用ZendDeveloperTools模块
- 我们要求只在生产中有配置缓存
要实现以上的要求,我们在我们的Web Server的配置中设置一个环境变量,在Apache中设置APP_ENV,要么在你系统级的apache.conf文件,要么在http.conf文件中输入类似以下命令,或者在你的虚拟主机中定义,也可以输入到.htaccess文件中
SetEnv "APP_ENV" "development"
对于其它的web server,查阅web server文档来确定如何设置环境变量
为了简化问题,我们假定环境是“生产”如果没有环境变量存在。
我们按照以下的代码修改config/application.config.php
<?php
$env = getenv('APP_ENV') ?: 'production';
// Use the $env value to determine which modules to load
$modules = array(
'Application',
);
if ($env == 'development') {
$modules[] = 'ZendDeveloperTools';
}
return array(
'modules' => $modules,
'module_listener_options' => array(
'module_paths' => array(
'./module',
'./vendor',
),
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
// Use the $env value to determine the state of the flag
'config_cache_enabled' => ($env == 'production'),
'config_cache_key' => 'app_config',
// Use the $env value to determine the state of the flag
'module_map_cache_enabled' => ($env == 'production'),
'module_map_cache_key' => 'module_map',
'cache_dir' => 'data/config/',
// Use the $env value to determine the state of the flag
'check_dependencies' => ($env != 'production'),
),
);
这种方法给你带来了改变系统级设置的灵活性。
但是,如何在基于环境的情况下修改应用程序特定的设置(不是系统配置)?
三、特定环境应用程序配置
有些时候你想要修改应用程序配置来调用一些东西,例如数据库适配器,log写入,缓存适配器以及更多机基于环境的东西。这些在服务管理器中具有代表性的托管可能在模块中被定义。你可以通过Zend\ModuleManager\Listener\ConfigListener在应用级别中重写它们,经过一个在系统配置(System Configuration)中指定的全局路径 - 在上例中的module_listener_options.config_glob_paths关键字。
它的默认值是config/autoload/{,*.}{global,local}.php。这个意思是说它会在config/autoload目录中按照以下的顺序寻找应用程序配置(Application Configuration)
- global.php
- *.global.php
- local.php
- *.local.php
这允许你在“全局”配置文件中定义应用程序级别的默认值,然后提交到版本控制系统中,特定环境下在“本地”配置文件中重写那些你会忽略版本控制的值。
这是一个非常好的开发解决方案,它允许你指定交替的配置,针对你的开发环境不用担心意外的部署。
然而,如果你有很多环境 - 例如一个“测试”或者“展现”环境 - 它们每个都有自己的特殊的重写?
再说一次,使用应用程序环境变量。我们可以稍稍的改变以下系统配置中的全局路径:
'config_glob_paths' => array(
sprintf('config/autoload/{,*.}{global,%s,local}.php', $env)
),
以上代码允许你为每个环境额外的定义应用程序配置文件;此外,只有当相应的环境被侦测到,这些配置才会被调用!
作为一个例子,考虑一下以下的配置文件结构树:
config/
autoload/
global.php
local.php
users.development.php
users.testing.php
users.local.php
如果$env是testing,以下的文件将按照顺序被合并:
global.php
users.testing.php
local.php
users.local.php
注意:users.development.php没有被调用 - 因为它没有匹配全局模式!
此外,由于按照顺序调用,你可以预测哪些值会覆盖其它的值,让你可以在后来的调试中有选择性的覆盖。
注意:
在config/autoload/目录下的文件在模块配置之后被合并,具体信息见下个段落。我们有详细的说明,然而,设定应用程序配置(Application Configuration)的全局路径是在系统配置(System Configuration)中的(config/application.config.php)
四、模块配置
模块的职责之一是为应用程序提供它们自己的配置。模块有两个一般的机制来实现这个。
首先,要么通过Zend\ModuleManager\Feature\ConfigProviderInterface实现和/或通过getConfig()方法返回模块的配置。默认,推荐使用getConfig()方法:
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
module.config.php返回一个PHP数组。
第二,模块能够实现许多接口和/或与指定的服务管理器或插件管理器配置有关的方法。例如:
接口名称 | 方法名称 |
ControllerPluginProviderInterface | getControllerPluginConfig() |
ControllerProviderInterface | getControllerConfig() |
FilterProviderInterface | getFilterConfig() |
FormElementProviderInterface | getFormElementConfig() |
HydratorProviderInterface | getHydratorConfig() |
InputFilterProviderInterface | getInputFilterConfig() |
RouteProviderInterface | getRouteConfig() |
SerializerProviderInterface | getSerializerConfig() |
ServiceProviderInterface | getServiceConfig() |
ValidatorProviderInterface | getValidatorConfig() |
ViewHelperProviderInterface | getViewHelperConfig() |
所有的接口列表都在Zend\ModuleManager\Feature命名空间中,每个都为服务管理器返回一个配置数组,在“默认服务管理”中有提到。
考虑到你可能会在模块配置文件中含有服务配置,哪一个优先?
合并的顺序是:
- getConfig()返回的配置
- 在模块类中通过各种服务配置方法返回的配置
换句话说,你各种服务配置方法优先。另外特别注意:从方法中返回的配置不会被缓存。原因是它不是经常使用闭包或者工厂实例从你的模块类返回配置 - 不能可靠的缓存配置。
注意:
当你需要定义闭包或者为工厂模式定义回调接口,抽象工厂和初始化时使用各种服务配置方法,这预防了缓存问题,同时也允许你使用其它格式写你自己的配置文件。
五、合并配置工作流程
在结束本教程之前,让我们回顾一下如何以及何时定义以及合并配置。
系统配置(System Configuration) - 在config/application.config.php中定义 - 不会合并 - 允许以编程方式操作,允许:
- 在计算值的基础上改变标记
- 在计算值的基础上改变配置全局路径
- 配置被传递给Application接口,然后ModuleManager按照顺序初始化系统
应用程序配置(Application Configuration) - ModuleManager按照定义的顺序循环访问每个模块类
在系统配置(System Configuration)中 - 定义在模块类中的服务配置是聚合 - 通过Module::getConfig()返回的配置是聚合
- 从服务配置(Service Configuration)的config_glob_paths设置中发现的文件会被合并,合并的顺序是根据它们在全局路径中的顺序。
- 合并后的配置最终被传递给ServiceManager
未完待续,谢谢......