柔軟で制御可能なアプリケーションでは、必然的に多数の制御可能なパラメーター (これらを構成と呼びます) が存在します。 CI の主な構成 構成ファイル (ここでは Application/Config/Config.php ファイルを指します) には、次のような複数の構成があります:
リーリーそれだけでなく、CI を使用すると、メイン設定ファイルの外に設定パラメータを配置することもできます。たとえば、独自の構成ファイルを Config_app.php として定義し、次のようにアプリケーション コントローラーに構成ファイルをロードできます。 リーリー
CI はこのようなさまざまな設定項目や設定ファイルをどのように管理しているのでしょうか?これが今日追跡する内容です: CI の構成管理コンポーネント - Config.php.まずこのコンポーネントのクラス図を見てください:
その中に:
_config_paths: 検索する設定ファイルのパス。ここでは APPPATH ディレクトリを指します。設定ファイルも APPPATH の下にある必要があります。
Config: この配列は、すべての構成アイテムの保存に使用されます
Is_loaded: ロードされたすべての設定ファイルのリストを保存します。
_construct: コンポーネントのコンストラクター。主にbase_urlを構成します。
_assign_to_config:index.php の設定項目がメイン設定ファイルの設定をオーバーライドできるようにします
_uri_string、site_url、base_url、system_url: URI、プロジェクトパス、およびその他の関連処理。
load: 設定ファイルをロードします。
item: 構成アイテムを取得します
slash_item: item と同じですが、最後に "" 区切り文字が追加される点が異なります。通常、slash_item が必要になるのは site_url、base_url などだけです。
各メソッドの具体的な実装を分析してみましょう:1.コンポーネントの初期化_construct
リーリー
なぜなら、私たちのアプリケーションはbase_urlの値を取得する必要があることが多く、この値は必須ではありません(設定のbase_urlは空に設定できます)が、base_urlの値を空にしたくないからです。したがって、CI は、Config コンポーネントを初期化するときに、base_url に対して特定の処理を実行します。これは主に、Config.php でbase_url が空に設定されている場合に発生します。(1) $_SERVER['HTTP_HOST'] が設定されている場合、base_url はプロトコル (http または https) + $_SERVER['HTTP_HOST'] + SCIRPT_PATH:
の形式に設定されます。 リーリー(2)。それ以外の場合は、http://localhost/:
に直接設定されます。 リーリー(3) 同時に、後続のアクセスを容易にするために、base_url 構成アイテムを構成配列にマップします (後で set_item メソッドを使用します。ここで知っておく必要があるのは、それが構成アイテムに追加され、古い値):
リーリーbase_url 設定項目は多くのコンポーネントに必要であることがわかります。したがって、CI がbase_url の正確性を保証するために一定のエネルギーを費やすことは理解できます。
2.設定ファイルの読み込み
すべてのパラメータはオプションのパラメータです。
ここで、各仮パラメータの意味を簡単に説明します:
$fileロードする必要がある設定ファイルにはサフィックス名を含めることも、含めないこともできます。このパラメータが指定されていない場合は、デフォルトで Config.php ファイルがロードされます。 $user_sections
: ロードされた設定ファイルに別のセクションを使用するかどうか、独自の設定ファイルを定義する場合、設定ファイル内の設定項目が Config.php ファイルと異なる可能性があることを想像してみてください。構成項目内で構成項目が競合しています。 $section を true に指定すると、構成項目が上書きされるのを防ぐことができます。$fail_graceful
: ロードする設定ファイルが存在しない場合の処理。 Graceful は「優雅」を意味します。このパラメータが true に設定されている場合、ファイルが存在しない場合はエラーを表示せずに false のみを返します。このメソッドの具体的な実装を見てみましょう:
(1). 設定ファイル名の前処理:
リーリー この $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'; <br /> /* 如果已经加载,则跳至最外层循环,事实上,由于_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中这样加载配置文件:
<span>$this</span>->config->load("config_app",<span>true</span>);
则config数组是这样的格式:
[config] => <span>Array</span><span> ( [base_url] </span>=> http:<span>//</span><span>test.xq.com</span> [index_page] =><span> [uri_protocol] </span>=><span> AUTO [url_suffix] </span>=> .<span>html [proxy_ips] </span>=><span> [web_akey] </span>=><span> yyyyyyyyyyyy [config_app] </span>=> <span>Array</span><span> ( [web_akey] </span>=><span> xxxxxxx [web_skey] </span>=><span> xxxxxxxxxxxxxxxxxxx [web_callback_url] </span>=> http:<span>//</span><span>test.xq.com/</span> [sess_pre] =><span> WEB_APP [cart_min] </span>=> 1<span> [cart_max] </span>=> 999<span> ) )</span>
相反,如果我们不指定use_sections,则数组是这样存储的:
[config] => <span>Array</span><span> ( [base_url] </span>=> http:<span>//</span><span>test.xq.com</span> [index_page] =><span> [uri_protocol] </span>=><span> AUTO [url_suffix] </span>=> .<span>html [web_akey] </span>=><span> xxxxxxx [web_skey] </span>=><span> xxxxxxxxxxxxxxxxxxx [web_callback_url] </span>=> http:<span>//</span><span>test.xq.com/</span> [sess_pre] =><span> WEB_APP [cart_min] </span>=> 1<span> [cart_max] </span>=> 999<span> )</span>
这也意味着,在不启用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.'); }
item方法用于在配置中获取特定的配置项,改方法的签名:
<span>function</span> item(<span>$item</span>, <span>$index</span> = '')
注意,如果你在load配置文件的时候启用了use-sections,则在使用item()获取配置项的时候需要指定第二个参数,也就是加载的配置文件的文件名(不包含后缀)。为了更清楚这一点,我们假设现在Config/目录下有配个配置文件:config.php和config_app.php,这两个配置文件中含有一个相同的键web_akey, 在config.php中,该配置为:
<span>$config</span>['web_akey'] = 'yyyyyyyyyyyy';
而config_app.php中,该配置为:
<span>$config</span>['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"),"<br/>"; echo "config :web_akey => ",$this->config->item("web_akey");
实际的获取结果:
config_app:web_akey =><span> xxxxxxx config </span>: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], '/').'/'; }
这里先澄清这几个含义的区别:
echo "site_url : ",$this->config->site_url("index/rain"),"</br>"; echo "base_url : ",$this->config->base_url("index/rain"),"<br/>"; echo "system_url: ",$this->config->system_url();
的结果分别是:
<span>site_url : http://test.xq.com/index/rain.html base_url : http://test.xq.com/index/rain system_url: http://test.xq.com/system/</span>
可以看出,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).'/'; }
site_url和base_url都调用了_uri_string。这个函数是做什么用的呢?
按理来说, _uri_string的功能应该由URI组件来完成,这里却放在了Config组件中,似乎有些不妥(实际上,_uri_string是为base_url和site_url专属服务的)。
对于这样的uri:
<span>array( 'p1' </span>=> 'param1',<span> 'p2' </span>=<span>> 'param2' )</span>
如果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¶m2=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; }
与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组件,会省不少事啊。