一个非常完美的读写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 相关文章推荐
通过html表格发电子邮件
Oct 09 PHP
php下关于Cannot use a scalar value as an array的解决办法
Aug 08 PHP
PHP中全局变量global和$GLOBALS[]的区别分析
Aug 06 PHP
file_get_contents获取不到网页内容的解决方法
Mar 07 PHP
php调用c接口无错版介绍
Mar 11 PHP
一个比较不错的PHP日历类分享
Nov 18 PHP
Yii快速入门经典教程
Dec 28 PHP
Zend Framework实现多文件上传功能实例
Mar 21 PHP
利用PHPExcel实现Excel文件的写入和读取
Apr 26 PHP
PHP从尾到头打印链表实例讲解
Sep 27 PHP
Laravel框架之解决前端显示图片问题
Oct 24 PHP
PHP实现rar解压读取扩展包小结
Jun 03 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 分页类(模仿google)-面试题目解答
2009/09/13 PHP
PHP laravel中的多对多关系实例详解
2017/06/07 PHP
Javascript SHA-1:Secure Hash Algorithm
2006/12/20 Javascript
javascript 简单抽屉效果的实现代码
2010/03/09 Javascript
基于JQuery实现鼠标点击文本框显示隐藏提示文本
2012/02/23 Javascript
js获取元素到文档区域document的(横向、纵向)坐标的两种方法
2013/05/17 Javascript
前端开发过程中浏览器版本的两种判定方法
2013/10/30 Javascript
js改变鼠标的形状和样式的方法
2014/03/31 Javascript
js实现在同一窗口浏览图片
2014/09/17 Javascript
javascript实现单击和双击并存的方法
2014/12/13 Javascript
JavaScript 数组some()和filter()的用法及区别
2016/05/20 Javascript
js实现简单的网页换肤效果
2017/01/18 Javascript
JavaScript函数节流和函数防抖之间的区别
2017/02/15 Javascript
详解webpack 多页面/入口支持&amp;公共组件单独打包
2017/06/29 Javascript
vue项目webpack中Npm传递参数配置不同域名接口
2018/06/15 Javascript
详解vue通过NGINX部署在子目录或者二级目录实践
2018/09/03 Javascript
vue2之简易的pc端短信验证码的问题及处理方法
2019/06/03 Javascript
如何利用JavaScript编写更好的条件语句详解
2020/08/10 Javascript
python字典排序实例详解
2015/05/20 Python
将Python的Django框架与认证系统整合的方法
2015/07/24 Python
初学python的操作难点总结(新手必看篇)
2017/08/03 Python
python装饰器常见使用方法分析
2019/06/26 Python
python scrapy重复执行实现代码详解
2019/12/28 Python
Python批量启动多线程代码实例
2020/02/18 Python
python字典和json.dumps()的遇到的坑分析
2020/03/11 Python
Keras SGD 随机梯度下降优化器参数设置方式
2020/06/19 Python
常用的10个Python实用小技巧
2020/08/10 Python
英国著名的化妆品折扣网站:Allbeauty.com
2016/07/21 全球购物
百丽国际旗下购物网站:优购
2017/02/28 全球购物
JAVA程序员面试题
2012/10/03 面试题
学生无故旷课检讨书
2014/09/20 职场文书
北京青年观后感
2015/06/15 职场文书
2019年大学毕业生个人自我鉴定范文大全
2019/03/21 职场文书
《三国志》赏析
2019/08/27 职场文书
Python办公自动化之教你如何用Python将任意文件转为PDF格式
2021/06/28 Python
Python中文纠错的简单实现
2021/07/07 Python