Magento的配置对象与全局XML配置文件构建

注:后台搜索到两篇比较好的介绍这个主题的文章:

http://alanstorm.com/magento_config_tutorial

http://alanstorm.com/magento_config_declared_modules_tutorial

主要类是Mage_Core_Model_Config,先看它们的继承关系:
Magento配置类继承结构

1
2
3
4
5
6
7
8
9
10
11
12
class Mage_Core_Model_Config_Base extends Varien_Simplexml_Config
{
     /**
      * Constructor
      *
      */
     public function __construct( $sourceData =null)
     {
         $this ->_elementClass = 'Mage_Core_Model_Config_Element' ;
         parent::__construct( $sourceData );
     }
}

Mage_Core_Model_Config_Base 只是修改了继承过来的_elementClass属性,基本是原始的Varien_Simplexml_Config对象。注意,Mage_Core_Model_Config_Element继承自Varien_Simplexml_Element,这个也是继承过来的方法能用的保证(因为Varien_Simplexml_Config很多方法的参数类型是Varien_Simplexml_Element)。

由于Magento实际实例化的是Mage_Core_Model_Config类,看它的构造函数:

1
2
3
4
5
6
7
8
public function __construct( $sourceData =null)
{
     $this ->setCacheId( 'config_global' );
     $this ->_options         = new Mage_Core_Model_Config_Options( $sourceData );
     $this ->_prototype       = new Mage_Core_Model_Config_Base();
     $this ->_cacheChecksum   = null;
     parent::__construct( $sourceData );
}

它用_options保存了一份Mage_Core_Model_Config_Options实例,用_prototype保存了它的父类的一份实例。

这里关键是Mage_Core_Model_Config_Options类实例(它直接继承自Varien_Object类),看它的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected function _construct()
{
     $appRoot = Mage::getRoot();      //就是…/public/app目录
     $root   = dirname( $appRoot );    //就是…/public
 
     $this ->_data[ 'app_dir' ]     = $appRoot ;
     $this ->_data[ 'base_dir' ]    = $root ;
     $this ->_data[ 'code_dir' ]    = $appRoot .DS. 'code' ;
     $this ->_data[ 'design_dir' ]  = $appRoot .DS. 'design' ;
     $this ->_data[ 'etc_dir' ]     = $appRoot .DS. 'etc' ;
     $this ->_data[ 'lib_dir' ]     = $root .DS. 'lib' ;
     $this ->_data[ 'locale_dir' ]  = $appRoot .DS. 'locale' ;
     $this ->_data[ 'media_dir' ]   = $root .DS. 'media' ;
     $this ->_data[ 'skin_dir' ]    = $root .DS. 'skin' ;
     $this ->_data[ 'var_dir' ]     = $this ->getVarDir();
     $this ->_data[ 'tmp_dir' ]     = $this ->_data[ 'var_dir' ].DS. 'tmp' ;
     $this ->_data[ 'cache_dir' ]   = $this ->_data[ 'var_dir' ].DS. 'cache' ;
     $this ->_data[ 'log_dir' ]     = $this ->_data[ 'var_dir' ].DS. 'log' ;
     $this ->_data[ 'session_dir' ] = $this ->_data[ 'var_dir' ].DS. 'session' ;
     $this ->_data[ 'upload_dir' ]  = $this ->_data[ 'media_dir' ].DS. 'upload' ;
     $this ->_data[ 'export_dir' ]  = $this ->_data[ 'var_dir' ].DS. 'export' ;
}

一系列的目录信息被保存到了_data数组中。这个类的方法就是获取这些不同的目录。所以当调用Mage_Core_Model_Config类的getOptions方法时就是返回这个Options的引用,而它就是保存了一些的目录。

而对Mage_Core_Model_Config的首次调用是在是在Mage_Core_Model_App类的run()方法中,在这个方法中第一步是运行$this->baseInit($options)方法,这个方法内部最关键的一行代码是$this->_initBaseConfig()方法(还有$this->_initCache($cacheInitOptions)):

