CI架構源碼閱讀筆記7 組態管理組件 Config.php,ciconfig.php_PHP教程

來源:互聯網
上載者:User

CI架構源碼閱讀筆記7 組態管理組件 Config.php,ciconfig.php


一個靈活可控的應用程式中,必然會存在大量的可控參數(我們稱為配置),例如在CI的主設定檔中(這裡指Application/Config/Config.php檔案),有如下多項配置:

$config['base_url']   = 'http://test.xq.com';$config['index_page'] = '';$config['uri_protocol']     = 'AUTO';$config['url_suffix'] = '.html';$config['language']  = 'english';$config['charset'] = 'UTF-8';$config['enable_hooks'] = FALSE;…………………………

不僅如此,CI還允許你將配置參數放到主設定檔之外。例如,你可以定義自己的設定檔為Config_app.php, 然後在你的應用程式控制器中這樣載入你的設定檔:

$this->config->load('config_app');

如此紛繁多樣的配置項和設定檔,CI是如何進行管理的?這便是我們今天要跟蹤的內容:CI的組態管理組件-Config.php.

先看該組件的類圖:

其中:

_config_paths:要搜尋的設定檔的路徑,這裡指APPPATH目錄,你的設定檔也應該位於APPPATH下。

Config: 這個數組用於存放所有的配置項的item

Is_loaded: 存放所有的已經載入的設定檔列表。

_construct: 組件的建構函式,主要是配置base_url

_assign_to_config: 允許index.php中的配置項覆蓋主設定檔中的設定

_uri_string,site_url,base_url,system_url: URI, 項目路徑等相關處理。

load: 載入設定檔。

item:擷取配置項

slash_item:同item,不同的是,在最後加了”\”分隔字元,一般只有site_url,base_url等會需要slash_item

下面我們去剖析各個方法的具體實現:

1.  組件初始化 _construct

之前我們在分析Common.php全域函數的時候提到過,在Config組件執行個體化之前,所有的組設定檔的擷取都是由get_config()函數來代理的。在Config組件執行個體化時,要將所有的配置存放到自己的私人變數$config中,便於之後的訪問和處理:

$this->config =& get_config();

由於我們應用程式很多時候需要擷取base_url的值,而這個值並不是必填項(config中base_url可以設定為空白),但我們又不希望擷取到的base_url的值為空白。因此,CI在Config組件初始化的時候,對base_url做了一定的處理。這主要出現在Config.php中base_url設定為空白的情況:

(1). 如果設定了$_SERVER[‘HTTP_HOST’],則base_url被設定為Protocal(http或者https) + $_SERVER['HTTP_HOST'] + SCIRPT_PATH的形式:

$base_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off' ? 'https' : 'http';$base_url .= '://'. $_SERVER['HTTP_HOST'];$base_url .= str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']);

(2). 否者,直接被設定為http://localhost/:

$base_url = 'http://localhost/';

(3). 同時將base_url配置項映射到配置數組中,方便之後的訪問(set_item方法我們稍後會將,這裡只需要知道,它是添加到配置項且會覆蓋舊值):

$this->set_item('base_url', $base_url);

之後我們會看到,base_url這個配置項對於很多組件都是必須的,因此,CI花費一定的精力來保證base_url的正確性,也是可以理解的。

2.  載入設定檔 load

這是Config組件中較核心的方法之一,該函數的簽名:

function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)

所有的參數都是選擇性參數。

我們這裡簡單解釋一下各形參的含義:

  $file 需要載入的設定檔,可以包含尾碼名也不可以不包含,如果未指定該參數,則預設載入Config.php檔案

  $user_sections: 是否為載入的設定檔使用獨立的section,這麼說可能還是不明白,試想,如果你定義了自己的設定檔,而你的設定檔中的配置項可能與Config.php檔案中的配置項衝突,通過指定$section為true可以防止配置項的覆蓋。

  $fail_gracefully: 要load的設定檔不存在時的處理。Gracefully意為優雅的,如果該參數設定為true,則在檔案不存在時只會返回false,而不會顯示錯誤。

下面看該方法的具體實現:

(1). 設定檔名預先處理:

$file = ($file == '') ? 'config' : str_replace('.php', '', $file);

這個$file最後只包含檔案名稱,而不包含副檔名。如果該參數為空白,則預設載入Config.php設定檔。這同時也說明,我們載入自己的設定檔時:

$this->config->load("");與

$this->config->load("config")效果是一樣的,而:

$this->config->load("config_app")與

$this->config->load("config_app.php")的效果也是一樣的。

如果啟用了$use_sections,這個$file會作為config的主鍵。

(2). 尋找和載入設定檔。

在跟蹤實現之前,先解釋幾個尋找和載入過程中比較重要的參數:

(3).具體的尋找過程是一個雙重的foreach迴圈:

