原文见这里:http://www.cnblogs.com/ohmygirl/p/CIRead-7.html

一个灵活可控的应用程序中,必定会存在大量的可控參数(我们称为配置),比如在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中,便于之后的訪问和处理:

1
$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的形式:

1
2
3
$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/

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

(3).    同一时候将base_url配置项映射到配置数组中,方便之后的訪问(set_item方法我们稍后会将。这里仅仅须要知道,它是加入到配置项且会覆盖旧值):

1
$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).    查找和载入配置文件。

在跟踪实现之前。先解释几个查找和载入过程中比較重要的參数:

  1. $found  这个參数实际上是个flag,用于标识配置文件是否查找到,一旦查找到配置文件,则停止不论什么搜索。
  2. $loaded  同$found參数类似。这个$loaded也是一个flag,用于标识请求的配置文件是否被载入。

    普通情况下,被载入的配置文件会被CI_Config:: is_loaded变量追踪

  3. $_config_path  要查找的配置路径,这个变量因为是写死在Config组件中的,且没有提供加入或者更改的接口。因此我们能够觉得_config_path就是APPPATH.也就是,配置文件的load一定是在APPPATH文件夹下查找的。
  4. $check_locations  这个參数是要查找的位置(详细文件)。

    相同。假设定了ENVIRONMENT且存在对应ENVIRONMENT下的配置文件,优先载入该文件。

(3).详细的查找过程是一个双重的foreach循环:

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
/*  对于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參数决定文件不存在的处理方式;假设文件存在。则须要对配置文件的格式检查:

1
2
3
4
5
6
7
8
9
10
11
12
/* 引入配置文件 */
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同样的键,则会覆盖主配置文件里的项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 启用单独的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做对应的错误处理:

1
2
3
4
5
6
7
8
9
/* 未成功载入不论什么配置 */
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组件初始化的时候被载入):

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

然后在控制器中获取web_akey配置项:

1
2
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 => xxxxxxx
config :web_akey => yyyyyyyyyyyy

了解原理之后。该方法的实现就比較简单了:

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
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这两个配置项的处理:

该方法的实现源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

这里先澄清这几个含义的差别:

1
2
3
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();

的结果各自是:

site_url : http://test.xq.com/index/rain.html
base_url : http://test.xq.com/index/rain
system_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路径并没有直接运行的脚本,所以这种方法的实际用途是什么。临时不知。有知道的童鞋麻烦告知。

详细的方法实现,这里不赘述了。