1
2
3
4
5
6
7
     protected function _initBaseConfig()
     {
         Varien_Profiler::start( 'mage::app::init::system_config' );
         $this ->_config->loadBase();
         Varien_Profiler::stop( 'mage::app::init::system_config' );
         return $this ;
}

看到了吧,$this->_config->loadBase(),它负责加载基础配置文件,同时也是Mage_Core_Model_Config这个对象第一次获取填充($this->_config返回已经保存到APP中的Mage_Core_Model_Config对象的实例),马上,我们看看Mage_Core_Model_Config的loadBase()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function loadBase()
{
     $etcDir = $this ->getOptions()->getEtcDir(); //这个获得app/etc目录
     $files = glob ( $etcDir .DS. '*.xml' );          //把app/etc目录下的xml文件用数组返回(不包含子目录的XML文件)
     $this ->loadFile(current( $files ));            //把第一个xml文件用loadFile读入
     while ( $file = next( $files )) {
         $merge = clone $this ->_prototype;        //克隆一份配置对象
         $merge ->loadFile( $file );             //把第二(第三第四…)个xml读入当前克隆的配置对象
         $this ->extend( $merge );               //把它们合并
     }
     if (in_array( $etcDir .DS. 'local.xml' , $files )) { //如果app/etc/local.xml已经读入,标志一下
         $this ->_isLocalConfigLoaded = true;
     }
     return $this ;
}

很自然,我想看看loadFie如果load xml:

1
2
3
4
5
6
7
8
9
10
11
public function loadFile( $filePath )
{
     if (! is_readable ( $filePath )) {
         //throw new Exception('Can not read xml file '.$filePath);
         return false;
     }
 
     $fileData = file_get_contents ( $filePath );
     $fileData = $this ->processFileData( $fileData ); //直接把$fileData返回
     return $this ->loadString( $fileData , $this ->_elementClass);
}

Wow, 直接使用file_get_contents读入文件内容。然后返回loadString处理后的内容,注意第二参数$this->_elementClass,它在当前代码里值被替换为Mage_Core_Model_Config_Element。继续看loadString函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function loadString( $string )
{
     if ( is_string ( $string )) {
         $xml = simplexml_load_string( $string , $this ->_elementClass);
 
         if ( $xml instanceof Varien_Simplexml_Element) {
             $this ->_xml = $xml ;
             return true;
         }
     } else {
         Mage::logException( new Exception( '"$string" parameter for simplexml_load_string is not a string' ));
     }
     return false;
}

Wow,直接使用simplexml_load_string把xml字符串加载到Mage_Core_Model_Config_Element的对象中(simplexml_load_string() 的第二参数可以指定一个类,这个类必须是继承SimpleXMLElement类,这样函数将返回这个类实例。)

然后看生成的这个$xml对象是不是Varien_Simplexml_Element的实例(当然是了,因为$xml是Mage_Core_Model_Config_Element类型,它继承自Varien_Simplexml_Element),如果是就把它保存到Mage_Core_Model_Config类对象的_xml中。

XML就这样读进来了,其它XML也是这样读进来的,回到那段XML合并的代码,它调用extend方法,实际在其内部是调用_xml对象的extend方法,合并细节只要跟踪进去就可以知晓,不过我想我了解到这里已经可以了。

这里先总结一下:

 
Mage_Core_Model_Config 
$_options		保存了Mage_Core_Model_Config_Options对象,主要记录了系统相关的目录
$_xml			保存了Mage_Core_Model_Config_Element对象,完成基础配置的加载后就合并了app/etc/local.xml和app/etc/config.xml文件的内容(后续会继续合并其它配置)。
$_ isLocalConfigLoaded 	在local.xml加载后就被标志位true,这个对应系统是否能继续往下运行提供了开关设置。

