一个非常完美的读写ini格式的PHP配置类分享
基本满足所有配置相关的需求。
/**
*解析.ini格式的配置文件为一个树形结构的对象
*配置文件不同section通过冒号继承
*默认根据hostname确定使用的section,如果不能确定就优先使用production
*检测环境的时候总是优先检测production,其余section按定义顺序检测
*
*@authorares@phpdr.net
*
*/
classConfig{
/**
*解析后的配置文件
*
*@varstdClass
*/
private$config;
/**
*一个二维数组,键是配置文件的section
*值是一个数组或回调函数
*如果是数组则计算hostname是否在数组中决定是否使用该section
*如果是回调函数通过返回值true或false来确定是否使用该section
*
*@vararray
*/
private$map=array();
/**
*section会被解析,:表示继承
*配置项中的'.'用来区分层级关系
*section中的'.'不会被解析,配置中的数组不受影响。
*
*@paramstring$conf
*@throwsErrorException
*@returnstdClass
*/
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{
thrownewErrorException('productionsectionnotfoundinconfig');
}
$section='production';
$hostname=gethostname();
foreach($mapas$k=>$v){
if(is_array($v)){
foreach($vas$v1){
if($v1==$hostname){
$section=$k;
break2;
}
}
}elseif(is_callable($v)){
if(true==call_user_func($v)){
$section=$k;
break;
}
}else{
thrownewErrorException('Wrongmapvaluein'.__CLASS__);
}
}
$this->config=$config->$section;
}
/**
*总是返回配置对象
*
*@returnmixed
*/
function__get($key){
if(isset($this->config->$key)){
return$this->config->$key;
}
}
/**
*切分
*
*@paramstdClass$v
*@paramstring$k1
*@parammixed$v1
*/
privatefunctionsplit($v,$k1,$v1){
$keys=explode('.',$k1);
$last=array_pop($keys);
$node=$v;
foreach($keysas$v2){
if(!isset($node->$v2)){
$node->$v2=newstdClass();
}
$node=$node->$v2;
}
$node->$last=$v1;
if(count($keys)>0){
unset($v->$k1);
}
}
/**
*parseIni
*
*@paramobject$conf
*@returnstdClass
*/
privatefunctionparseIni($conf){
$confObj=newstdClass();
foreach($confas$k=>$v){
//是section
if(is_array($v)){
$confObj->$k=(object)$v;
foreach($vas$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($confObjas$k=>$v){
if(false!==strpos($k,':')){
if(0===strpos($k,':')){
thrownewErrorException('config'.$k.'isinvalid,':'can'tbethefirstchar');
}elseif(1<substr_count($k,':')){
thrownewErrorException('config'.$k.'isinvalid,':'canappearonlyonce');
}else{
$keys=explode(':',$k);
if(!isset($confObj->$keys[1])){
thrownewErrorException('parentsection'.$keys[1].'doesn'texistinconfigfile');
}else{
if(isset($confObj->$keys[0])){
thrownewErrorException('configisinvalid,'.$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默认是浅克隆,函数实现深克隆
*
*@paramobject$obj
*@returnobject$obj
*/
privatefunctiondeepCloneR($obj){
$objClone=clone$obj;
foreach($objCloneas$k=>$v){
if(is_object($v)){
$objClone->$k=$this->deepCloneR($v);
}
}
return$objClone;
}
/**
*递归的合并两个对象
*
*@paramunknown$obj1
*@paramunknown$obj2
*/
privatefunctionobjectMergeR($obj1,$obj2){
foreach($obj2as$k=>$v){
if(is_object($v)&&isset($obj1->$k)&&is_object($obj1->$k)){
$this->objectMergeR($obj1->$k,$v);
}else{
$obj1->$k=$v;
}
}
}
}
简单使用:
$_ENV['config']=newConfig(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