Разбор "продвинутого" INI файла с помощью PHP
В принципе, мне бы хотелось, чтобы простой, простой и однофайловый способ синтаксического анализа 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, и будет супер-раздутым и настраиваемым.
Есть ли небольшая и простая библиотека для анализа этого?
Если нет, есть ли простая реализация, которую я не вижу?
Маленьким и простым я имею в виду что-то вроде sfYaml файлов INI.
К моим (очень неопытным) глазам я должен был бы разобрать один раз с помощью parse_ini_file
, затем вернуться и разрешить наследование, затем пропустить каждую секцию и развернуть ключи рекурсивно...
ОБНОВЛЕНИЕ. Поскольку это, кажется, популярный вопрос, я хотел бы отметить, что У меня есть простой класс, реализующий это на GitHub, не стесняйтесь отправлять запросы на тягу, проблемы и т.д.
Ответы
Ответ 1
Не уверен, должен ли я изменить свой старый ответ или добавить новый.
Попробуйте эту версию, это должно быть то, что вы ищете.
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
)
)
Ответ 2
Прежде всего, чтобы ответить на одно, свойство nesting avilable от parse_ini_file(), установите второй параметр в true i.e parse_ini_file ('test.ini', true); Это даст вам многомерный массив i.e
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
)
)
Последние победы в записи i.e
[bar2: foo2: bar: foo]
bar2 выигрывает с этим настройками в своем собственном массиве
ПРИМЕЧАНИЕ. Остальные 3 массива будут там до этой точки.
Надеюсь, это было то, что вы искали.
Ответ 3
Я написал что-то вроде этого, и пока это работает нормально для меня:
$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] );
}
}
Ответ 4
Другая опция - это пара, сборка и анализ.
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;
}