一个非常完美的读写ini格式的PHP配置类分享


Posted in PHP onFebruary 12, 2015

基本满足所有配置相关的需求。

/**
 * 解析.ini格式的配置文件为一个树形结构的对象
 * 配置文件不同section通过冒号继承
 * 默认根据hostname确定使用的section,如果不能确定就优先使用production
 * 检测环境的时候总是优先检测production,其余section按定义顺序检测
 *
 * @author ares@phpdr.net
 *    
 */
class Config {
 /**
 * 解析后的配置文件
 *
 * @var stdClass
 */
 private $config;
 /**
 * 一个二维数组,键是配置文件的section
 * 值是一个数组或回调函数
 * 如果是数组则计算hostname是否在数组中决定是否使用该section
 * 如果是回调函数通过返回值true或false来确定是否使用该section
 *
 * @var array
 */
 private $map = array ();
 
 /**
 * section会被解析,:表示继承
 * 配置项中的'.'用来区分层级关系
 * section中的'.'不会被解析,配置中的数组不受影响。
 *
 * @param string $conf     
 * @throws ErrorException
 * @return stdClass
 */
 function __construct($conf, $map) {
 $config = $this->parseIni ( ( object ) parse_ini_string ( $conf, true ) );
 if (array_key_exists ( 'production', $map )) {
  $production = $map ['production'];
  unset ( $map ['production'] );
  $map = array_merge ( array (
   'production' => $production ), $map );
 } else {
  throw new ErrorException ( 'production section not found in config' );
 }
 $section = 'production';
 $hostname = gethostname ();
 foreach ( $map as $k => $v ) {
  if (is_array ( $v )) {
  foreach ( $v as $v1 ) {
   if ($v1 == $hostname) {
   $section = $k;
   break 2;
   }
  }
  } elseif (is_callable ( $v )) {
  if (true == call_user_func ( $v )) {
   $section = $k;
   break;
  }
  } else {
  throw new ErrorException ( 'Wrong map value in ' . __CLASS__ );
  }
 }
 $this->config = $config->$section;
 }
 
 /**
 * 总是返回配置对象
 *
 * @return mixed
 */
 function __get($key) {
 if (isset ( $this->config->$key )) {
  return $this->config->$key;
 }
 }
 
 /**
 * 切分
 *
 * @param stdClass $v     
 * @param string $k1     
 * @param mixed $v1     
 */
 private function split($v, $k1, $v1) {
 $keys = explode ( '.', $k1 );
 $last = array_pop ( $keys );
 $node = $v;
 foreach ( $keys as $v2 ) {
  if (! isset ( $node->$v2 )) {
  $node->$v2 = new stdClass ();
  }
  $node = $node->$v2;
 }
 $node->$last = $v1;
 if (count ( $keys ) > 0) {
  unset ( $v->$k1 );
 }
 }
 
