目錄
CI框架源码阅读笔记7 配置管理组件 Config.php,ciconfig.php
1.  组件初始化 _construct
2.  加载配置文件 load
3.  获取配置项item,slash_item
4.  获取站点site_url, base_url,system_url
5.  获取URI String: _uri_string
6.  设置配置项 set_item  _assign_to_config
首頁 後端開發 php教程 CI框架源码阅读笔记7 配置管理组件 Config.php,ciconfig.php_PHP教程

CI框架源码阅读笔记7 配置管理组件 Config.php,ciconfig.php_PHP教程

Jul 13, 2016 am 10:14 AM
config.php 框架 原始碼 筆記 管理 組件 配置 閱讀

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

 一个灵活可控的应用程序中,必然会存在大量的可控参数(我们称为配置),例如在CI的主配置文件中(这里指Application/Config/Config.php文件),有如下多项配置:

<span>$config</span>['base_url']   = 'http://test.xq.com'<span>;
</span><span>$config</span>['index_page'] = ''<span>;
</span><span>$config</span>['uri_protocol']     = 'AUTO'<span>;
</span><span>$config</span>['url_suffix'] = '.html'<span>;
</span><span>$config</span>['language']  = 'english'<span>;
</span><span>$config</span>['charset'] = 'UTF-8'<span>;
</span><span>$config</span>['enable_hooks'] = <span>FALSE</span><span>;
&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;</span>
登入後複製

不仅如此,CI还允许你将配置参数放到主配置文件之外。例如,你可以定义自己的配置文件为Config_app.php, 然后在你的应用程序控制器中这样加载你的配置文件:

<span>$this</span>->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组件中较核心的方法之一,该函数的签名:

<span>function</span> load(<span>$file</span> = '', <span>$use_sections</span> = <span>FALSE</span>, <span>$fail_gracefully</span> = <span>FALSE</span>)
登入後複製

所有的参数都是可选参数。

我们这里简单解释一下各形参的含义:

  $file 需要加载的配置文件,可以包含后缀名也不可以不包含,如果未指定该参数,则默认加载Config.php文件

  $user_sections: 是否为加载的配置文件使用独立的section,这么说可能还是不明白,试想,如果你定义了自己的配置文件,而你的配置文件中的配置项可能与Config.php文件中的配置项冲突,通过指定$section为true可以防止配置项的覆盖。

  $fail_gracefully: 要load的配置文件不存在时的处理。Gracefully意为优雅的,如果该参数设置为true,则在文件不存在时只会返回false,而不会显示错误。

下面看该方法的具体实现:

(1). 配置文件名预处理:

<span>$file</span> = (<span>$file</span> == '') ? 'config' : <span>str_replace</span>('.php', '', <span>$file</span>);
登入後複製

这个$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.');
}
登入後複製

3.  获取配置项item,slash_item

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], '/').'/';
}
登入後複製

4.  获取站点site_url, base_url,system_url

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

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).'/';
}
登入後複製

5.  获取URI String: _uri_string

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&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组件,会省不少事啊。

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/909743.htmlTechArticleCI框架源码阅读笔记7 配置管理组件 Config.php,ciconfig.php 一个灵活可控的应用程序中,必然会存在大量的可控参数(我们称为配置),例如在...
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何評估Java框架商業支援的性價比 如何評估Java框架商業支援的性價比 Jun 05, 2024 pm 05:25 PM

評估Java框架商業支援的性價比涉及以下步驟:確定所需的保障等級和服務等級協定(SLA)保證。研究支持團隊的經驗和專業知識。考慮附加服務,如昇級、故障排除和效能最佳化。權衡商業支援成本與風險緩解和提高效率。

PHP 框架的學習曲線與其他語言框架相比如何? PHP 框架的學習曲線與其他語言框架相比如何? Jun 06, 2024 pm 12:41 PM

PHP框架的學習曲線取決於語言熟練度、框架複雜性、文件品質和社群支援。與Python框架相比,PHP框架的學習曲線較高,而與Ruby框架相比,則較低。與Java框架相比,PHP框架的學習曲線中等,但入門時間較短。

PHP 框架的輕量級選項如何影響應用程式效能? PHP 框架的輕量級選項如何影響應用程式效能? Jun 06, 2024 am 10:53 AM

輕量級PHP框架透過小體積和低資源消耗提升應用程式效能。其特點包括:體積小,啟動快,記憶體佔用低提升響應速度和吞吐量,降低資源消耗實戰案例:SlimFramework創建RESTAPI,僅500KB,高響應性、高吞吐量

谷歌管理器如何新增和管理用戶 谷歌管理器如何新增和管理用戶 Sep 02, 2024 pm 02:41 PM

谷歌管理器如何新增和管理使用者?谷歌瀏覽器是支援多個用戶登入使用的,這樣我們跨裝置登入也不用怕了,如果我們有許多用戶就需要對其進行添加管理。有些小夥伴可能不知道要如何操作。別擔心,小編今天為大家整理了詳細步驟教學說明,有興趣的夥伴快來和小編一起看看吧。詳細步驟教學說明1、開啟電腦後,在桌面上找到安裝好的Google瀏覽器圖示並雙擊打開,如下圖所示。 2.點擊Google瀏覽器右上角的三小點圖標,如下圖所示。 3.點選Google瀏覽器下拉選單裡的【設定】選項,如下圖所示。 4.在開啟的Google瀏覽器設定介面裡, 點選【管理ch

如何為不同的應用場景選擇最佳的golang框架 如何為不同的應用場景選擇最佳的golang框架 Jun 05, 2024 pm 04:05 PM

根據應用場景選擇最佳Go框架:考慮應用類型、語言特性、效能需求、生態系統。常見Go框架:Gin(Web應用)、Echo(Web服務)、Fiber(高吞吐量)、gorm(ORM)、fasthttp(速度)。實戰案例:建構RESTAPI(Fiber),與資料庫互動(gorm)。選擇框架:效能關鍵選fasthttp,靈活Web應用選Gin/Echo,資料庫互動選gorm。

Java框架學習路線圖:不同領域中的最佳實踐 Java框架學習路線圖:不同領域中的最佳實踐 Jun 05, 2024 pm 08:53 PM

針對不同領域的Java框架學習路線圖:Web開發:SpringBoot和PlayFramework。持久層:Hibernate和JPA。服務端響應式程式設計:ReactorCore和SpringWebFlux。即時計算:ApacheStorm和ApacheSpark。雲端運算:AWSSDKforJava和GoogleCloudJava。

Golang框架學習過程中常見的迷思有哪些? Golang框架學習過程中常見的迷思有哪些? Jun 05, 2024 pm 09:59 PM

Go框架學習的迷思有以下5種:過度依賴框架,限制彈性。不遵循框架約定,程式碼難以維護。使用過時庫,帶來安全和相容性問題。過度使用包,混淆程式碼結構。忽視錯誤處理,導致意外行為和崩潰。

golang框架開發實戰詳解:問題答疑 golang框架開發實戰詳解:問題答疑 Jun 06, 2024 am 10:57 AM

在Go框架開發中,常見的挑戰及其解決方案是:錯誤處理:利用errors套件進行管理,並使用中間件集中處理錯誤。身份驗證和授權:整合第三方庫並建立自訂中間件來檢查憑證。並發處理:利用goroutine、互斥鎖和通道來控制資源存取。單元測試:使用gotest包,模擬和存根隔離,並使用程式碼覆蓋率工具確保充分性。部署和監控:使用Docker容器打包部署,設定資料備份,透過日誌記錄和監控工具追蹤效能和錯誤。

See all articles