使用 PHP 解析“高级”INI 文件
Posted
技术标签:
【中文标题】使用 PHP 解析“高级”INI 文件【英文标题】:Parsing an "advanced" INI file with PHP 【发布时间】:2011-03-15 14:31:40 【问题描述】:基本上,我想要一种简单、简单、单文件的方法来解析具有“高级”功能的 INI 文件,例如部分继承和属性嵌套,例如 Zend_Config_Ini。
例如:
[foo]
a = 1
b.a = 2
b.b = 3
b.c = 4
c = 5
[bar : foo]
b.b = 17
c = 42
会解析成
array(
'foo'=>array(
'a'=>'1',
'b'=>array(
'a'=>'2',
'b'=>'3',
'c'=>'4'
),
'c'=>'5'
),
'bar'=>array(
'a'=>'1',
'b'=>array(
'a'=>'2',
'b'=>'17',
'c'=>'4'
),
'c'=>'42'
)
)
php 的内置 parse_ini_file
,除了带有简单部分和简单键的简单 INI 之外不处理任何其他内容。
我使用Zend_Config_Ini
的问题是我必须包含几乎整个 Zend_Config 子包,并且超级臃肿且可配置。
是否有一个 small 和 simple 库可用于解析这个? 如果没有,是否有我没有看到的简单实现?
我所说的小而简单,是指 INI 文件的 sfYaml。
在我(非常缺乏经验的)眼中,我必须使用 parse_ini_file
解析一次,然后返回并解决继承问题,然后遍历每个部分并递归扩展键...
更新:由于这似乎是一个热门问题,我想注意I have a simple class implementing this on GitHub,请随时发送拉取请求、问题等。
【问题讨论】:
你能解释一下为什么 Zend_Config_* 是“超级臃肿”吗?哦,你不需要整个树......只需 Zend_Config、Zend_Config_Exception 和 Zend_Exception 查看源文件,Zend_Config_Ini 依赖于 Zend_Config_Exception 和 Zend_Config。 Zend_Config_Exception 依赖于 Zend_Exception。所有功能都分布在多个类和文件中。诚然,我对采埃孚的 OOP 设计非常高度重视,但在这种情况下,它确实太过分了。 我想要它做的就是将一个 ini 解析为一个关联数组。我不想要或不需要 Zend_Config 具有的额外功能。 如果你想以艰难的方式来做,你可以选择 :) 虽然你可以对 Zend_Config_Ini 做一些小的修改来移除依赖。 我不会称 Zend_Config 臃肿。臃肿意味着过度功能,这反过来又意味着功能。我们谈论的是 Zend“构建你自己的 ACL 实现!”框架在这里。 【参考方案1】:另一个选项 - 这是对,构建和解析。
function build_ini_string_nested( $data, $path = null )
$content = array();
foreach( $data AS $key => $val )
if( is_array($val) )
$content[] = build_ini_string_nested( $val, ($path ? $path. '.' : '') . $key );
else if( $path )
$content[] = $path . '[' . ($path && is_numeric($key) ? '' : $key) . '] = ' . $val;
else
$content[] = $key . ' = ' . $val;
return implode("\n", $content);
function parse_ini_string_nested( $data, $path = null )
if( is_string($data) )
$data = parse_ini_string($data);
if( $path )
foreach( $data AS $key => $val )
if( strpos( $key, $path.'.' ) !== false )
$find_node = explode('.', $path);
$this_path = reset(explode('.', substr($key, strlen($path.'.'))));
$node =& $data;
do
$node =& $node[ array_shift($find_node) ];
while( count($find_node) );
if( is_array($node[ $this_path ]) )
$node[ $this_path ][] = $val;
else
$node[ $this_path ] = $val;
else
$drop_keys = array();
foreach( $data AS $key => $val )
if( count(explode('.', $key)) > 1 )
$path = reset(explode('.', $key));
$data[ $path ] = array();
$data = parse_ini_string_nested( $data, $path );
$drop_keys[] = $key;
foreach( $drop_keys AS $key )
unset($data[ $key ]);
return $data;
【讨论】:
【参考方案2】:我已经写了这样的东西,现在它对我来说还可以:
$config = array();
$configSrc = parse_ini_file( $filePath, true );
foreach( $configSrc as $sectionName => $section )
$config[$sectionName] = array();
foreach( $section as $itemName => $item )
$itemNameArray = explode( '.', $itemName );
eval( sprintf('$config[$sectionName][\'%s\'] = $item;', join("']['", $itemNameArray)) );
// marge inheritance;
foreach( $config as $sectionName => $section )
$ancestryArray = explode( ':', $sectionName );
$ancestryCount = count( $ancestryArray );
if( $ancestryCount > 1 )
//
$config[$sectionNameTrimmed = trim($ancestryArray[0])] = array();
$ancestryArray = array_reverse( array_slice($ancestryArray, 1) );
foreach( $ancestryArray as $ancestryName )
$ancestryName = trim( $ancestryName );
if( isset($config[$ancestryName]) )
$config[$sectionNameTrimmed] = array_replace_recursive( $config[$sectionNameTrimmed], $config[$ancestryName] );
$config[$sectionNameTrimmed] = array_replace_recursive( $config[$sectionNameTrimmed], $section );
unset( $config[$sectionName] );
【讨论】:
【参考方案3】:不确定我应该编辑旧答案还是添加新答案。
试试这个版本,应该是你要找的。p>
function parse_ini_advanced($array)
$returnArray = array();
if (is_array($array))
foreach ($array as $key => $value)
$e = explode(':', $key);
if (!empty($e[1]))
$x = array();
foreach ($e as $tk => $tv)
$x[$tk] = trim($tv);
$x = array_reverse($x, true);
foreach ($x as $k => $v)
$c = $x[0];
if (empty($returnArray[$c]))
$returnArray[$c] = array();
if (isset($returnArray[$x[1]]))
$returnArray[$c] = array_merge($returnArray[$c], $returnArray[$x[1]]);
if ($k === 0)
$returnArray[$c] = array_merge($returnArray[$c], $array[$key]);
else
$returnArray[$key] = $array[$key];
return $returnArray;
function recursive_parse($array)
$returnArray = array();
if (is_array($array))
foreach ($array as $key => $value)
if (is_array($value))
$array[$key] = recursive_parse($value);
$x = explode('.', $key);
if (!empty($x[1]))
$x = array_reverse($x, true);
if (isset($returnArray[$key]))
unset($returnArray[$key]);
if (!isset($returnArray[$x[0]]))
$returnArray[$x[0]] = array();
$first = true;
foreach ($x as $k => $v)
if ($first === true)
$b = $array[$key];
$first = false;
$b = array($v => $b);
$returnArray[$x[0]] = array_merge_recursive($returnArray[$x[0]], $b[$x[0]]);
else
$returnArray[$key] = $array[$key];
return $returnArray;
会这样称呼:
$array = parse_ini_file('test.ini', true);
$array = recursive_parse(parse_ini_advanced($array));
这可以做得更好/更清晰,但对于一个简单的解决方案,它应该工作得很好。
如果你的配置是:
[foo]
a = 1
b.a = 2
b.b = 3
b.c = 4
c = 5
[bar : foo]
b.x.c = 33
b.b = 17
c = 42
[hot : bar : foo]
b.a = 83
b.d = 23
输出应该是:
Array
(
[foo] => Array
(
[a] => 1
[b] => Array
(
[a] => 2
[b] => 3
[c] => 4
)
[c] => 5
)
[bar] => Array
(
[a] => 1
[b] => Array
(
[a] => 2
[b] => 17
[c] => 4
[x] => Array
(
[c] => 33
)
)
[c] => 42
)
[hot] => Array
(
[a] => 1
[b] => Array
(
[a] => 83
[b] => 17
[c] => 4
[x] => Array
(
[c] => 33
)
[d] => 23
)
[c] => 42
)
)
【讨论】:
嘿,谢谢!这正是我正在寻找的。我不可能如此简洁地提出这个问题。供将来参考,因为它是对您其他答案的修正,您应该对其进行编辑。 我知道这是一篇旧帖子,但仍然有用,尽管 recursive_parse 函数存在错误。在第二个 foreach 中,在第一个循环之后$b = array($v => $b);
$b 将不会被设置。如果 $b 未设置,递归合并将在 $b[$x[0]] 上失败【参考方案4】:
首先回答一件事,属性嵌套可以从 parse_ini_file() 中获得,将第二个参数设置为 true 即 parse_ini_file('test.ini', true);这将为您提供一个多维数组,即
Array
(
[foo] => Array
(
[a] => 1
[b.a] => 2
[b.b] => 3
[b.c] => 4
[c] => 5
)
[bar : foo] => Array
(
[b.b] => 17
[c] => 42
)
)
这是一个小函数,它将解析 parse_ini_file() 返回的数组并将其转换为类别。
/**
* Parse INI files Advanced
* process_sections = true
* scanner_mode = default
*
* Supports section inheritance
* and has property nesting turned on
*
* @param string $filename
* return array
*/
function parse_ini_file_advanced($filename)
$array = parse_ini_file($filename, true);
$returnArray = array();
if (is_array($array))
foreach ($array as $key => $value)
$x = explode(':', $key);
if (!empty($x[1]))
$x = array_reverse($x, true);
foreach ($x as $k => $v)
$i = trim($x[0]);
$v = trim($v);
if (empty($returnArray[$i]))
$returnArray[$i] = array();
if (isset($array[$v]))
$returnArray[$i] = array_merge($returnArray[$i], $array[$v]);
if ($k === 0)
$returnArray[$i] = array_merge($returnArray[$i], $array[$key]);
else
$returnArray[$key] = $array[$key];
else
return false;
return $returnArray;
它会返回这个:
Array
(
[foo] => Array
(
[a] => 1
[b.a] => 2
[b.b] => 3
[b.c] => 4
[c] => 5
)
[bar] => Array
(
[a] => 1
[b.a] => 2
[b.b] => 17
[b.c] => 4
[c] => 42
)
)
最后写入获胜,即 [bar2:foo2:bar:foo] bar2 以其自己的数组中的设置获胜 注意:其他 3 个阵列将在那里。
希望这就是你要找的。p>
【讨论】:
好吧,你所说的属性嵌套只是部分([foo]
或[foo : bar]
)。您的函数处理部分继承(非常好,我可能会添加),但不是我的意思的属性嵌套:b.a = 5
应该生成 [b]=>Array([a]=>5)
。以上是关于使用 PHP 解析“高级”INI 文件的主要内容,如果未能解决你的问题,请参考以下文章