一个非常完美的读写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