一个非常完美的读写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环境配置之CGI、FastCGI、PHP-CGI、PHP-FPM、Spawn-FCGI比较?
Oct 17 PHP
有关php运算符的知识大全
Nov 03 PHP
PHP __autoload()方法真的影响性能吗?
Mar 30 PHP
谈谈关于php的优点与缺点
Apr 11 PHP
PHP临时文件的安全性分析
Jul 04 PHP
ThinkPHP框架任意代码执行漏洞的利用及其修复方法
Jul 04 PHP
qq登录,新浪微博登录接口申请过程中遇到的问题
Jul 22 PHP
php微信公众平台配置接口开发程序
Sep 22 PHP
CakePHP框架Session设置方法分析
Feb 23 PHP
PHP实现权限管理功能示例
Sep 22 PHP
详细对比php中类继承和接口继承
Oct 11 PHP
smarty模板的使用方法实例分析
Sep 18 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
将OICQ数据转成MYSQL数据
2006/10/09 PHP
destoon实现调用图文新闻的方法
2014/08/21 PHP
深入解析WordPress中加载模板的get_template_part函数
2016/01/11 PHP
10个值得深思的PHP面试题
2016/11/14 PHP
php从数据库中读取特定的行(实例)
2017/06/02 PHP
PHP实现的权重算法示例【可用于游戏根据权限来随机物品】
2019/02/15 PHP
PHP 范围解析操作符(::)用法分析【访问静态成员和类常量】
2020/04/14 PHP
google地图的路线实现代码
2009/08/20 Javascript
JavaScript高级程序设计(第3版)学习笔记13 ECMAScript5新特性
2012/10/11 Javascript
js将当前时间格式转换成时间搓(自写)
2013/09/26 Javascript
js加入收藏夹代码(兼容ie/ff/op)
2014/05/16 Javascript
轻松创建nodejs服务器(10):处理上传图片
2014/12/18 NodeJs
jQuery+Ajax实现无刷新操作
2016/01/04 Javascript
JS实现的简易拖放效果示例
2016/12/29 Javascript
ComboBox(下拉列表框)通过url加载调用远程数据的方法
2017/08/06 Javascript
jQuery实现每隔一段时间自动更换样式的方法分析
2018/05/03 jQuery
js遍历添加栏目类添加css 再点击其它删除css【推荐】
2018/06/12 Javascript
利用vue重构有赞商城的思路以及总结整理
2019/02/21 Javascript
微信小程序下拉加载和上拉刷新两种实现方法详解
2019/09/05 Javascript
VUE DEMO之模拟登录个人中心页面之间数据传值实例
2019/10/31 Javascript
[57:28]2018DOTA2亚洲邀请赛 4.6 淘汰赛 TNC vs Liquid 第一场
2018/04/10 DOTA
按日期打印Python的Tornado框架中的日志的方法
2015/05/02 Python
在Django的视图中使用form对象的方法
2015/07/18 Python
Python常见异常分类与处理方法
2017/06/04 Python
python使用folium库绘制地图点击框
2018/09/21 Python
使用python 对验证码图片进行降噪处理
2019/12/18 Python
pycharm双击无响应(打不开问题解决办法)
2020/01/10 Python
Python selenium抓取虎牙短视频代码实例
2020/03/02 Python
西部世纪.net笔试题面试题
2014/04/03 面试题
医药代表个人的求职信分享
2013/12/08 职场文书
销售人员获奖感言
2014/02/05 职场文书
委托书的写法
2014/08/30 职场文书
2014年学校体育工作总结
2014/12/08 职场文书
2016教师学习党章心得体会
2016/01/15 职场文书
HR必备:销售经理聘用合同范本
2019/08/21 职场文书
mysql使用FIND_IN_SET和group_concat两个方法查询上下级机构
2022/04/20 MySQL