/*  對於config_paths中的路徑迴圈尋找 */foreach ($this->_config_paths as $path){  /* 對每個location尋找,也就是分別對ENVIRONMENT/config/ 和 config/ 目錄尋找  */  foreach ($check_locations as $location)  {/* 實際的設定檔名 */$file_path = $path.'config/'.$location.'.php';
/* 如果已經載入,則跳至最外層迴圈,事實上,由於_config_paths的設定,會跳出整個迴圈 */if (in_array($file_path, $this->is_loaded, TRUE)){  $loaded = TRUE;  continue 2;}/* 若檔案存在,跳出當前迴圈 */if (file_exists($file_path)){  $found = TRUE;  break;}  }  /* 如果沒有找到設定檔,繼續下一次迴圈。同樣,由於_config_path的設定,會跳出整個迴圈 */  if ($found === FALSE)  {continue;  }}

(4).引入設定檔

到這裡,如果設定檔不存在,則$found和$loaded都為false,CI會根據fail_gracefully參數決定檔案不存在的處理方式;如果檔案存在,則需要對設定檔的格式檢查:

/* 引入設定檔 */include($file_path);/* 設定檔的格式檢查,這同時也說明,設定檔中最起碼應該包含$config數組 */if ( ! isset($config) OR ! is_array($config)){  if ($fail_gracefully === TRUE)  {return FALSE;  }  show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.');}

(5).對use_sections參數的處理

前面說過,use_secitons參數如果為true,則CI_Config會對該設定檔啟用獨立的key儲存。例如,我們在controller中這樣載入設定檔:

$this->config->load("config_app",true);

則config數組是這樣的格式:

[config] => Array(    [base_url] => http://test.xq.com    [index_page] =>    [uri_protocol] => AUTO    [url_suffix] => .html    [proxy_ips] =>    [web_akey] => yyyyyyyyyyyy    [config_app] => Array        (            [web_akey] => xxxxxxx            [web_skey] => xxxxxxxxxxxxxxxxxxx            [web_callback_url] => http://test.xq.com/            [sess_pre] => WEB_APP            [cart_min] => 1            [cart_max] => 999        ))

相反,如果我們不指定use_sections,則數組是這樣儲存的:

[config] => Array(    [base_url] => http://test.xq.com    [index_page] =>    [uri_protocol] => AUTO    [url_suffix] => .html    [web_akey] => xxxxxxx    [web_skey] => xxxxxxxxxxxxxxxxxxx    [web_callback_url] => http://test.xq.com/    [sess_pre] => WEB_APP    [cart_min] => 1    [cart_max] => 999)

這也意味著,在不啟用user_secitons的情況下,如果你的設定檔中有與主設定檔Config.php相同的鍵,則會覆蓋主設定檔中的項:

/* 啟用單獨的key存放載入的config */if ($use_sections === TRUE){  if (isset($this->config[$file]))  {$this->config[$file] = array_merge($this->config[$file], $config);  }  else  {$this->config[$file] = $config;  }}else{  /* 執行merge,更改CI_Config::config */  $this->config = array_merge($this->config, $config);}

(6).錯誤處理

雙層迴圈完成後,如果loaded為false,也就是未成功載入任何配置,則根據fail_gracefully做相應的錯誤處理:

/* 未成功載入任何配置 */if ($loaded === FALSE){  if ($fail_gracefully === TRUE)  {return FALSE;  }  show_error('The configuration file '.$file.'.php does not exist.');}

3.  擷取配置項item,slash_item

item方法用於在配置中擷取特定的配置項,改方法的簽名:

function item($item, $index = '')

注意,如果你在load設定檔的時候啟用了use-sections,則在使用item()擷取配置項的時候需要指定第二個參數,也就是載入的設定檔的檔案名稱(不包含尾碼)。為了更清楚這一點,我們假設現在Config/目錄下有配個設定檔:config.php和config_app.php,這兩個設定檔中含有一個相同的鍵web_akey, 在config.php中,該配置為:

$config['web_akey']  = 'yyyyyyyyyyyy';

而config_app.php中,該配置為:

$config['web_akey'] = 'xxxxxxx';

現在,通過use-sections的方法載入config_app設定檔(config.php會在Config組件初始化的時候被載入):

$this->config->load("config_app",true);

然後在控制器中擷取web_akey配置項:

echo "config_app:web_akey => ",$this->config->item("web_akey","config_app"),"
";echo "config :web_akey => ",$this->config->item("web_akey");

實際的擷取結果:

config_app:web_akey => xxxxxxxconfig :web_akey => yyyyyyyyyyyy

瞭解原理之後,該方法的實現就比較簡單了:

function item($item, $index = ''){  /* 沒有設定use_sections的情況,直接在config中尋找配置項 */  if ($index == '')  {if ( ! isset($this->config[$item])){  return FALSE;}$pref = $this->config[$item];  }  else  {if ( ! isset($this->config[$index])){  return FALSE;}if ( ! isset($this->config[$index][$item])){  return FALSE;}$pref = $this->config[$index][$item];  }  /* 統一的return出口 */  return $pref;}

slash_item實際上與item()方法類似,但他不會去使用者的配置中尋找,並且,他返回的是主設定檔中的配置項,並在配置項最後添加反斜線.這個方法,通常用於base_url和index_page這兩個配置項的處理:

該方法的實現源碼:

function slash_item($item){  /* 不存在配置項 */  if ( ! isset($this->config[$item]))  {return FALSE;  }  /* 配置項為空白 */  if( trim($this->config[$item]) == '')  {return '';  }  /* 去除最後的多餘的"/",並在結尾添加一個"/" */  return rtrim($this->config[$item], '/').'/';}

4.  擷取網站site_url, base_url,system_url

這裡先澄清這幾個含義的區別:

echo "site_url  : ",$this->config->site_url("index/rain"),"
";echo "base_url : ",$this->config->base_url("index/rain"),"
";echo "system_url: ",$this->config->system_url();

的結果分別是:

site_url : http://test.xq.com/index/rain.htmlbase_url : http://test.xq.com/index/rainsystem_url: http://test.xq.com/system/

可以看出,site_url是添加了suffix(在Config/config.php中配置)後的url地址(呵呵,如果你的uri中有query string,則Ci總是在最後添加suffix:http://test.xq.com/index/rain?w=ss.html 是不是很奇怪.)

base_url則是沒有添加suffix的url地址。

而system_url這個東西很奇怪,是擷取系統的url路徑。但實際上,由於system路徑並沒有直接執行的指令碼,所以這個方法的實際用途是什麼,暫時不知。有知道的童鞋麻煩告知。

具體的方法實現,這裡不贅述了。直接貼出源碼:

function site_url($uri = ''){/* 沒有設定uri,使用base_url + index_page */if ($uri == ''){return $this->slash_item('base_url').$this->item('index_page');}/* enable_query_strings未啟用,可以添加suffix尾碼 */if ($this->item('enable_query_strings') == FALSE){$suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');return $this->slash_item('base_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix;}/* 否者不添加suffix尾碼 */else{return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri);}}/* 擷取base_url,注意與site_url的區別 */function base_url($uri = ''){return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/');}/* 擷取system url */function system_url(){       /* 擷取系統目錄.   BASEPATH:/search/xx/phpCode/CI/system/ */    $x = explode("/", preg_replace("|/*(.+?)/*$|", "\\1", BASEPATH));    return $this->slash_item('base_url').end($x).'/';}

5.  擷取URI String: _uri_string

site_url和base_url都調用了_uri_string。這個函數是做什麼用的呢?

按理來說, _uri_string的功能應該由URI組件來完成,這裡卻放在了Config組件中,似乎有些不妥(實際上,_uri_string是為base_url和site_url專屬服務的)。

對於這樣的uri:

array(    'p1' => 'param1',    'p2' => 'param2')

如果enable_query_string為false,則_uri_string處理過後是這樣的形式:

param1/param2

而enable_query_string為true,則處理後的形式是這樣的:

p1=param1&p2=param2

這是我們常見(雖然很難看且SEO不好)的形式。改方法的實現源碼:

protected function _uri_string($uri){/* enable_query_strings 為false,直接implode */if ($this->item('enable_query_strings') == FALSE){if (is_array($uri)){$uri = implode('/', $uri);}$uri = trim($uri, '/');}/* 否者,拼接成類似param1=param1&param2=param2的形式 */else{if (is_array($uri)){$i = 0;$str = '';foreach ($uri as $key => $val){/* 第一個參數前面不需要加& */$prefix = ($i == 0) ? '' : '&';$str .= $prefix.$key.'='.$val;$i++;}$uri = $str;}}    return $uri;}

6.  設定配置項 set_item _assign_to_config

與item()相反,set_item用於設定配置項。如果配置項已經存在,則會被覆蓋:

$this->config[$item] = $value;

_assign_to_config同set_item,該方法提供了數組的設定方式(調用set_item。我們之前在解釋CodeIgniter.php檔案的時候提到過:改方法允許在index.php中設定獨立的配置項,且index.php中的配置具有更高的優先權(會覆蓋主設定檔中的配置):

function _assign_to_config($items = array()){if (is_array($items)){foreach ($items as $key => $val){$this->set_item($key, $val);}}}

到這裡,Config組件的基本解析就算是完成了,我們再次回顧下該組件的準系統:

最後感慨一下,一個好的Config組件,會省不少事啊。

http://www.bkjia.com/PHPjc/909743.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/909743.htmlTechArticleCI架構源碼閱讀筆記7 組態管理組件 Config.php,ciconfig.php 一個靈活可控的應用程式中,必然會存在大量的可控參數(我們稱為配置),例如在...

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.