Разбор расширенного файла 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, не стесняйтесь отправлять запросы на перенос, проблемы и т. д.


person Austin Hyde    schedule 13.07.2010    source источник
comment
Вы можете объяснить, почему Zend_Config_ * такой раздутый? Да, и вам не нужно все дерево ... Просто Zend_Config, Zend_Config_Exception и Zend_Exception   -  person Jani Hartikainen    schedule 14.07.2010
comment
Просматривая исходный файл, Zend_Config_Ini зависит от Zend_Config_Exception и Zend_Config. Zend_Config_Exception зависит от Zend_Exception. Вся функциональность распределена по нескольким классам и файлам. Конечно, я очень высоко ценю ZF за его ООП-дизайн, но в данном случае это действительно слишком.   -  person Austin Hyde    schedule 14.07.2010
comment
Все, что я хочу, это преобразовать ini в ассоциативный массив. Мне не нужны и не нужны дополнительные функции, которые есть в Zend_Config.   -  person Austin Hyde    schedule 14.07.2010
comment
Ваш выбор, если вы хотите сделать это сложным путем :) Хотя вы, вероятно, могли бы внести небольшие изменения в Zend_Config_Ini для удаления зависимостей.   -  person Jani Hartikainen    schedule 14.07.2010
comment
Я боялся этого ... Спасибо за подсказку, Яни.   -  person Austin Hyde    schedule 14.07.2010
comment
Я бы не назвал Zend_Config раздутым. Раздутый подразумевает чрезмерную функциональность, которая, в свою очередь, подразумевает функциональность. Мы говорим о Zend Build your own ACL реализации! Рамки здесь.   -  person Kalium    schedule 14.07.2010


Ответы (4)


Не уверен, следует ли мне отредактировать свой старый ответ или добавить новый.

Попробуйте эту версию, это должно быть то, что вы ищете.

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
    )
)
person Viper_Sb    schedule 15.07.2010
comment
Привет спасибо! Это именно то, что я ищу. Я бы ни за что не смог придумать это так лаконично. Для справки в будущем, поскольку это поправка к вашему другому ответу, вам следует отредактировать его. - person Austin Hyde; 16.07.2010
comment
Я знаю, что это старый пост, но он все еще полезен, хотя есть ошибка с функцией recursive_parse. Во втором цикле после первого цикла $b = array($v => $b); $ b не будет установлен. и если $ b не задано, рекурсивное слияние не будет выполнено на $ b [$ x [0]] - person DarkMukke; 04.11.2013

Во-первых, чтобы ответить на один вопрос, вложение свойств доступно из 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 массива БУДУТ присутствовать до этого момента.

Надеюсь, это было то, что вы искали.

person Viper_Sb    schedule 14.07.2010
comment
То, что вы называете вложением свойств, - это просто разделы ([foo] или [foo : bar]). Ваша функция обрабатывает наследование разделов (тоже очень хорошо, я мог бы добавить), но не вложение свойств, как я имею в виду: b.a = 5 должен генерировать [b]=>Array([a]=>5). - person Austin Hyde; 15.07.2010

Я написал что-то вроде этого, и пока у меня все работает нормально:

        $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] );
            }

        }
person barthPL    schedule 15.06.2013

Другой вариант - это пара, сборка и разбор.

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;
}
person David H.    schedule 22.03.2016