一个非常完美的读写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作的文本留言本的例子(三)
Oct 09 PHP
PHP ? EasyUI DataGrid 资料存的方式介绍
Nov 07 PHP
php在window iis的莫名问题的测试方法
May 14 PHP
php function用法如何递归及return和echo区别
Mar 07 PHP
PHP中的替代语法介绍
Jan 09 PHP
php判断数组中是否存在指定键(key)的方法
Mar 17 PHP
WordPress中获取指定分类及其子分类下的文章数目
Dec 31 PHP
2017年最好用的9个php开发工具推荐(超好用)
Oct 23 PHP
Laravel模型间关系设置分表的方法示例
Apr 21 PHP
微信公众平台开发教程⑤ 微信扫码支付模式介绍
Apr 10 PHP
gearman管理工具GearmanManager的安装与php使用方法示例
Feb 27 PHP
php生成随机数/生成随机字符串的方法小结【5种方法】
May 27 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
set_include_path在win和linux下的区别
2008/01/10 PHP
PHP下利用shell后台运行PHP脚本,并获取该脚本的Process ID的代码
2011/09/19 PHP
php使HTML标签自动补全闭合函数代码
2012/10/04 PHP
php二维数组用键名分组相加实例函数
2013/11/06 PHP
CI框架(CodeIgniter)公共模型类定义与用法示例
2017/08/10 PHP
小议Javascript中的this指针
2010/03/18 Javascript
javascript基础第一章 JavaScript与用户端
2010/07/22 Javascript
jquery中实现简单的tabs插件功能的代码
2011/03/02 Javascript
在图片上显示左右箭头类似翻页的代码
2013/03/04 Javascript
javascript设置页面背景色及背景图片的方法
2015/12/29 Javascript
JavaScript中的继承之类继承
2016/05/01 Javascript
Node.js刷新session过期时间的实现方法推荐
2016/05/18 Javascript
微信小程序 数据封装,参数传值等经验分享
2017/01/09 Javascript
Angular中ng-bind和ng-model的区别实例详解
2017/04/10 Javascript
jQuery使用正则验证15/18身份证的方法示例
2017/04/27 jQuery
js实现canvas图片与img图片的相互转换的示例
2017/08/31 Javascript
Windows下使用Nodejs运行js的方法
2017/09/02 NodeJs
JS学习笔记之贪吃蛇小游戏demo实例详解
2019/05/29 Javascript
vue element-ui el-date-picker限制选择时间为当天之前的代码
2019/11/07 Javascript
Python安装Imaging报错:The _imaging C module is not installed问题解决方法
2014/08/22 Python
TensorFlow实现Softmax回归模型
2018/03/09 Python
python 判断文件还是文件夹的简单实例
2019/06/10 Python
numpy和pandas中数组的合并、拉直和重塑实例
2019/06/28 Python
python中类的输出或类的实例输出为这种形式的原因
2019/08/12 Python
基于 HTML5 WebGL 实现的垃圾分类系统
2019/10/08 HTML / CSS
鲜为人知的HTML5语音合成功能
2019/05/17 HTML / CSS
Bravofly德国:预订廉价航班和酒店
2019/09/22 全球购物
斯洛伐克最大的婴儿食品和用品网上商店:Feedo.sk
2020/12/21 全球购物
城市轨道交通工程职业生涯规划书范文
2014/09/16 职场文书
小学生作文评语集锦
2014/12/25 职场文书
《棉鞋里的阳光》教学反思
2016/02/20 职场文书
技术转让协议书
2016/03/19 职场文书
工作建议书范文
2019/07/08 职场文书
Python带你从浅入深探究Tuple(基础篇)
2021/05/15 Python
解决linux下redis数据库overcommit_memory问题
2022/02/24 Redis
Nginx报错104:Connection reset by peer问题的解决及分析
2022/07/23 Servers