11

I need to parse an .ini file into an array, and later change the values of the array and export it to the same .ini file. I managed to read the file, but didn’t find any simple way to write it back. Any suggestions?

Sample .ini file:

1 = 0;
2 = 1372240157;    // timestamp.
Ivar
  • 6,138
  • 12
  • 49
  • 61
user2216190
  • 784
  • 5
  • 10
  • 25
  • 4
    http://stackoverflow.com/questions/1268378/create-ini-file-write-values-in-php may well help you – Novocaine Jun 26 '13 at 09:54
  • 1
    isn't it better to use json? – Robert Jun 26 '13 at 09:58
  • 1
    Depends on the application Robert. If the data is intended to be stored and retrieved without human interaction, or transmitted across a network (ajax, etc), then json is appropriate. If the data is going to occasionally need to be manually edited by humans who may or may not be developers, having it in a more readable .ini format is appropriate. It should also be mentioned that .ini files actually parse faster than php files. – mopsyd Nov 27 '14 at 19:40

5 Answers5

11

In order to write the .ini file back, you need to create your own function, for PHP offers no functions out of the box other than for reading (which can be found here: http://php.net/manual/pl/function.parse-ini-file.php).

An example of function that might encapsulate a multidimensional array to .ini-syntax compatible string might look like this:

function arr2ini(array $a, array $parent = array())
{
    $out = '';
    foreach ($a as $k => $v)
    {
        if (is_array($v))
        {
            //subsection case
            //merge all the sections into one array...
            $sec = array_merge((array) $parent, (array) $k);
            //add section information to the output
            $out .= '[' . join('.', $sec) . ']' . PHP_EOL;
            //recursively traverse deeper
            $out .= arr2ini($v, $sec);
        }
        else
        {
            //plain key->value case
            $out .= "$k=$v" . PHP_EOL;
        }
    }
    return $out;
}

You can test it like this:

$x = [
  'section1' => [
    'key1' => 'value1',
    'key2' => 'value2',
    'subsection' => [
      'subkey' => 'subvalue',
      'further' => ['a' => 5],
      'further2' => ['b' => -5]]]];
echo arr2ini($x);

(Note that short array syntax is available only since PHP 5.4+.)

Also note that it doesn't preserve the comments that were present in your question. There are no easy ways to remember them, when it is software (as opposed to a human) that updates the file back.

rr-
  • 14,303
  • 6
  • 45
  • 67
3

I've made significant changes to the function provided by rr- (many thanks for the kick-start!)

I was unhappy with the way multidimensional properties are handled in that version. I took the example ini file from the php documentation page for parse_ini_file and got a result which included the keys third_section.phpversion and third_section.urls - not what I expected.

I tried using a RecursiveArrayIterator for unlimited nesting, but unfortunately, a header with key-value pairs under it is the maximum limit of recursion that parse_ini_string will process before choking on an error message.

So I started from scratch, added some curveballs as the fourth and last items, and ended up with this:

$test = array(
    'first_section' => array(
        'one' => 1,
        'five' => 5,
        'animal' => "Dodo bird",
    ),
    'second_section' => array(
        'path' => "/usr/local/bin",
        'URL' => "http://www.example.com/username",
    ),

    'third_section' => array(
        'phpversion' => array(5.0, 5.1, 5.2, 5.3),
        'urls' => array(
            'svn' => "http://svn.php.net",
            'git' => "http://git.php.net",
        ),
    ),

    'fourth_section' => array(
        7.0, 7.1, 7.2, 7.3,
    ),
    'last_item' => 23,
);
echo '<pre>';
print_r($test);
echo '<hr>';
$ini = build_ini_string($test);
echo $ini;
echo '<hr>';
print_r( parse_ini_string($ini, true) );

function build_ini_string(array $a) {
    $out = '';
    $sectionless = '';
    foreach($a as $rootkey => $rootvalue){
        if(is_array($rootvalue)){
            // find out if the root-level item is an indexed or associative array
            $indexed_root = array_keys($rootvalue) == range(0, count($rootvalue) - 1);
            // associative arrays at the root level have a section heading
            if(!$indexed_root) $out .= PHP_EOL."[$rootkey]".PHP_EOL;
            // loop through items under a section heading
            foreach($rootvalue as $key => $value){
                if(is_array($value)){
                    // indexed arrays under a section heading will have their key omitted
                    $indexed_item = array_keys($value) == range(0, count($value) - 1);
                    foreach($value as $subkey=>$subvalue){
                        // omit subkey for indexed arrays
                        if($indexed_item) $subkey = "";
                        // add this line under the section heading
                        $out .= "{$key}[$subkey] = $subvalue" . PHP_EOL;
                    }
                }else{
                    if($indexed_root){
                        // root level indexed array becomes sectionless
                        $sectionless .= "{$rootkey}[] = $value" . PHP_EOL;
                    }else{
                        // plain values within root level sections
                        $out .= "$key = $value" . PHP_EOL;
                    }
                }
            }

        }else{
            // root level sectionless values
            $sectionless .= "$rootkey = $rootvalue" . PHP_EOL;
        }
    }
    return $sectionless.$out;
}

My input and output arrays match (functionally, anyway) and my ini file looks like this:

fourth_section[] = 7
fourth_section[] = 7.1
fourth_section[] = 7.2
fourth_section[] = 7.3
last_item = 23

[first_section]
one = 1
five = 5
animal = Dodo bird

[second_section]
path = /usr/local/bin
URL = http://www.example.com/username

[third_section]
phpversion[] = 5
phpversion[] = 5.1
phpversion[] = 5.2
phpversion[] = 5.3
urls[svn] = http://svn.php.net
urls[git] = http://git.php.net

I know it may be a little overkill, but I really needed this function in two of my own projects. Now I can read an ini file, make changes and save it.

vahanpwns
  • 939
  • 5
  • 12
1

The answer by RR works and I added one change

in else statement

//plain key->value case
$out .= "$k=$v" . PHP_EOL;

change it to

//plain key->value case
$out .= "$k=\"$v\"" . PHP_EOL;

By having " around the value, you can have larges values in the INI otherwise parse_ini_* functions will have an issue

http://missioncriticallabs.com/blog/2009/08/double-quotation-marks-in-php-ini-files/

0

This is my enhanced version answer of rr- (thanks to him), my function is a part of class in eco-system so a function named Arr::isAssoc is used which is basically to detect whether the given array is an associative array or not.


  private function arrayToConfig(array $array, array $parent = []): string
  {
    $returnValue = '';

    foreach ($array as $key => $value)
      {
        if (is_array($value)) // Subsection case
          {
            // Merge all the sections into one array
            if (is_int($key)) $key++;
            $subSection = array_merge($parent, (array)$key);
            // Add section information to the output
            if (Arr::isAssoc($value))
              {
                if (count($subSection) > 1) $returnValue .= PHP_EOL;
                $returnValue .= '[' . implode(':', $subSection) . ']' . PHP_EOL;
              }
            // Recursively traverse deeper
            $returnValue .= $this->arrayToConfig($value, $subSection);
            $returnValue .= PHP_EOL;
          }
        elseif (isset($value)) $returnValue .= "$key=" . (is_bool($value) ? var_export($value, true) : $value) . PHP_EOL; // Plain key->value case
      }

    return count($parent) ? $returnValue : rtrim($returnValue) . PHP_EOL;
  }
-2

What about using php internal functions ? http://php.net/manual/en/function.parse-ini-file.php

PierreL
  • 460
  • 4
  • 7
  • 3
    These functions are to parse ini file or string in ini format, he wants export array to ini file which these functions **DO NOT DO** – Robert Jun 26 '13 at 09:59
  • 1
    @Brewal from manual "parse_ini_string() returns the settings in string ini in an associative array." **he wants opposite action**. He wants settings in array to be written in ini. – Robert Jun 26 '13 at 10:02
  • @Robert you completely understood me. Do you have any solution? – user2216190 Jun 26 '13 at 10:07
  • @user2216190 - you expect already written code? It's really not difficult to create an ini file from an array. Even beginners could do it within 15 minutes, it's so trivial AND you can find results for your question if you google (and it even leads you to stackoverflow). – N.B. Jun 26 '13 at 10:09
  • "I need to read a .ini file into an array"... You've got at least that – Brewal Jun 26 '13 at 10:10
  • @user2216190 one of friends already posted a link to http://stackoverflow.com/questions/1268378/create-ini-file-write-values-in-php check it. – Robert Jun 26 '13 at 10:10
  • @N.B. I don't want any code, i didn't asked that. but pointing could help. – user2216190 Jun 26 '13 at 10:11
  • @user2216190 - if you google "php array to ini" you get the code and explanation behind it. I'm not calling you out, I'm just trying to say that you're introducing unnecessary duplication of content, and it's way, way faster for you to obtain the answer by using google. The issues you're having - someone else had it before you. It's the best to ask the question if it's the last resort and googling didn't yield any results. Also, if you know how to use functions such as sprintf, how to loop an array - there's no problem to create a function on your own that does it. – N.B. Jun 26 '13 at 10:23