 /**
 * parseIni
 *
 * @param object $conf     
 * @return stdClass
 */
 private function parseIni($conf) {
 $confObj = new stdClass ();
 foreach ( $conf as $k => $v ) {
  // 是section
  if (is_array ( $v )) {
  $confObj->$k = ( object ) $v;
  foreach ( $v as $k1 => $v1 ) {
   call_user_func ( array (
    $this,
    'split' ), $confObj->$k, $k1, $v1 );
  }
  } else {
  call_user_func ( array (
   $this,
   'split' ), $confObj, $k, $v );
  }
 }
 unset ( $conf );
 // 处理继承
 foreach ( $confObj as $k => $v ) {
  if (false !== strpos ( $k, ':' )) {
  if (0 === strpos ( $k, ':' )) {
   throw new ErrorException ( 'config ' . $k . ' is invalid, ':' can't be the first char' );
  } elseif (1 < substr_count ( $k, ':' )) {
   throw new ErrorException ( 'config ' . $k . ' is invalid, ':' can appear only once' );
  } else {
   $keys = explode ( ':', $k );
   if (! isset ( $confObj->$keys [1] )) {
   throw new ErrorException ( 'parent section ' . $keys [1] . ' doesn't exist in config file' );
   } else {
   if (isset ( $confObj->$keys [0] )) {
    throw new ErrorException ( 'config is invalid, ' . $keys [0] . ' and ' . $k . ' conflicts' );
   } else {
    $confObj->$keys [0] = $this->deepCloneR ( $confObj->$keys [1] );
    $this->objectMergeR ( $confObj->$keys [0], $v );
    unset ( $confObj->$k );
   }
   }
  }
  }
 }
 return $confObj;
 }
 
 /**
 * php默认是浅克隆,函数实现深克隆
 *
 * @param object $obj     
 * @return object $obj
 */
 private function deepCloneR($obj) {
 $objClone = clone $obj;
 foreach ( $objClone as $k => $v ) {
  if (is_object ( $v )) {
  $objClone->$k = $this->deepCloneR ( $v );
  }
 }
 return $objClone;
 }
 
 /**
 * 递归的合并两个对象
 *
 * @param unknown $obj1     
 * @param unknown $obj2     
 */
 private function objectMergeR($obj1, $obj2) {
 foreach ( $obj2 as $k => $v ) {
  if (is_object ( $v ) && isset ( $obj1->$k ) && is_object ( $obj1->$k )) {
  $this->objectMergeR ( $obj1->$k, $v );
  } else {
  $obj1->$k = $v;
  }
 }
 }
}

简单使用:

$_ENV ['config'] = new Config ( file_get_contents ( __DIR__ . '/config.ini' ), array (
 'development' => array (
  'localhost.localdomain',
  'localhost'
 ),
 'production' => array ()
) );

配置文件示例:

[product]
db.default.dsn="mysql:host=127.0.0.1;dbname=default"
db.default.username=root
db.default.password=123456

admin.username=admin
admin.password=123456

php.error_reporting=E_ALL
php.display_errors=no
php.log_errors=yes
php.error_log=APP_PATH'/resource/log/phpError.log'
php.session.save_path=APP_PATH'/resource/data/session'

[development:product]
db.test1.dsn="mysql:host=127.0.0.1;dbname=test1"
db.test1.username=root
db.test1.password=123456
php.display_errors=yes
PHP 相关文章推荐
优化PHP代码的53条建议
Mar 27 PHP
为PHP初学者的8点有效建议
Nov 20 PHP
php 无法加载mysql的module的时候的配置的解决方案引发的思考
Jan 27 PHP
php的array_multisort()使用方法介绍
May 16 PHP
php利用单例模式实现日志处理类库
Feb 10 PHP
PHP+FastCGI+Nginx配置PHP运行环境
Aug 07 PHP
php计算整个目录大小的方法
Jun 19 PHP
解决php表单重复提交实现方法
Sep 29 PHP
PHP给文字内容中的关键字进行套红处理
Apr 12 PHP
Joomla使用Apache重写模式的方法
May 04 PHP
PHP单例模式定义与使用实例详解
Feb 06 PHP
ThinkPHP3.2框架自定义配置和加载用法示例
Jun 14 PHP
PHP动态输出JavaScript代码实例
Feb 12 #PHP
PHP使用JSON和将json还原成数组
Feb 12 #PHP
服务器上配置PHP运行环境教程
Feb 12 #PHP
PHP保存带BOM文件的方法
Feb 12 #PHP
自己写的php curl库实现整站克隆功能
Feb 12 #PHP
PIGCMS 如何关闭聊天机器人
Feb 12 #PHP
PHP实现抓取Google IP并自动修改hosts文件
Feb 12 #PHP
You might like
php $_SERVER当前完整url的写法
2009/11/12 PHP
说说PHP的autoLoad自动加载机制
2012/09/27 PHP
ubuntu12.04使用c编写php扩展模块教程分享
2013/12/25 PHP
PHP通过插入mysql数据来实现多机互锁实例
2014/11/05 PHP
php判断一个数组是否为有序的方法
2015/03/27 PHP
php实现与python进行socket通信的方法示例
2017/08/30 PHP
PHP读取并输出XML文件数据的简单实现方法
2017/12/22 PHP
jquery 图片轮换效果
2010/07/29 Javascript
一个JavaScript变量声明的知识点
2013/10/28 Javascript
Js表格万条数据瞬间加载实现代码
2014/02/20 Javascript
js触发onchange事件的方法说明
2014/03/08 Javascript
jQuery支持动态参数将函数绑定到事件上的方法
2015/03/17 Javascript
javascript实现模拟时钟的方法
2015/05/13 Javascript
原生JS实现匀速图片轮播动画
2016/10/18 Javascript
jQuery实现二维码扫描功能
2017/01/09 Javascript
利用node.js制作命令行工具方法教程(一)
2017/06/22 Javascript
详解node.js 下载图片的 2 种方式
2018/03/02 Javascript
使用iView Upload 组件实现手动上传图片的示例代码
2018/10/01 Javascript
jQuery实现轮播图源码
2019/10/23 jQuery
[01:45]DOTA2新英雄“神谕者”全方位展示
2014/11/21 DOTA
[08:54]DOTA2-DPC中国联赛 正赛 Aster vs LBZS 选手采访
2021/03/11 DOTA
python基础教程之循环介绍
2014/08/29 Python
基于DataFrame改变列类型的方法
2018/07/25 Python
python使用Matplotlib绘制分段函数
2018/09/25 Python
python微信公众号之关注公众号自动回复
2018/10/25 Python
对Python 检查文件名是否规范的实例详解
2019/06/10 Python
python实现按日期归档文件
2021/01/30 Python
澳大利亚排名第一的露营和户外设备在线零售商:Outbax
2020/05/06 全球购物
环境科学专业个人求职信
2013/12/15 职场文书
大学生职业规划书的范本
2014/02/18 职场文书
房屋租赁协议书范本
2014/04/10 职场文书
个人自荐材料
2014/05/23 职场文书
法定代表人资格证明书
2015/06/18 职场文书
2016大一新生军训心得体会
2016/01/11 职场文书
vue基于Teleport实现Modal组件
2021/05/31 Vue.js
python基础之类属性和实例属性
2021/10/24 Python