PHP 配置文件解析
作者:Wucl
时间:2014-12-29
章节内容:基础背景、实现过程、个人心得(这个人非常没品德,想到什么就写什么)。
1. 基础背景:
配置文件的应用需要达到两个目标,分别是:成功解析.ini和.php文件以及把解析出来的内容作为数组或对象调用。
2. 实现过程:
1) 使用include引入php文件,从而获取文件里设置的数组数据。
2) 使用parse_ini_file解析ini文件,从而获取解析文件后的数组数据。
3) 通过1)和2)我们可以初步确定如何解析配置文件的方向,为什么只是初步呢?因为parse_ini_file只能解析一层ini文件结构。为了解决这个问题我们决定使用分隔符来进行多层结构的表示,然后通过特定的逻辑操作解析出来(解析代码请参考附录);
4) 通过以上几步,数组数据已经获得。那么,接下来是考虑怎么设置成一个可调用的对象,经过多次的尝试我们决定通过实例本类加上魔术函数__get()获取值。(请参考附录代码)
5) 调用的demo(config.php由于代码太多只在附录展示):
Php.ini:
[basic]
mode = dev
host = 127.0.0.1
[app]
path.host = home
使用例子:
/**
* 测试配置文件解析
*/
private function testConfig(){
$basic = Registry::getObject('db');
$arr = Registry::getArray();
var_dump($basic->magic);
var_dump($arr['db']);
}
Registry:
/**
* 获取解析配置文件内容的数组
*/
public static function getArray($file = ''){
$conf = Config::getInstance($file);
return $conf->getContentArray();
}
/**
* 获取解析配置文件内容的对象
*/
public static function getObject($key = '', $file = ''){
$conf = Config::getInstance($file);
$obj = $conf->getContentObject();
if ($key == "") {
return $conf;
} else {
return $conf->$key;
}
}
3. 个人心得:
我们解决问题的过程是一步一个脚印,作出来的东西可能比较适合我们自身所在环境。同时局限性也可能比较大。
4. 附录:
Config.php:
/**
* @desc 解析配置文件(改进于:Ryan)
* @author wucl
* @since 2014-12-23
*/
namespace Library\Core;
class Config extends Singleton {
private $nestSeparator = '.';
private $filename = '';
protected $data = array();
/**
* 配置调用
* @param string $filename 文件名+后缀
* @param array $array 配置数组
* @param string $appName 应用名称
*/
protected function __construct($filename = '', $arr = array()){
if(is_array($arr) && !empty($arr)){
$this->setData($arr);
}else{
$this->filename = $filename;
}
}
/**
* 单例化类
*/
public static function getInstance($filename = '', $arr = array()){
return self::instance($filename, $arr);
}
/**
* 设置data数据
*/
private function setData($arr){
foreach ($arr as $key => $value) {
if (is_array($value)) {
$this->data[$key] = new static('', $value);
} else {
$this->data[$key] = $value;
}
}
}
/**
* 获取解析配置文件内容的对象
*/
public function getContentObject(){
$arr = $this->getContentArray();
$this->setData($arr);
return $this;
}
/**
* 获取解析配置文件内容的数组
*/
public function getContentArray(){
$type = $this->filename ? pathinfo($this->filename)['extension'] : 'ini';
$filename = $this->getFullName($type, $this->filename);
switch ($type) {
case "ini":
$config = parse_ini_file($filename, true);
break;
case "php":
$config = include($filename);
break;
default:
$config = array();
break;
}
$config = $this->process($config);
return $config;
}
/**
* 获取文件路径
*/
public function getFullPath($type){
if ($type == "ini") {
$path = WEB_ROOT . DS . 'config' . DS;
} else {
$path = APP_PATH . DS . 'config' . DS;
}
return $path;
}
/**
* 获取配置文件全名
*/
public function getFullName($type, $filename){
$path = $this->getFullPath($type);
if(!empty($filename)){
$name = $path.$filename;
}else if ($type == "ini") {
$name = $path."app.ini";
} else {
$name = $path."config.php";
}
return $name;
}
/**
* 魔术函数获取数据
*/
public function __get($name){
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return null;
}
/**
* Process data from the parsed ini file.
* @param array $data
* @return array
*/
protected function process(array $data){
$config = array();
foreach ($data as $section => $value) {
if (is_array($value)) {
if (strpos($section, $this->nestSeparator) !== false) {
$sections = explode($this->nestSeparator, $section);
$config = array_merge_recursive($config, $this->buildNestedSection($sections, $value));
} else {
$config[$section] = $this->processSection($value);
}
} else {
$this->processKey($section, $value, $config);
}
}
return $config;
}
/**
* Process a section.
* @param array $section
* @return array
*/
protected function processSection(array $section){
$config = array();
foreach ($section as $key => $value) {
$this->processKey($key, $value, $config);
}
return $config;
}
/**
* Process a key.
* @param string $key
* @param string $value
* @param array $config
* @return array
*/
protected function processKey($key, $value, array &$config){
if (strpos($key, $this->nestSeparator) !== false) {
$pieces = explode($this->nestSeparator, $key, 2);
if (!strlen($pieces[0]) || !strlen($pieces[1])) {
// throw new Exception\RuntimeException(sprintf('Invalid key "%s"', $key));
} elseif (!isset($config[$pieces[0]])) {
if ($pieces[0] === '0' && !empty($config)) {
$config = array($pieces[0] => $config);
} else {
$config[$pieces[0]] = array();
}
} elseif (!is_array($config[$pieces[0]])) {
// throw new Exception\RuntimeException(sprintf(
// 'Cannot create sub-key for "%s", as key already exists', $pieces[0]
// ));
}
$this->processKey($pieces[1], $value, $config[$pieces[0]]);
} else {
if ($key === '@include') {
if ($this->directory === null) {
// throw new Exception\RuntimeException('Cannot process @include statement for a string config');
}
$reader = clone $this;
$include = $reader->fromFile($this->directory . '/' . $value);
$config = array_replace_recursive($config, $include);
} else {
$config[$key] = $value;
}
}
}
/**
* Process a nested section
* @param array $sections
* @param mixed $value
* @return array
*/
private function buildNestedSection($sections, $value){
if (count($sections) == 0) {
return $this->processSection($value);
}
$nestedSection = array();
$first = array_shift($sections);
$nestedSection[$first] = $this->buildNestedSection($sections, $value);
return $nestedSection;
}
}