直接贴出源代码:

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
33
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()
{   <br>    /* 获取系统文件夹.   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不好)的形式。改方法的实现源代码:

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
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用于设置配置项。假设配置项已经存在。则会被覆盖:

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

_assign_to_config同set_item。该方法提供了数组的设置方式(调用set_item。我们之前在解释CodeIgniter.php文件的时候提到过:改方法同意在index.php中设置独立的配置项,且index.php中的配置具有更高的优先权(会覆盖主配置文件里的配置):

1
2
3
4
5
6
7
8
9
10
function
_assign_to_config(
$items
=
array())
{
    if
(is_array($items))
    {
        foreach
($items
as
$key =>
$val)
        {
            $this->set_item($key,
$val);
        }
    }
}

到这里,Config组件的基本解析就算是完毕了,我们再次回想下该组件的基本功能:

  1. set_item和item是Config组件的基本对外接口。也就是常见的setter 和getter。_assign_to_config算是批量的setter,slash_item则是特殊处理的getter
  2. load方法是载入配置文件,假设你自己定义了自己的配置文件,须要先load使得你的配置纳入CI_Config的管理之下。

  3. system_url,base_url,site_url,用于获取特定的配置项。
  4. _uri_string是CI_Config中唯一一个Protected的方法。

    这种方法主要是处理uri,提供给site_url和base_url使用

最后感慨一下,一个好的Config组件,会省不少事啊。

CI框架源代码阅读笔记7 配置管理组件 Config.php的更多相关文章

  1. CI框架源代码阅读笔记5 基准測试 BenchMark.php

    上一篇博客(CI框架源代码阅读笔记4 引导文件CodeIgniter.php)中.我们已经看到:CI中核心流程的核心功能都是由不同的组件来完毕的.这些组件类似于一个一个单独的模块,不同的模块完毕不同的 ...

  2. CI框架源代码阅读笔记3 全局函数Common.php

    从本篇開始.将深入CI框架的内部.一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说.全局函数具有最高的载入优先权.因此大多数的框架中BootStrap ...

  3. CI框架源代码阅读笔记2 一切的入口 index.php

    上一节(CI框架源代码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程.这里再次贴出流程图.以备參考: 作为CI框架的入口文件.源代码阅读,自然由此開始. 在源代码阅读的 ...

  4. ****CI框架源码阅读笔记7 配置管理组件 Config.php

    http://blog.csdn.net/ohmygirl/article/details/41041597 一个灵活可控的应用程序中,必然会存在大量的可控参数(我们称为配置),例如在CI的主配置文件 ...

  5. CI框架源代码阅读笔记6 扩展钩子 Hook.php

    CI框架同意你在不改动系统核心代码的基础上加入或者更改系统的核心功能(如重写缓存.输出等). 比如,在系统开启hook的条件下(config.php中$config['enable_hooks'] = ...

  6. Mongodb源代码阅读笔记:Journal机制

    Mongodb源代码阅读笔记:Journal机制 Mongodb源代码阅读笔记:Journal机制 涉及的文件 一些说明 PREPLOGBUFFER WRITETOJOURNAL WRITETODAT ...

  7. Spark源代码阅读笔记之DiskStore

    Spark源代码阅读笔记之DiskStore BlockManager底层通过BlockStore来对数据进行实际的存储.BlockStore是一个抽象类,有三种实现:DiskStore(磁盘级别的持 ...

  8. Java Jdk1.8 HashMap源代码阅读笔记二

    三.源代码阅读 3.元素包括containsKey(Object key) /** * Returns <tt>true</tt> if this map contains a ...

  9. ruby2.2.2 源代码阅读笔记

    这是win32下的结构 从ruby_setup开始阅读 Ruby对象内存结构 RVALUE是一个union,内含ruby所有结构体(RBasic RObject RClass RFloat RStri ...

随机推荐

  1. Java源代码编译过程

      编译其本质是将一种语言规范转换成另一种语言规范,即将Java语言规范转换为JVM虚拟机语言规范.结果就是.java文件到.class文件. 对于C/C++编译直接将高级语言转换为机器语言,Java ...

  2. 通过修改VHD文件的位置来提升性能

    昨天用VHD装了一个Win 10的预览版体验了一下,感觉磁盘操作非常慢,便用HD Tune测试了一下,发现速度只有物理硬盘的一半都不到.          这个倒大出我意料之外,由于VHD的便利性,我 ...

  3. Android Json的使用(2) 使用Jackson解析和生成json

    使用Jackson的三种方式 数据绑定模式:使用最方便 流模式:性能最佳 树模式:最灵活 以最常用的数据绑定模式为例 Json数据如下 { "name" : { "fir ...

  4. android 检查能否上网

    文章一: 首先在,AndroidManifest.xml 中增加访问权限: <uses-permission android:name="android.permission.ACCE ...

  5. Kubernetes下的Redis主从配置架构

    文章看了一大堆,但都是直接从各种地方直接拉master,slave镜像,没有交代这些镜像如何构建出来的 好把,我这篇就讲讲这些master,slave镜像如何做成. 先得找到一个标准的redis镜像, ...

  6. 使用神经网络识别手写数字Using neural nets to recognize handwritten digits

    The human visual system is one of the wonders of the world. Consider the following sequence of handw ...

  7. mac下源码安装redis

    转载:http://www.jianshu.com/p/6b5eca8d908b 下载安装包 redis-3.0.7.tar.gz 官网地址:http://redis.io/download 解压:t ...

  8. iOS: 环信的推送

    原文:http://m.blog.csdn.net/article/details?id=38824551 1.先创建一个apns证书,链接如下 http://developer.easemob.co ...

  9. iOS:自动读取图片插件KSImageNamed-Xcode-master的使用

    gitHub链接:https://github.com/ksuther/KSImageNamed-Xcode   KSImageNamed-Xcode是一个Xcode插件,可以帮助开发者在Xcode中 ...

  10. windows下 memcached 和 redis 服务器安装

    memcached 安装: 1.下载memcached 文件: 2.拷贝到运行目录: 3.命令行进入到程序目录: 运行命令: memcached -d install 如果没有报错说明安装成功 4.打 ...