另,它们中的继承关系:
Magento的XML类继承结构
这里其实已经涉及到了配置相关内容的大部分了。不过,我们继续回到Mage_Core_Model_App类的run()方法中,它在运行了$this->baseInit($options)后接下来会运行$this->_initModules(),看下这个方法吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected function _initModules()
{
     if (! $this ->_config->loadModulesCache()) {
         $this ->_config->loadModules();
         if ( $this ->_config->isLocalConfigLoaded() && ! $this ->_shouldSkipProcessModulesUpdates()) {
             Varien_Profiler::start( 'mage::app::init::apply_db_schema_updates' );
             Mage_Core_Model_Resource_Setup::applyAllUpdates();
             Varien_Profiler::stop( 'mage::app::init::apply_db_schema_updates' );
         }
         $this ->_config->loadDb();
         $this ->_config->saveCache();
     }
     return $this ;
}

在模块缓存没有获取到的情况下,它调用配置对象的loadModules()方法。所以我们在这里继续深入进去,我们马上进入Mage_Core_Module_Config的loadModules()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public function loadModules()
{
     Varien_Profiler::start( 'config/load-modules' );
     //先调用_getDeclaredModules()函数,到app/etc/modules里面取出所有XML文件,分成三类,返回array('base'=>对应Mage_All文件,'mage'=>对应Mage_开头的文件,'custom'=>其它自定义模块),然后合并,然后检查依赖关系,最后合并到配置文件的_xml中(全局配置)
     $this ->_loadDeclaredModules();
     //获取资源链接模型 最终得到的串是mysql4,这个在这里没有作用
     $resourceConfig = sprintf( 'config.%s.xml' , $this ->_getResourceConnectionModel( 'core' ));
     //把所有模块的的config.xml都合并进来
     $this ->loadModulesConfiguration( array ( 'config.xml' , $resourceConfig ), $this );
 
     //再加载一次app/etc/local.xml,防止被覆盖,这里说明所有模块的配置文件都加载了
     $mergeConfig = clone $this ->_prototype;
     $this ->_isLocalConfigLoaded = $mergeConfig ->loadFile( $this ->getOptions()->getEtcDir().DS. 'local.xml' );
     if ( $this ->_isLocalConfigLoaded) {
         $this ->extend( $mergeConfig );
     }
 
     $this ->applyExtends();
     Varien_Profiler::stop( 'config/load-modules' );
     return $this ;
}

里面涉及到的其它函数跟踪进去看看就能明白。这里我感兴趣的是$this->loadModulesConfiguration()方法,它展示了模块配置文件合并的细节:

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
28
29
30
31
32
public function loadModulesConfiguration( $fileName , $mergeToObject = null, $mergeModel =null)
{
     //_canUseLocalModules()获取配置global/disable_local_modules的值,这是一个禁止使用本地模块的开关
     $disableLocalModules = ! $this ->_canUseLocalModules();
 
     if ( $mergeToObject === null) {
         $mergeToObject = clone $this ->_prototype;
         $mergeToObject ->loadString( '<config/>' );
     }
     if ( $mergeModel === null) {
         $mergeModel = clone $this ->_prototype;
     }
     $modules = $this ->getNode( 'modules' )->children();
     foreach ( $modules as $modName => $module ) {
         if ( $module ->is( 'active' )) {
             if ( $disableLocalModules && ( 'local' === (string) $module ->codePool)) {
                 continue ;
             }
             if (! is_array ( $fileName )) {
                 $fileName = array ( $fileName );
             }
 
             foreach ( $fileName as $configFile ) {
                 $configFile = $this ->getModuleDir( 'etc' , $modName ).DS. $configFile ;
                 if ( $mergeModel ->loadFile( $configFile )) {
                     $mergeToObject ->extend( $mergeModel , true);
                 }
             }
         }
     }
     return $mergeToObject ;
}

Mage_Core_Model_App类的run()方法中接下来运行$this->_config->loadDb(),这个运行完毕后,全局的巨大的配置文档树就构建起来了。loadDb()是从数据库加载配置到这个XML文档树中。本文就不深入跟踪下去了。

永久连接: http://blog.ifeeline.com/411.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值