PHP Doku:: Analysiert eine Konfigurationsdatei - function.parse-ini-file.html

Verlauf / Chronik / History: (1) anzeigen

Sie sind hier:
Doku-StartseitePHP-HandbuchFunktionsreferenzDateisystemrelevante ErweiterungenDateisystemDateisystem-Funktionenparse_ini_file

Ein Service von Reinhard Neidl - Webprogrammierung.

Dateisystem-Funktionen

<<move_uploaded_file

parse_ini_string>>

parse_ini_file

(PHP 4, PHP 5)

parse_ini_fileAnalysiert eine Konfigurationsdatei

Beschreibung

array parse_ini_file ( string $filename [, bool $process_sections ] )

parse_ini_file() lädt die in filename angegebene Datei, und gibt die darin enthaltenen Einstellungen in einem assoziativen Array zurück. Setzen Sie den letzten Parameter process_sections auf TRUE, erhalten Sie ein mehrdimensionales Array mit den Gruppennamen und Einstellungen. Ist process_sections nicht angegeben, wird FALSE angenommen.

Hinweis:

Diese Funktion hat nichts mit der php.ini zu tun, denn diese ist bereits abgearbeitet, wenn Sie Ihr Skript ausführen. Diese Funktion ist vorgesehen, um Konfigurationsdateien für Ihre eigenen Applikationen einzulesen.

Hinweis:

Enthält ein Wert in der ini Datei nicht alphanumerische Zeichen, so muss dieser von doppelten Anführungszeichen (") eingeschlossen sein.

Hinweis: Seit PHP 4.2.1 wird diese Funktion auch von Safe Mode und open_basedir beeinflusst.

Die Struktur der ini Datei ist der von php.ini ähnlich.

Konstanten können in der ini Datei ebenfalls geparsed werden. Wenn Sie eine Konstante als einen ini Wert definieren bevor Sie parse_ini_file() aufrufen, wird diese in den Ergebnissen enthalten sein. Es werden nur ini Werte ausgewertet. Zum Beispiel:

Beispiel #1 Inhalt der sample.ini

; Dies ist ein Beispiel für eine Konfigurationsdatei
; Kommentare beginnen wie in der php.ini mit ';'

[erste_gruppe]
eins = 1
fünf = 5
animal = BIRD

[zweite_gruppe]
pfad = /usr/local/bin
URL = "http://www.example.com/~username"

[dritte_gruppe]
phpversion[] = "5.0"
phpversion[] = "5.1"
phpversion[] = "5.2"
phpversion[] = "5.3"

Beispiel #2 parse_ini_file()

<?php

define ('BIRD', 'Dodo bird');

// Ohne Gruppen analysieren
$ini_array = parse_ini_file("sample.ini");
print_r($ini_array);

// Mit Gruppen analysieren
$ini_array = parse_ini_file("sample.ini", TRUE);
print_r($ini_array);

?>

Würde wie folgt ausgeben:

Array
(
    [eins] => 1
    [fünf] => 5
    [animal] => BIRD
    [pfad] => /usr/local/bin
    [URL] => http://www.example.com/~username
    [phpversion] => Array
        (
            [0] => 5.0
            [1] => 5.1
            [2] => 5.2
            [3] => 5.3
        )

)
Array
(
    [erste_gruppe] => Array
        (
            [eins] => 1
            [fünf] => 5
            [animal] => BIRD
        )

    [zweite_gruppe] => Array
        (
            [pfad] => /usr/local/bin
            [URL] => http://www.example.com/~username
        )

    [dritte_gruppe] => Array
        (
            [phpversion] => Array
                (
                    [0] => 5.0
                    [1] => 5.1
                    [2] => 5.2
                    [3] => 5.3
                )

        )


)


77 BenutzerBeiträge:
- Beiträge aktualisieren...
daevid at daevid dot com
11.11.2010 4:04
Be warned that this function will not only NOT parse 'null', 'true', 'false', etc. but worse, it converts EVERYTHING to strings and sets 'true' values to "1" and null/'false' values to "".

Here is my test.ini file:

[examples]                                  ; this is a section
                                            ; this is a comment line
log_level = E_ALL & ~E_NOTICE
1 = intkey                                  ; this is a int key
nullvalue = null                            ; this is NULL
truebool = true                             ; this is boolean (TRUE)
falsebool = false                           ; this is boolean (FALSE)
intvalue = -1                               ; this is a integer (-1)
floatvalue = +1.4E-3                        ; this is a float (0.0014)
stringvalue = Hello World                   ; this is a unquoted string
quoted = "Hello World"                      ; this is a quoted string
apostrophed = 'Hello World'                 ; this is a apostrophed string
quoted escaped = "it work's \"fine\"!"      ; this is a quoted string with escaped quotes
apostrophed escaped = 'it work\'s "fine"!'  ; this is a apostrophed string with escaped apostrophes

Here is my test.php page:

<?php
    var_dump
(parse_ini_file('./test.ini', true));
?>

Here is the output:

array
  'examples' =>
    array
      'log_level' => string '6135' (length=4)
      1 => string 'intkey' (length=6)
      'nullvalue' => string '' (length=0)
      'truebool' => string '1' (length=1)
      'falsebool' => string '' (length=0)
      'intvalue' => string '-1' (length=2)
      'floatvalue' => string '+1.4E-3' (length=7)
      'stringvalue' => string 'Hello World' (length=11)
      'quoted' => string 'Hello World' (length=11)
      'apostrophed' => string ''Hello World'' (length=13)
      'quoted escaped' => string 'it work's \fine\!' (length=17)
      'apostrophed escaped' => string ''it work\'sfine' (length=15)

verified with PHP 5.2.4 to 5.3.3
Rolf
21.09.2010 23:27
As of PHP 5.3, you can escape a double quote like this:

description = "an \" example"

But strangely, this fails when you try to escape two consecutive double quotes:

description = "no \"\" good"

Unless there is something between them (in this example, there is a space character):

description = "this is \" \" ok"
uramihsayibok, gmail, com
16.09.2010 10:10
Undocumented feature!

Using ${...} as a value will look to
1) an INI setting, or
2) an environment variable

For example,

<?php

print_r
(parse_ini_string('
php_ext_dir = ${extension_dir}
operating_system = ${OS}
'
));

?>

Array
(
    [php_ext_dir] => ./ext/
    [operating_system] => Windows_NT
)

Present in PHP 5.3.2, likely in 5.x, maybe even earlier too.
Anonymous
11.09.2010 18:42
a ini lexer with regexp:

<?php

@header('Content-Type: text/plain');

$myini = <<<EOT
[examples]                                  ; this is a section
                                            ; this is a comment line
1 = intkey                                  ; this is a int key
nullvalue = null                            ; this is NULL
truebool = true                             ; this is boolean (TRUE)
falsebool = false                           ; this is boolean (FALSE)
intvalue = -1                               ; this is a integer (-1)
floatvalue = +1.4E-3                        ; this is a float (0.0014)
stringvalue = Hello World                   ; this is a unquoted string
quoted = "Hello World"                      ; this is a quoted string
apostrophed = 'Hello World'                 ; this is a apostrophed string
quoted escaped = "it work's \"fine\"!"      ; this is a quoted string with escaped quotes
apostrophed escaped = 'it work\'s "fine"!'  ; this is a apostrophed string with escaped apostrophes

    [[valid special cases]]                 ; this is a section with square brackets and whitespaces at the beginning
quoted multiline = "line1
line2
line3"                                      ; this is a quoted multiline string
apostrophed multiline = "line1
line2
line3"                                      ; this is a apostrophed multiline string
     spaces before key = is ok               ; this line has whitespaces at the beginning
no val =                                    ; this setting has no key
= no key                                    ; this setting has no value
=                                           ; this setting has no key and no value

[bad cases]                                 ; you should never do that but it works
notgood = unquoted"string                   ; this value has a single quote
notgood2 = unapostrophed'string             ; this value has a single apostrophe
bad = "unclosed quotes                      ; this value has unclosed quotes
bad2 = 'unclosed apostrophes                ; this value has unclosed apostrophes

[invalid
section]
invalid setting
EOT;

function
get_tokens_from_ini_lexer($data, $verbose = FALSE)
{
   
$regexp = '/
    (?<=^|\r\n|\r|\n)
    (?P<line>
        (?:
            (?(?![\t\x20]*;)
                (?P<left_space>[\t\x20]*)
                (?:
                    \[(?P<section>[^;\r\n]+)\]
                    |
                    (?P<setting>
                        (?P<key>
                            [^=;\r\n]+?
                        )?
                        (?P<left_equal_space>[\t\x20]*)
                        (?P<equal_sign>=)
                        (?P<right_equal_space>[\t\x20]*)
                        (?P<val>
                            \x22(?P<quoted>.*?)(?<!\x5C)\x22
                            |
                            \x27(?P<apostrophed>.*?)(?<!\x5C)\x27
                            |
                            (?P<null>null)
                            |
                            (?P<bool>true|false)
                            |
                            (?P<int>[+-]?(?:[1-9]\d{0,18}|0))
                            |
                            (?P<float>(?:[+-]?(?:[1-9]\d*|0))\.\d+(?:E[+-]\d+)?)
                            |
                            (?P<string>[^;\r\n]+?)
                        )?
                    )
                )
            )
            (?P<right_space>[\t\x20]*)
            (?:
                (?P<comment_seperator>;)
                (?P<comment_space>[\t\x20]*)
                (?P<comment>[^\r\n]+?)?
            )?
        )
        |
        (?P<error>
            [^\r\n]+?
        )
    )
    (?=\r\n|\r|\n|$)(?P<crlf>\r\n|\r|\n)?
    |
    (?<=\r\n|\r|\n)(?P<emptyline>\r\n|\r|\n)
    /xsi'
;

    if(!@
is_int(preg_match_all($regexp, $data, $tokens, PREG_SET_ORDER)))
    {
       
// parse error
   
}
    else
    {
        foreach(
$tokens as $i => $token)
        {
            if(!
$verbose)
            {
                unset(
$tokens[$i]['line']);
                unset(
$tokens[$i]['crlf']);
                unset(
$tokens[$i]['setting']);
                unset(
$tokens[$i]['equal_sign']);
                unset(
$tokens[$i]['val']);
                unset(
$tokens[$i]['left_space']);
                unset(
$tokens[$i]['left_equal_space']);
                unset(
$tokens[$i]['right_equal_space']);
                unset(
$tokens[$i]['right_space']);
                unset(
$tokens[$i]['comment_seperator']);
                unset(
$tokens[$i]['comment_space']);
            };

            foreach(
$token as $key => $val)
            {
                if(!@
is_string($key) || !@strlen($val))
                {
                    unset(
$tokens[$i][$key]);
                };
            };
        };

        return(
$tokens);
    };
};

$verbose = FALSE;

print_r(get_tokens_from_ini_lexer($myini, $verbose));

?>
pd at frozen-bits dot de
19.08.2010 11:21
I use the following syntax to secure my config.ini.php file:

;<?php
;die(); // For further security
;/*

[category]
name="value"

;*/

;?>

Works like a charm and is both: A valid PHP File and a valid ini-File ;)
geggert at web dot de
23.07.2010 15:44
As quick an dirty way to gain the security of that *.ini.php-files you may alternatively use this as first line:

; <?php exit(); __halt_compiler();
// the closing tag is just to end up the syntax highlighting ...
// leave these comments and the closing tag away in your ini.php-file!
?>

You can use parse_ini_file() in the normal way and any criminal stranger will only see a ";" then ...
forcestudios.square7.de
15.06.2010 21:46
Tip: you cannot parse an ini-file with this safer structure:

<?php exit();
$data="

[section_one]
test = abc

[section_two]
and_so=on

"
;
?>

(strangers are not able to see this file because php closed the file previously by executing exit();)

But here is a very simple code to prevent this:
<?php

   
class iniParser{
       
        private
$IniFile;
        private
$SafeFile;
        private
$ParseClasses;
       
        public
$KeysWithoutSections;
        public
$KeysWithSections;
       
       
        public function
__construct($FileName, $SafeFile = false){
           
           
$this->IniFile = $FileName;
           
$this->SafeFile = $SafeFile;
           
        }
       
        public function
parseIni($SaveInClass = true){
           
           
$FileHandle = file($this->IniFile);
           
           
$CountLines = count($FileHandle);
           
$Counter = 0;
           
           
$NKeys = "";
           
            if (
$this->SafeFile ){
               
               
$Counter += 2;
               
$CountLines -= 2;
            }
           
            while (
$Counter < $CountLines ){
               
               
$CurLine = $FileHandle[$Counter];
               
               
$CurLineSplit = explode("=", $CurLine);
               
               
$CurKey = $CurLineSplit[0];
               
$CurValue = $CurLineSplit[1];
                if(
$SaveInClass )
                   
$this->Keys[trim($CurKey)] = trim($CurValue);
                   
                else
                   
$NKeys[trim($CurKey)] = trim($CurValue);
               
               
$Counter++;
            }
           
            if(
$SaveInClass )
                return
$this->KeysWithoutSections;
           
            else
                return
$NKeys;
           
        }
       
        public function
parseIniWithSections($SaveInClass = true){
       
           
$FileHandle = file($this->IniFile);
           
           
$CountLines = count($FileHandle);
           
$Counter = 0;
           
           
$LastSection = "";
           
           
$NKeys = "";
           
            if (
$this->SafeFile ){
           
               
$CountLines -= 2;
               
$Counter += 2;
           
            }
           
            while (
$Counter < $CountLines ){
           
               
$CurLine = $FileHandle[$Counter];
               
                if (
strpos($CurLine, "[") == 1 ){
               
                   
$LastSection = $CurLine;
                    continue;
               
                }
               
               
$Explosion = explode("=", $CurLine);
               
               
$CurKey = trim($Explosion[0]);
               
$CurValue = trim($Explosion[1]);
               
                if (
$SaveInClass )
                   
$this->KeysWithSections[$LastSection][$CurKey] = $CurValue;
                   
                else
                   
$NKeys[$LastSection][$CurKey] = $CurValue;
               
               
            }
           
            if (
$SaveInClass )
                return
$this->KeysWithSections;
               
            else
                return
$NKeys;
       
        }
       
    };

?>

To use this class just try this script here:

<?php

include "iniparser.php" // class above

$SafeIniParser = new iniParser("test.php", true); // file: test.php, safefile.

$Keys = $SafeIniParser->parseIniWithSections(false);

echo
$Keys["section_one"]["test"];

?>

i used this file:
<?php exit();
$data="

[section_one]
test = abc

[section_two]
and_so=on

"
;
?>
Mauro Gabriel Titimoli
20.01.2010 0:14
I have recently finished an implementacion of a multiple configuration type class.

<?php
class Configuration {
    const
AUTO = 0;
    const
JSON = 2;
    const
PHP_INI = 4;
    const
XML = 16;

    static private
$CONF_EXT_RELATION = array(
       
'json' => 2, // JSON
       
'ini' => 4// PHP_INI
       
'xml' => 16  // XML
   
);

    static private
$instances;

    private
$data;

    static public function
objectToArray($obj) {
       
$arr = (is_object($obj))?
           
get_object_vars($obj) :
           
$obj;

        foreach (
$arr as $key => $val) {
           
$arr[$key] = ((is_array($val)) || (is_object($val)))?
               
self::objectToArray($val) :
               
$val;
        }

        return
$arr;
    }

    private function
__construct($file, $type = Configuration::AUTO) {
        if (
$type == self::AUTO) {
           
$type = self::$CONF_EXT_RELATION[pathinfo($file, PATHINFO_EXTENSION)];
        }

        switch(
$type) {
            case
self::JSON:
               
$this->data = json_decode(file_get_contents($file), true);
                break;

            case
self::PHP_INI:
               
$this->data = parse_ini_file($file, true);
                break;

            case
self::XML:
               
$this->data = self::objectToArray(simplexml_load_file($file));
                break;
        }
    }

    static public function &
getInstance($file, $type = Configuration::AUTO) {
        if(! isset(
self::$instances[$file])) {
           
self::$instances[$file] = new Configuration($file, $type);
        }

        return
self::$instances[$file];
    }

    public function
__get($section) {
        if ((
is_array($this->data)) &&
                (
array_key_exists($section, $this->data))) {
            return
$this->data[$section];
        }
    }

    public function
getAvailableSections() {
        return
array_keys($this->data);
    }
}

$configuration = Configuration::getInstance(/*configuration filename*/);
foreach(
$configuration->getAvailableSections() as $pos => $sectionName) {
   
var_dump($sectionName);
   
var_dump($configuration->{$sectionName});
}
?>
freamer89 at gmail dot com
3.11.2009 17:38
Didn`t find the one,which suits my needs,so Here`s a small and easy write ini from array function... Maybe you`ll find it handy.
<?php
function write_php_ini($array, $file)
{
   
$res = array();
    foreach(
$array as $key => $val)
    {
        if(
is_array($val))
        {
           
$res[] = "[$key]";
            foreach(
$val as $skey => $sval) $res[] = "$skey = ".(is_numeric($sval) ? $sval : '"'.$sval.'"');
        }
        else
$res[] = "$key = ".(is_numeric($val) ? $val : '"'.$val.'"');
    }
   
safefilerewrite($file, implode("\r\n", $res));
}
//////
function safefilerewrite($fileName, $dataToSave)
{    if (
$fp = fopen($fileName, 'w'))
    {
       
$startTime = microtime();
        do
        {           
$canWrite = flock($fp, LOCK_EX);
          
// If lock not obtained sleep for 0 - 100 milliseconds, to avoid collision and CPU load
          
if(!$canWrite) usleep(round(rand(0, 100)*1000));
        } while ((!
$canWrite)and((microtime()-$startTime) < 1000));

       
//file was locked so now we can store information
       
if ($canWrite)
        {           
fwrite($fp, $dataToSave);
           
flock($fp, LOCK_UN);
        }
       
fclose($fp);
    }

}
?>
prometheus
30.09.2009 18:32
I made a small test to check differencies between parse_ini_file and json_decode and I surprised a little bit.

Here are my test files...
parseini-test.ini:
[global]
a = 1
b = 2
c = "Lorem Ipsum"
d = "Dolor Sit Amet"
e = 3.14

jsondecode-test.json:
{
    "a": 1,
    "b": 2,
    "c": "Lorem Ipsum",
    "d": "Dolor Sit Amet",
    "e": 3.14
}

And source codes are...
parseini.php:
<?php

$s
= microtime(TRUE);
for (
$i=0; $i<100000; $i++)
{
$a = parse_ini_file('./parseini-test.ini');
unset(
$a);
}
$e = microtime(TRUE);
echo
$e-$s;

?>

jsondecode.php:
<?php

$s
= microtime(TRUE);
for (
$i=0; $i<100000; $i++)
{
$a = json_decode(file_get_contents('./jsondecode-test.json'), TRUE);
unset(
$a);
}
$e = microtime(TRUE);
echo
$e-$s;

?>

These tests ran for three times (the third result is near to average in my experiences):
- parseini.php: 3.24759721756
- jsondecode.php: 3.289290905

My conclusion:
I`m going to use the json_decode() for reading config files because no significant difference in running time between parse_ini_file() and json_decode() + file_get_contents() but JSON is a more powerful format for storing well typed (parse_ini_file parses 3.14 as string, json_decode as float) and well structured configuration settings. As a note: json_decode is sensitive for associative keys` quotation, use quotes at all time in keys` names.

Test ran on PHP 5.2.
marc at kings dot nl
30.09.2009 11:48
Please note that apparently as of PHP 5.3 the last line of the .ini file you're parsing needs to have a linefeed. Otherwise parse_ini() will return false.
flacroix897 at hotmail dot com
25.08.2009 19:23
Make sure you use double-quotes when using spaces in a value as of 5.3.

Consider the following INI file:

   key = tested on php5

with the following code:

   $res = parse_ini_file('myini.ini');
   var_dump($res);

In 5.2, this will give you:

   array(1) {
     ["key"]=>
     string(14) "tested on php5"
   }

In 5.3, this will give you:

   Warning: syntax error, unexpected BOOL_TRUE in Unknown on line 1 in test.php on line 3
   bool(false)

This is because the 'on' word is a reserved keyword for boolean TRUE. The documentation now states that a string that contains any non-alphanumeric character should be enclosed in double-quotes (a space is not alphanumeric).
rmcleod79 [at] talktalk ~dot~ net
8.07.2009 10:40
Here's a simple settings class that parses an ini file for settings. It has a private constructor so that it can only be instantiated through the getInstance method, this means that there can only ever be one settings object at a time.

<?php

class Settings {
    private static
$instance;
    private
$settings;
   
    private function
__construct($ini_file) {
       
$this->settings = parse_ini_file($ini_file, true);
    }
   
    public static function
getInstance($ini_file) {
        if(! isset(
self::$instance)) {
           
self::$instance = new Settings($ini_file);           
        }
        return
self::$instance;
    }
   
    public function
__get($setting) {
        if(
array_key_exists($setting, $this->settings)) {
            return
$this->settings[$setting];
        } else {
            foreach(
$this->settings as $section) {
                if(
array_key_exists($setting, $section)) {
                    return
$section[$setting];
                }
            }
        }
    }
}

?>

settings.ini

[General]

url = "http://www.example.com"

[Database]

host = localhost
username = user
password = password
db = cms
adapter = mysqli

Using the class

<?php

$settings
= Settings::getInstance(/*path to settings.ini*/);

echo
$settings->url;

print_r($settings->Database);

echo
$settings->db;

?>

Output would be:

http://www.example.com

Array
(
    [host] => localhost
    [username] => user
    [password] => password
    [db] => cms
    [adapter] => mysqli
)

cms
joe at u13 dot net
20.06.2009 9:46
I'm not sure why, but for some reason php's ini functions always leave out entries for me.

To solve this problem, I wrote my own ini parsing function, intended to be a replacement for parse_ini_file('file.ini', true);

<?php

function new_parse_ini($f)
{

   
// if cannot open file, return false
   
if (!is_file($f))
        return
false;

   
$ini = file($f);

   
// to hold the categories, and within them the entries
   
$cats = array();

    foreach (
$ini as $i) {
        if (@
preg_match('/\[(.+)\]/', $i, $matches)) {
           
$last = $matches[1];
        } elseif (@
preg_match('/(.+)=(.+)/', $i, $matches)) {
           
$cats[$last][$matches[1]] = $matches[2];
        }
    }

    return
$cats;

}

?>

The usage follows the Example #2 on http://us3.php.net/manual/en/function.parse-ini-file.php , except without the second parameter being 'true'.
DDRKhat
19.06.2009 23:11
Here is an adaption of Jomel's write_ini_file function to support arrays and eliminate redundant speechmarking on NULL values

<?php
if (!function_exists('write_ini_file')) {
    function
write_ini_file($assoc_arr, $path, $has_sections=FALSE) {
       
$content = "";

        if (
$has_sections) {
            foreach (
$assoc_arr as $key=>$elem) {
               
$content .= "[".$key."]\n";
                foreach (
$elem as $key2=>$elem2)
                {
                    if(
is_array($elem2))
                    {
                        for(
$i=0;$i<count($elem2);$i++)
                        {
                           
$content .= $key2."[] = \"".$elem2[$i]."\"\n";
                        }
                    }
                    else if(
$elem2=="") $content .= $key2." = \n";
                    else
$content .= $key2." = \"".$elem2."\"\n";
                }
            }
        }
        else {
            foreach (
$assoc_arr as $key=>$elem) {
                if(
is_array($elem))
                {
                    for(
$i=0;$i<count($elem);$i++)
                    {
                       
$content .= $key2."[] = \"".$elem[$i]."\"\n";
                    }
                }
                else if(
$elem=="") $content .= $key2." = \n";
                else
$content .= $key2." = \"".$elem."\"\n";
            }
        }

        if (!
$handle = fopen($path, 'w')) {
            return
false;
        }
        if (!
fwrite($handle, $content)) {
            return
false;
        }
       
fclose($handle);
        return
true;
    }
}
?>
jeremygiberson at gmail dot com
1.05.2009 1:01
Here is a quick parse_ini_file wrapper to add extend support to save typing and redundancy.
<?php
   
/**
     * Parses INI file adding extends functionality via ":base" postfix on namespace.
     *
     * @param string $filename
     * @return array
     */
   
function parse_ini_file_extended($filename) {
       
$p_ini = parse_ini_file($filename, true);
       
$config = array();
        foreach(
$p_ini as $namespace => $properties){
            list(
$name, $extends) = explode(':', $namespace);
           
$name = trim($name);
           
$extends = trim($extends);
           
// create namespace if necessary
           
if(!isset($config[$name])) $config[$name] = array();
           
// inherit base namespace
           
if(isset($p_ini[$extends])){
                foreach(
$p_ini[$extends] as $prop => $val)
                   
$config[$name][$prop] = $val;
            }
           
// overwrite / set current namespace values
           
foreach($properties as $prop => $val)
           
$config[$name][$prop] = $val;
        }
        return
$config;
    }
?>

Treats this ini:
<?php
/*
[base]
host=localhost
user=testuser
pass=testpass
database=default

[users:base]
database=users

[archive : base]
database=archive
*/
?>
As if it were like this:
<?php
/*
[base]
host=localhost
user=testuser
pass=testpass
database=default

[users:base]
host=localhost
user=testuser
pass=testpass
database=users

[archive : base]
host=localhost
user=testuser
pass=testpass
database=archive
*/
?>
prikkeldraad at gmail dot com
26.03.2009 15:15
When PHP dies without any warning or message when parsing the ini-file, check the values of the file. All non alphanumeric values need to be quoted.
php at e-pla dot net
29.01.2009 23:12
Just needed a loose and multiline function to parse ini files.

So here is my attempt:

- process multiline values
- accepts non alphanum characters (spaces, dot, equals etc...) inside keys or values
- "raw" mode
- "section" mode
- proper comment parsing
- compact coding: only 400 bytes !...

Code is here:
http://mach13.com/loose-and-multiline-parse_ini_file-function-in-php
pBakhuis at googles mail dot com (gmail)
12.12.2008 17:42
To those who were like me looking if this could be used to create an array out of commandline output I offer you the function below (I used it to parse mplayer output).

If you want it behave exactly the same as parse_ini_file you'll obviously have to add some code to feed the different sections to this one. Hope it's of help to someone!

<?php
/**
 * The return is very similar to that of parse_ini_file, but this works off files
 *
 * Below is an example of what it does, where the first
 * value is what you'd normally want to do, and the second and third things that might
 * happen and in case it does it's good to know what is going on.
 *
 * $anArray = array( 'default=theValue', 'setting=', 'something=value=value' );
 * explodeExplode( '=', $anArray );
 *
 * the return will be
 * array( 'default' => 'theValue', 'setting' => '', 'something' => 'value=value' );
 *
 * So the oddities here are, text after the second $string occurence dissapearing
 * and empty values resulting in an empty string.
 *
 * @return $returnArray array array( 'setting' => 'value' )
 * @param $string Object
 * @param $array Object
 */
function explodeExplode( $string, $array )
{
   
$returnArray = array();
   
    foreach(
$array as $arrayValue )
    {
       
$tmpArray = explode( $string, $arrayValue );
       
        if(
count( $tmpArray ) == 1 )
        {
           
$returnArray[$tmpArray[0]] = '';
        }
        else if(
count( $tmpArray ) == 2 )
        {
           
$returnArray[$tmpArray[0]] = $tmpArray[1];
        }
        else if(
count( $tmpArray ) > 2 )
        {
           
$implodeBack = array();
           
$firstLoop      = true;
            foreach(
$tmpArray as $tmpValue )
            {
                if(
$firstLoop )
                {
                   
$firstLoop = false;
                }
                else
                {
                   
$implodeBack[] = $tmpValue;
                }
            }
           
print_r( $implodeBack );
           
$returnArray[$tmpArray[0]] = implode( '=', $implodeBack );
        }
    }
   
    return
$returnArray;
}
?>
bas at muer dot nl
31.10.2008 12:52
In response to juampii_4 at hotmail dot com (10-Jul-2008 05:24):

You're parsing the ini file every time someone requests a variable. You'd have a better performing class if you were to parse it once, then store the resulting array. Requests for a value from the ini should be taken from that stored array, not by reparsing the ini over and over.
Bill Brown - macnimble.com
17.10.2008 4:47
Working on a project for a client recently, I needed a way to set a default configuration INI file, but also wanted to allow the client to override the settings through the use of a custom INI file.

I thought array_merge or array_merge_recursive would do the trick for me, but it fails to override settings in the way that I wanted. I wrote my own function to do what I wanted. It's nothing spectacular, but thought I'd post it here in case it saved someone else some time.

<?php
function ini_merge ($config_ini, $custom_ini) {
  foreach (
$custom_ini AS $k => $v):
    if (
is_array($v)):
     
$config_ini[$k] = ini_merge($config_ini[$k], $custom_ini[$k]);
    else:
     
$config_ini[$k] = $v;
    endif;
  endforeach;
  return
$config_ini;
};
$CONFIG_INI = parse_ini_file('../config.ini', TRUE);
$CUSTOM_INI = parse_ini_file('ini/custom.ini', TRUE);
$INI = ini_merge($CONFIG_INI, $CUSTOM_INI);
?>

This allowed me to put the default INI file above the web root with information that requires extra security (database connection info, etc.) and a writable INI file within the structure of the site without affecting the default settings of the default config.ini file.

Anyway, hope it helps.
david dot dyess at gmail dot com
4.08.2008 9:12
Here is another way to group values in the ini:

my.ini:

[singles]
test = a test
test2 = another test
test3 = this is a test too

[multiples]
tests[] = a test
tests[] = another test
tests[] = this is a test too

my.php:

$init = parse_ini_file('my.ini');

The same as:

$init['test'] = 'a test';
$init['test2'] = 'another test';
$init['test3'] = 'this is a test too';
$init['tests'][0] = 'a test';
$init['tests'][1] = 'another test';
$init['tests'][2] = 'this is a test too';

This works with the bool set to true also, can be useful with loops. Works with the bool set to true as well.
another_user at example dot com
25.07.2008 1:43
yarco dot w at gmail dot com:  Constants can be concatenated with strings, but the string segments must be enclosed in quotes.  Note to users, no joining symbol is used:

+++
; Start config.ini file here

output = "bla bla bla "TEST_TXT" bla bla bla"

; end
+++

<?php

define
("TEST_TXT","something something");
$the_array = parse_ini_file("/www/includes/config.ini");
echo
$the_array["output"];

?>

outputs...
bla bla bla something something bla bla bla
juampii_4 at hotmail dot com
10.07.2008 5:24
class.parseini.php
<?php
##By juan pablo tosso
class Parser
{

    public function
printini($file, $sector, $var)
    {
       
$file=$file.".ini";
       
$is=array();
       
$is= parse_ini_file($file, true);
       
trim($is);
        if(
is_array($is) && file_exists($file))
        {
            return
$is[$sector][$var];
        }else{
            return
"error";
        }
       
    }
   
   
}

?>

Ini.ini:
[test]
foo=bar

[test2]
foo1=bar1
foo2=bar2
foo bar=something else

just in another file write:

include("class.parseini.php");
$new= new Parser();
echo $new->printini("ini", "test2", "foo1");
tim {at} tim {hyphen} ryan {dot} com
2.07.2008 9:14
I whipped up an alternate, small (4kb) INI reader/writer class, implementing most of the features below:

 - [] array support
 - dot (.) notation heirarchies
 - sections (and non-section mode)
 - proper comment (;) parsing
 - parsing literal values (booleans, numbers, constants)
 - file reading/writing

Check it out here: http://tim-ryan.com/labs/parseINI/
asohn ~at~ aircanopy ~dot~ net
1.05.2008 0:27
Comments don't have to have an entire line dedicated to them. You can put a comment on the same line as a section or variable/value declaration and the built-in parse_ini_file() function will omit them. This being the case I took the liberty of revising goulven.ch AT gmail DOT com 's parse_ini() function. I also added the $process_sections argument to better reflect PHP's built-in parse_ini_file(). As soon as a semicolon is found in a line everything from that position to the end of the line is omitted so as to not become part of the value. However, any semicolon found that occurs between a single-quote or double-quote will be left alone to become part of the value.

<?php
function _parse_ini_file($file, $process_sections = false) {
 
$process_sections = ($process_sections !== true) ? false : true;

 
$ini = file($file);
  if (
count($ini) == 0) {return array();}

 
$sections = array();
 
$values = array();
 
$result = array();
 
$globals = array();
 
$i = 0;
  foreach (
$ini as $line) {
   
$line = trim($line);
   
$line = str_replace("\t", " ", $line);

   
// Comments
   
if (!preg_match('/^[a-zA-Z0-9[]/', $line)) {continue;}

   
// Sections
   
if ($line{0} == '[') {
     
$tmp = explode(']', $line);
     
$sections[] = trim(substr($tmp[0], 1));
     
$i++;
      continue;
    }

   
// Key-value pair
   
list($key, $value) = explode('=', $line, 2);
   
$key = trim($key);
   
$value = trim($value);
    if (
strstr($value, ";")) {
     
$tmp = explode(';', $value);
      if (
count($tmp) == 2) {
        if (((
$value{0} != '"') && ($value{0} != "'")) ||
           
preg_match('/^".*"\s*;/', $value) || preg_match('/^".*;[^"]*$/', $value) ||
           
preg_match("/^'.*'\s*;/", $value) || preg_match("/^'.*;[^']*$/", $value) ){
         
$value = $tmp[0];
        }
      } else {
        if (
$value{0} == '"') {
         
$value = preg_replace('/^"(.*)".*/', '$1', $value);
        } elseif (
$value{0} == "'") {
         
$value = preg_replace("/^'(.*)'.*/", '$1', $value);
        } else {
         
$value = $tmp[0];
        }
      }
    }
   
$value = trim($value);
   
$value = trim($value, "'\"");

    if (
$i == 0) {
      if (
substr($line, -1, 2) == '[]') {
       
$globals[$key][] = $value;
      } else {
       
$globals[$key] = $value;
      }
    } else {
      if (
substr($line, -1, 2) == '[]') {
       
$values[$i-1][$key][] = $value;
      } else {
       
$values[$i-1][$key] = $value;
      }
    }
  }

  for(
$j = 0; $j < $i; $j++) {
    if (
$process_sections === true) {
     
$result[$sections[$j]] = $values[$j];
    } else {
     
$result[] = $values[$j];
    }
  }

  return
$result + $globals;
}
?>

usage regarding semicolons:
<?php
;sample.ini

variable1  
= v1;v1
variable 2 
= "v2;v2"
variable_3  = "v3;v3;v3"
variable4   = "v4;v4" ;v4
variable 5 
= "v5;v5;v5" ;v5
variable_6 
= "v6;v6" ;v6;;
variable7   = "v7;;v7"
variable 8  = 'v8;v8'
variable_9  = 'v9;v9;v9'
variable10  = 'v10;v10' ;v10
variable 11
= 'v11;v11;v11' ;v11
variable_12
= 'v12;v12' ;v2;;
variable13  = 'v13;;v13'
variable 14 = "v14
variable_15 = 'v15
variable16  = "
v16;v16
variable 17
= 'v17;v17
?>
<?php
//example.php
print_r(_parse_ini_file("sample.ini"));
?>
<?php
//example.php output
Array
(
    [variable1] => v1
    [variable 2] => v2;v2
    [variable_3] => v3;v3;v3
    [variable4] => v4;v4
    [variable 5] => v5;v5;v5
    [variable_6] => v6;v6
    [variable7] => v7;;v7
    [variable 8] => v8;v8
    [variable_9] => v9;v9;v9
    [variable10] => v10;v10
    [variable 11] => v11;v11;v11
    [variable_12] => v12;v12
    [variable13] => v13;;v13
    [variable 14] => v14
    [variable_15] => v15
    [variable16] => v16
    [variable 17] => v17
)
?>
goulven.ch AT gmail DOT com
29.10.2007 15:33
Warning: parse_ini_files cannot cope with values containing the equal sign (=).

The following function supports sections, comments, arrays, and key-value pairs outside of any section.
Beware that similar keys will overwrite one another (unless in different sections).

<?php
function parse_ini ( $filepath ) {
   
$ini = file( $filepath );
    if (
count( $ini ) == 0 ) { return array(); }
   
$sections = array();
   
$values = array();
   
$globals = array();
   
$i = 0;
    foreach(
$ini as $line ){
       
$line = trim( $line );
       
// Comments
       
if ( $line == '' || $line{0} == ';' ) { continue; }
       
// Sections
       
if ( $line{0} == '[' ) {
           
$sections[] = substr( $line, 1, -1 );
           
$i++;
            continue;
        }
       
// Key-value pair
       
list( $key, $value ) = explode( '=', $line, 2 );
       
$key = trim( $key );
       
$value = trim( $value );
        if (
$i == 0 ) {
           
// Array values
           
if ( substr( $line, -1, 2 ) == '[]' ) {
               
$globals[ $key ][] = $value;
            } else {
               
$globals[ $key ] = $value;
            }
        } else {
           
// Array values
           
if ( substr( $line, -1, 2 ) == '[]' ) {
               
$values[ $i - 1 ][ $key ][] = $value;
            } else {
               
$values[ $i - 1 ][ $key ] = $value;
            }
        }
    }
    for(
$j=0; $j<$i; $j++ ) {
       
$result[ $sections[ $j ] ] = $values[ $j ];
    }
    return
$result + $globals;
}
?>

Example usage:
<?php
$stores
= parse_ini('stores.ini');
print_r( $stores );
?>

An example ini file:
<?php
/*
;Commented line start with ';'
global_value1 = a string value
global_value1 = another string value

; empty lines are discarded
[Section1]
key = value
; whitespace around keys and values is discarded too
otherkey=other value
otherkey=yet another value
; this key-value pair will overwrite the former.
*/
?>
www.onphp5.com
24.10.2007 22:26
Looks like in PHP 5.3.0 special characters like \n are extrapolated into real newlines. Gotta use \\n.
arnapou
3.10.2007 15:51
I didn't find a simple ini class so I wrote that class to read and write ini files.
I hope it could help you.

Read file : $ini = INI::read('myfile.ini');
Write file : INI::write('myfile.ini', $ini);

Features :
- support [] syntax for arrays
- support . in keys like bar.foo.something = value
- true and false string are automatically converted in booleans
- integers strings are automatically converted in integers
- keys are sorted when writing
- constants are replaced but they should be written in the ini file between braces : {MYCONSTANT}

<?php

class INI {
   
/**
     *  WRITE
     */
   
static function write($filename, $ini) {
       
$string = '';
        foreach(
array_keys($ini) as $key) {
           
$string .= '['.$key."]\n";
           
$string .= INI::write_get_string($ini[$key], '')."\n";
        }
       
file_put_contents($filename, $string);
    }
   
/**
     *  write get string
     */
   
static function write_get_string(& $ini, $prefix) {
       
$string = '';
       
ksort($ini);
        foreach(
$ini as $key => $val) {
            if (
is_array($val)) {
               
$string .= INI::write_get_string($ini[$key], $prefix.$key.'.');
            } else {
               
$string .= $prefix.$key.' = '.str_replace("\n", "\\\n", INI::set_value($val))."\n";
            }
        }
        return
$string;
    }
   
/**
     *  manage keys
     */
   
static function set_value($val) {
        if (
$val === true) { return 'true'; }
        else if (
$val === false) { return 'false'; }
        return
$val;
    }
   
/**
     *  READ
     */
   
static function read($filename) {
       
$ini = array();
       
$lines = file($filename);
       
$section = 'default';
       
$multi = '';
        foreach(
$lines as $line) {
            if (
substr($line, 0, 1) !== ';') {
               
$line = str_replace("\r", "", str_replace("\n", "", $line));
                if (
preg_match('/^\[(.*)\]/', $line, $m)) {
                   
$section = $m[1];
                } else if (
$multi === '' && preg_match('/^([a-z0-9_.\[\]-]+)\s*=\s*(.*)$/i', $line, $m)) {
                   
$key = $m[1];
                   
$val = $m[2];
                    if (
substr($val, -1) !== "\\") {
                       
$val = trim($val);
                       
INI::manage_keys($ini[$section], $key, $val);
                       
$multi = '';
                    } else {
                       
$multi = substr($val, 0, -1)."\n";
                    }
                } else if (
$multi !== '') {
                    if (
substr($line, -1) === "\\") {
                       
$multi .= substr($line, 0, -1)."\n";
                    } else {
                       
INI::manage_keys($ini[$section], $key, $multi.$line);
                       
$multi = '';
                    }
                }
            }
        }
       
       
$buf = get_defined_constants(true);
       
$consts = array();
        foreach(
$buf['user'] as $key => $val) {
           
$consts['{'.$key.'}'] = $val;
        }
       
array_walk_recursive($ini, array('INI', 'replace_consts'), $consts);
        return
$ini;
    }
   
/**
     *  manage keys
     */
   
static function get_value($val) {
        if (
preg_match('/^-?[0-9]$/i', $val)) { return intval($val); }
        else if (
strtolower($val) === 'true') { return true; }
        else if (
strtolower($val) === 'false') { return false; }
        else if (
preg_match('/^"(.*)"$/i', $val, $m)) { return $m[1]; }
        else if (
preg_match('/^\'(.*)\'$/i', $val, $m)) { return $m[1]; }
        return
$val;
    }
   
/**
     *  manage keys
     */
   
static function get_key($val) {
        if (
preg_match('/^[0-9]$/i', $val)) { return intval($val); }
        return
$val;
    }
   
/**
     *  manage keys
     */
   
static function manage_keys(& $ini, $key, $val) {
        if (
preg_match('/^([a-z0-9_-]+)\.(.*)$/i', $key, $m)) {
           
INI::manage_keys($ini[$m[1]], $m[2], $val);
        } else if (
preg_match('/^([a-z0-9_-]+)\[(.*)\]$/i', $key, $m)) {
            if (
$m[2] !== '') {
               
$ini[$m[1]][INI::get_key($m[2])] = INI::get_value($val);
            } else {
               
$ini[$m[1]][] = INI::get_value($val);
            }
        } else {
           
$ini[INI::get_key($key)] = INI::get_value($val);
        }
    }
   
/**
     *  replace utility
     */
   
static function replace_consts(& $item, $key, $consts) {
        if (
is_string($item)) {
           
$item = strtr($item, $consts);
        }
    }
}

?>
thuylnt
26.09.2007 11:09
I need to read a ini file, modify some values in some sections, and save it. But the important thing is, i want to keep all the comments, the new lines in the right order. So i modified function parse_ini_file_quotes_safe and write_ini_file.
I think they work fine.

    function read_ini_file($f, &$r)
    {
        $null = "";
        $r=$null;
        $first_char = "";
        $sec=$null;
        $comment_chars=";#";
        $num_comments = "0";
        $num_newline = "0";

        //Read to end of file with the newlines still attached into $f
        $f = @file($f);
        if ($f === false) {
            return -2;
        }
        // Process all lines from 0 to count($f)
        for ($i=0; $i<@count($f); $i++)
        {
            $w=@trim($f[$i]);
            $first_char = @substr($w,0,1);
            if ($w)
            {
                if ((@substr($w,0,1)=="[") and (@substr($w,-1,1))=="]") {
                    $sec=@substr($w,1,@strlen($w)-2);
                    $num_comments = 0;
                    $num_newline = 0;
                }
                else if ((stristr($comment_chars, $first_char) == true)) {
                    $r[$sec]["Comment_".$num_comments]=$w;
                    $num_comments = $num_comments +1;
                }               
                else {
                    // Look for the = char to allow us to split the section into key and value
                    $w=@explode("=",$w);
                    $k=@trim($w[0]);
                    unset($w[0]);
                    $v=@trim(@implode("=",$w));
                    // look for the new lines
                    if ((@substr($v,0,1)=="\"") and (@substr($v,-1,1)=="\"")) {
                        $v=@substr($v,1,@strlen($v)-2);
                    }
                   
                    $r[$sec][$k]=$v;
                   
                }
            }
            else {
                $r[$sec]["Newline_".$num_newline]=$w;
                $num_newline = $num_newline +1;
            }
        }
        return 1;
    }

    function write_ini_file($path, $assoc_arr) {
        $content = "";

        foreach ($assoc_arr as $key=>$elem) {
            if (is_array($elem)) {
                if ($key != '') {
                    $content .= "[".$key."]\r\n";                   
                }
               
                foreach ($elem as $key2=>$elem2) {
                    if ($this->beginsWith($key2,'Comment_') == 1 && $this->beginsWith($elem2,';')) {
                        $content .= $elem2."\r\n";
                    }
                    else if ($this->beginsWith($key2,'Newline_') == 1 && ($elem2 == '')) {
                        $content .= $elem2."\r\n";
                    }
                    else {
                        $content .= $key2." = ".$elem2."\r\n";
                    }
                }
            }
            else {
                $content .= $key." = ".$elem."\r\n";
            }
        }

        if (!$handle = fopen($path, 'w')) {
            return -2;
        }
        if (!fwrite($handle, $content)) {
            return -2;
        }
        fclose($handle);
        return 1;
    }

    function beginsWith( $str, $sub ) {
        return ( substr( $str, 0, strlen( $sub ) ) === $sub );
    }
yarco dot w at gmail dot com
29.06.2007 10:46
parse_ini_file can't deal with const which cancate a string. For example, if test.ini file is

classPath = ROOT/lib

If you:
<?php
define
('ROOT', dirname(__FILE__));

$buf = parse_ini_file('test.ini');
?>

const ROOT would't be parsed.

But my version could work find.

<?php
// array parse_ini_file ( string $filename [, bool $process_sections] )
function parse_ini($filename, $process_sections = false)
{
  function
replace_process(& $item, $key, $consts)
  {
   
$item = str_replace(array_keys($consts), array_values($consts), $item);
  }

 
$buf = get_defined_constants(true); // PHP version > 5.0
 
$consts = $buf['user'];
 
$ini = parse_ini_file($filename, $process_sections);

 
array_walk_recursive($ini, 'replace_process', $consts);
  return
$ini;
}

define('ROOT', '/test');
print_r(parse_ini(dirname(__FILE__).'/test.ini'));

?>
Adam
25.06.2007 19:45
Arrays can be defined in the ini file by adding '[]' at the end of a key name. For example:

value1 = 17
value2 = 13

value3[] = a
value3[] = b
value3[] = c

Will return:
Array
(
    [value1] => 17
    [value2] => 13
    [value3] => Array
        (
            [0] => a
            [1] => b
            [2] => c
        )
)
Mildred
25.05.2007 9:42
I wrote few functions to work with ini files.

The function make_ini_file($array, &$errors)
The function read_ini($file)
The function prepare_ini($array, $maxdepth=NULL)

The function prepare_ini($array, $maxdepth=NULL)
This function will take an array as returned by the function read_ini() and will return an array as needed by the function make_ini_file() so that you can write extanded ini files easily.
If maxdepth is not given (or if maxdepth is NULL), this function will try to create sections so the keys in the sections do not have dots. if maxdepth is given, it will create sections with $maxdepth members in them (or less if it is not possible). It won't use the special key name "."

<?php

function prepare_ini($arr, $maxdepth=NULL){
   
$res = array();
   
prepare_ini__1($res, $arr, $maxdepth);
    return
$res;
}

function
prepare_ini__1(
    &
$res, $arr, $maxdepth,
   
$prefix1="", $prefix2="", $depth=0,
   
$self='prepare_ini__1')
{
    foreach(
$arr as $key=>$val){
        if(
is_array($val)){
            if(
is_null($maxdepth) or $depth < $maxdepth){
               
$newprefix = $prefix1 ? "$prefix1.$key" : $key;
               
$self($res, $val, $maxdepth, $newprefix, $prefix2, $depth+1);
            }else{
               
$newprefix = $prefix2 ? "$prefix1.$key" : $key;
               
$self($res, $val, $maxdepth, $prefix1, $newprefix, $depth+1);
            }
        }else{
           
$newprefix = $prefix2 ? "$prefix2.$key" : $key;
            if(!isset(
$res[$prefix1])) $res[$prefix1] = array();
           
$res[$prefix1][$newprefix] = $val;
        }
    }
}

// kate: indent-width 4; tab-width 8; space-indent on;
// kate: replace-tabs off; remove-trailing-space on;
?>
mark at hostcobalt dot com
27.03.2007 22:39
or to prevent the file being viewed you can just use a .htaccess file and add this line

<files *.ini>
order deny,allow
deny from all
</files>

i use a similar thing to prevent my config files being accessed
mhall at lakeland dot net
2.02.2007 19:22
I modified phpcoder's readINIFile function to allow multi-lined values. Adding a backslash (\) to the end of a line indicates that the whole of the next line should be appended to the value.  Leading whitespace is ignored on continues lines, whitespace before the backslash is preserved. This is the same as the Java Properties spec: http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html

<?php
function readINIfile ($filename, $commentchar) {
 
$array1 = file($filename);
 
$section = '';
  for (
$line_num = 0; $line_num <= sizeof($array1); $line_num++) {
  
$filedata = $array1[$line_num];
  
$dataline = trim($filedata);
  
$firstchar = substr($dataline, 0, 1);
   if (
$firstchar!=$commentchar && $dataline!='') {
    
//It's an entry (not a comment and not a blank line)
    
if ($firstchar == '[' && substr($dataline, -1, 1) == ']') {
      
//It's a section
      
$section = strtolower(substr($dataline, 1, -1));
     }else{
      
//It's a key...
      
$delimiter = strpos($dataline, '=');
       if (
$delimiter > 0) {
        
//...with a value
        
$key = strtolower(trim(substr($dataline, 0, $delimiter)));
        
$array2[$section][$key] = '';
        
$value = trim(substr($dataline, $delimiter + 1));
         while (
substr($value, -1, 1) == '\\') {
            
//...value continues on the next line
            
$value = substr($value, 0, strlen($value)-1);
            
$array2[$section][$key] .= stripcslashes($value);
            
$line_num++;
            
$value = trim($array1[$line_num]);
         }
        
$array2[$section][$key] .= stripcslashes($value);
        
$array2[$section][$key] = trim($array2[$section][$key]);
         if (
substr($array2[$section][$key], 0, 1) == '"' && substr($array2[$section][$key], -1, 1) == '"') {
           
$array2[$section][$key] = substr($array2[$section][$key], 1, -1);
         }
       }else{
        
//...without a value
        
$array2[$section][strtolower(trim($dataline))]='';
       }
     }
   }else{
    
//It's a comment or blank line.  Ignore.
  
}
  }
  return
$array2;
}
?>
fantasysportswire at yahoo dot com
3.01.2007 16:54
The ahull version of the parse_ini_file_quotes_safe can not handle unicode... the original version from Julio L Garbayo can.
ant at loadtrax dot com
15.11.2006 18:09
A number of posts mention using pear::Config as a replacement for this function. Note however that internally it uses parse_ini_file to read the ini file, so it suffers from the same limitations.
Justin Hall
31.10.2006 20:46
This is a simple (but slightly hackish) way of avoiding the character limitations (in values):

<?php
define
('QUOTE', '"');
$test = parse_ini_file('test.ini');

echo
"<pre>";
print_r($test);
?>

contents of test.ini:

park yesterday = "I (walked) | {to} " QUOTE"the"QUOTE " park yesterday & saw ~three~ dogs!"

output:

<?php
Array
(
    [
park yesterday] => I (walked) | {to} "the" park yesterday & saw ~three~ dogs!
)
?>

23.10.2006 11:16
this function won't parse a remote INI file, even with allow_url_fopen turned on.
judas dot iscariote at gmail dot com
1.10.2006 9:26
If you are looking for an OOP way to parse ini files, take a look at Marcus Boerger's  IniGroups  class available here :

http://www.php.net/~helly/php/ext/spl/classIniGroups.html
parksto at gmail dot com
18.09.2006 14:46
or better

on first line :
;<?php exit(' you won\'t see my ini file'); ?>

19.04.2006 9:45
upgrade of "mauder[remove] at [remove]gmail[remove] dot com" idea of hiding ini content from being seen.

file.ini.php

first line:
;<?/*

last line:
;*/
?>

will result ";" in browser, not "pharse error: (...)".
sam at viveka dot net dot au
24.03.2006 5:27
In addition to the note that "Parsing an ini file stops at a key named 'none'".

Values of 'none' do not return as the string 'none'. They return nothing at all, however this does not halt the processing of the ini file.
tertillian at yahoo dot com
22.02.2006 2:12
I ran into a snag where I wanted to have an INI file for a library. All attempts to parse the file from the library, apart from hardcoded path qualification, failed because it couldn't find the INI file. Some of the php functions will optionally use the include path. Adding this to the parse_ini_file() function would permit its use in this way and would encourage not putting INI files in document root.
nbraczek at bsds dot de
16.02.2006 1:29
Beside the mentioned reserved words 'null', 'yes', 'no', 'true', and 'false', also 'none' seems to be a reserved word. Parsing an ini file stops at a key named 'none'.
mauder[remove] at [remove]gmail[remove] dot com
14.02.2006 13:31
Be careful if you put any .ini file in your readable directories, if somebody would know the name (e.g. if your application is widely used), the webserver might return it as plain text.

For example : your database username and password could be exposed, if it is stored in that file !

To prevent this from happening :
- give the file .php extension :  "my.ini.php"
- put ';<?php' (without quotes and without X between X and php) on first line
- put '
;?>' on last line

The server would run the ini file as being PHP-code, but will do nothing due to bad syntax, preventing the content from being exosed.
On the other hand, it is still a valid .ini file...

HTH !
ahull at clydemarine dot com
26.01.2006 10:33
I had a look at the code for function parse_ini_file_quotes_safe(
and added in the ability to preserve comments.

<?php
// Parse a file into an array following the rules for ini files as follows
//
// Looks for [] characters to mark section headings and = chars to mark the break between the key and its values.
// Also keeps comments delimited by any of the characters in $comments_chars in the array numbered as they are found.
//
// Note writing back the array will necessarily move the comments to the beginning of the section,
// even if they are found within
// a section simply because there is no exact place-holder information stored in the array.
// This could of course be a problem.
// Also the Write array routine will have to be modified
// to correctly write back comments otherwise they will appear as blank sections called [comment{x}]

function parse_ini_file_quotes_safe($f)
{
 
$newline = "<br>";
 
$null = "";
 
$r=$null;
 
$first_char = "";
 
$sec=$null;
 
$comment_chars="/*<;#?>";
 
$num_comments = "0";
 
$header_section = "";

 
//Read to end of file with the newlines still attached into $f
 
$f=@file($f);
 
// Process all lines from 0 to count($f)
 
for ($i=0;$i<@count($f);$i++)
 {
 
$newsec=0;
 
$w=@trim($f[$i]);
 
$first_char = @substr($w,0,1);
  if (
$w)
  {
   if ((!
$r) or ($sec))
   {
  
// Look for [] chars round section headings
  
if ((@substr($w,0,1)=="[") and (@substr($w,-1,1))=="]") {$sec=@substr($w,1,@strlen($w)-2);$newsec=1;}
  
// Look for comments and number into array
  
if ((stristr($comment_chars, $first_char) === FALSE)) {} else {$sec=$w;$k="Comment".$num_comments;$num_comments = $num_comments +1;$v=$w;$newsec=1;$r[$k]=$v;echo "comment".$w.$newline;}
  
//
  
}
   if (!
$newsec)
   {
  
//
   // Look for the = char to allow us to split the section into key and value
  
$w=@explode("=",$w);$k=@trim($w[0]);unset($w[0]); $v=@trim(@implode("=",$w));
  
// look for the new lines
  
if ((@substr($v,0,1)=="\"") and (@substr($v,-1,1)=="\"")) {$v=@substr($v,1,@strlen($v)-2);}
   if (
$sec) {$r[$sec][$k]=$v;} else {$r[$k]=$v;}
   }
  }
 }
 return
$r;
}

?>
Julio López Garbayo
22.09.2005 22:53
I wrote a replacement function with following changes:
-It allows quotes and double quotes.
-It detects wether your .ini file has sections or not.
-It will read until eof in any case, even if a line contains errors.

I know it can be improved a lot, so feel free to work on it and, please, notify me if you do.

<?php
function parse_ini_file_quotes_safe($f)
{
 
$r=$null;
 
$sec=$null;
 
$f=@file($f);
 for (
$i=0;$i<@count($f);$i++)
 {
 
$newsec=0;
 
$w=@trim($f[$i]);
  if (
$w)
  {
   if ((!
$r) or ($sec))
   {
    if ((@
substr($w,0,1)=="[") and (@substr($w,-1,1))=="]") {$sec=@substr($w,1,@strlen($w)-2);$newsec=1;}
   }
   if (!
$newsec)
   {
   
$w=@explode("=",$w);$k=@trim($w[0]);unset($w[0]); $v=@trim(@implode("=",$w));
    if ((@
substr($v,0,1)=="\"") and (@substr($v,-1,1)=="\"")) {$v=@substr($v,1,@strlen($v)-2);}
    if (
$sec) {$r[$sec][$k]=$v;} else {$r[$k]=$v;}
   }
  }
 }
 return
$r;
}
?>
wickedfather at hotmail dot com
13.09.2005 9:19
Slight modification of write_ini_file that will keep values global in an array if they appear after an array

<?php function write_ini_file($path, $assoc_array)
{
   
$content = '';
   
$sections = '';

    foreach (
$assoc_array as $key => $item)
    {
        if (
is_array($item))
        {
           
$sections .= "\n[{$key}]\n";
            foreach (
$item as $key2 => $item2)
            {
                if (
is_numeric($item2) || is_bool($item2))
                   
$sections .= "{$key2} = {$item2}\n";
                else
                   
$sections .= "{$key2} = \"{$item2}\"\n";
            }      
        }
        else
        {
            if(
is_numeric($item) || is_bool($item))
               
$content .= "{$key} = {$item}\n";
            else
               
$content .= "{$key} = \"{$item}\"\n";
        }
    }      

   
$content .= $sections;

    if (!
$handle = fopen($path, 'w'))
    {
        return
false;
    }
   
    if (!
fwrite($handle, $content))
    {
        return
false;
    }
   
   
fclose($handle);
    return
true;
}
?>
rossetti at multilab2000 dot it
21.07.2005 11:19
I have modified the code to delete double quote from values.

if (substr($value, 0, 1) == '"' && substr($value, -1) == '"') { $value = substr($value, 1, -1); }
christian at thebartels dot de
14.07.2005 7:12
@phpcoder:

there is another small bug in your code. in readINIfile the line
if (substr($value, 1, 1) == '"' && substr($value, -1, 1) == '"') {$value = substr($value, 1, -1); }

should be

if (substr($value, 0, 1) == '"' && substr($value, -1, 1) == '"') {$value = substr($value, 1, -1); }

(note the 0 in the first substr statement)
otherwise if you read an ini file, save it and read again the items of the array will have " around them.
dimk at pisem dot net
14.07.2005 6:33
Class to access ini values at format "section_name.property", for example $myconf->get("system.name") returns a property "name" in section "system":

class Settings {

var $properties = array();

    function Settings() {
        $this->properties = parse_ini_file(_SETTINGS_FILE, true);
    }

    function get($name) {
        if(strpos($name, ".")) {
            list($section_name, $property) = explode(".", $name);
            $section =& $this->properties[$section_name];
            $name = $property;
        } else {
            $section =& $properties;
        }

        if(is_array($section) && isset($section[$name])) {
            return $section[$name];
        }
        return false;
    }

}
dreamscape
24.06.2005 18:10
I handy function to allow values with new lines if you are PHP4, is the following:

<?php
function prepareIniNl($string) {
    return
preg_replace("/(\r\n|\n|\r)/", "\\n", $string);
}
?>

Now, when writing your INI file, parse the value through the function and it will turn for example:

Value line 1
Value line 2

Into literally:

Value line 1\nValue line 2

Which is stored as a single line in the INI file.  And when you read the INI file back into PHP, the \n will be parsed and you're value will be back to:

Value line 1
Value line 2
phpcoder at cyberpimp dot pimpdomain dot com
16.06.2005 16:55
Oops.  There is a small bug in my writeINIfile code example submitted on  13-Jan-2005 11:31.  How embarassing.  The incorrect statement is:

  if (substr($comtext, -1, 1)==$commentchar && substr($comtext, -1, 1)!=$commentchar) {

Note that this is a logic error and the statement will never execute.  It should have been written as:

  if (substr($comtext, -1, 1)==$commentchar && substr($commenttext, -1, 1)!=$commentchar) {

Notice how in the corrected statement, the string passed to the second substr() function call is $commenttext and not $comtext.

The purpose of this statement was to determine when to strip off the extra comment character that inadvertently gets appended to the comment text block by the previous compounded str_replace code (to prepend comment characters on each line of the comment text block) when the original comment text ends with a new-line sequence.
dawalama at gmail dot com
20.05.2005 17:09
/*
* Search_ini_file refined.
*/
function search_ini_file ( $filename, $search_param, $return_section = false )
{
        $search_key =   (isset($search_param['key'])?$search_param['key']:false);
        $search_value = (isset($search_param['value'])?$search_param['value']:false);
        if ( !($search_key !==false || $search_value !==false) ){
                return false;
        }
        $retvalue = false;
        $handle = fopen($filename, 'r');
        if ( ($search_key !== false) && ($search_value !== false) ){
                $key_found = false;
                $retvalue['key'] = false;
                $retvalue['value'] = false;
                while( !feof($handle) ) {
                        $line = trim(fgets($handle, 4096));
                        if (preg_match("/^\[$search_key\].*?$/s",$line)){
                                $key_found = true;
                                $retvalue['key'] = true;
                                continue;
                        }
                        if ($key_found){
                                if (preg_match("/^\[.*?$/", trim($line))){
                                        break;
                                }else{
                                        if ($return_section){
                                                if ($line != '') {
                                                        list($k, $v) = split("=", $line);
                                                        $retvalue[$search_key][$k] = preg_replace("/;.*$/", "", $v);
                                                }   }   }

                                if (preg_match("/^$search_value\s*?=.*$/", $line)){
                                        $retvalue['value'] = true;
                                        break;
                                }   }  }
        }elseif ($search_key !== false){
                $keyfound = false;
                while ( !feof($handle) ){
                        $line = trim(fgets($handle, 4096));
                        if (preg_match("/^\[$search_key\].*?$/s",$line)){
                                $retvalue  = true;
                                if (!$return_section){
                                        break;
                                }else{
                                        $retvalue = Array();
                                        $keyfound = true;
                                        continue;
                                }  }

                        if ( $keyfound ){
                                if (preg_match("/^\[.*?$/", trim($line))){
                                        break;
                                }else{
                                        if ($return_section){
                                                if ($line != ''){
                                                        list($k, $v) = split("=", $line);
                                                        $retvalue[$search_key][$k] = preg_replace("/;.*$/", "", $v);
                                                }   }  }  }  }
        }elseif ($search_value !== false){
                while ( !feof($handle) ){
                        $line = trim(fgets($handle, 4096));

                        if (preg_match("/^$search_value\s*?=.*$/", $line)){
                                $retvalue = true;
                                if ($return_section){
                                        $retvalue = array();
                                        if ($line != ''){
                                                list($k, $v) = split("=", $line);
                                                $retvalue[$k] = preg_replace("/;.*$/", "", $v);
                                        }  }
                                break;
                        }   }  }
        fclose($handle);
        return $retvalue;
}
alex at NO_SPAM_PLEASE_sourcelibre dot com
16.03.2005 23:07
Note these will be converted to '1' and '0'

[section]
foo = yes
bar = no

Therefore, they need to be put between brackets if you want the value to be 'yes' and 'no'.
sly at noiretblanc dot org
8.03.2005 17:57
Be careful with the string "none", for example if you want to save a CSS border-style in your config.ini file :

[style]
borderstyle=none

will return:
   'style' => array ( 'borderstyle' => '' )

and not
   'style' => array ( 'borderstyle' => 'none' )

The solution is to quote the string none :
[style]
borderstyle="none"
hoc at notmail dot com
31.01.2005 14:29
to phpcoder at cyberpimp dot pimpdomain dot com:
thx for the read/write ini functions, they work like a charm ...

except for that one small (easy to find) substr-bug in the readINIfile-function:

counting with substr starts from 0, not 1, so
<?php
if (substr($value, 1, 1) == '"' && ...
?>
should be ...
<?php
if (substr($value, 0, 1) == '"' && ...
?>
nospam_phpnet at scovetta dot com
18.01.2005 1:21
As a Java programmer, I find PHPs lack of handing of multi-line ".properties" files a bit of a pain. I didn't see PEAR::Config handle this, so I hacked together a quick Properties class. This is by no means complete. It works for me, but I'm sure that someone can improve it. I'm also not an expert in PHP, so it may look like a kludge. Anyway, here it is:

<?php
/*
 * Properties class. Similar to Java Properties, deals with multi-line
 * properties files.
 *
 *  Created on Jan 17, 2005
 *
 * @author Michael V. Scovetta
 * This code is released under the GPL license.
 */

class Properties

    var
$properties;
      var
$keyValueSeparators = "=: \t\r\n";
      var
$whiteSpaceChars = " \t\r\n";
     
      function
Properties($file = null) {
         
$this->properties = array();
          if (
$file) {
             
$this->load($file);
          }
      }
 
 
      function
set_property( $key, $value ) {
       
$this->properties[$key] = $value;
    }
   
    function
get_property( $key ) {
        return
$this->properties[$key];
    }
   
    function
load( $file ) {
       
$lines = file($file);
       
$lc = 0;
       
$cont = false;
        foreach (
$lines as $line) {
            if (!
$cont) {            
               
$line = ltrim($line, $this->whiteSpaceChars);
               
$key = $this->findFirstIn($line, $this->keyValueSeparators);
               
                if (
$key === false)
                    continue;
           
               
$value = substr($line, $key+2);
               
$value = trim($value, $this->whiteSpaceChars);
               
               
$key = substr($line, 0, $key+1);
               
$key = trim($key, $this->whiteSpaceChars);
               
                if (
substr($value, strlen($value)-1, 1) === '\\') {
                   
$value = substr($value, 0, strlen($value)-1);
                   
$cont = true;
                } else {
                   
$this->properties[$key] = $value;
                }
            } else {
               
$line = trim($line, $this->whiteSpaceChars);
                if (
substr($line, strlen($line)-1, 1) === '\\') {
                   
$value .= substr($line, 0, strlen($line)-1);
                } else {
                   
$cont = false;
                   
$value .= $line;
                   
$this->properties[$key] = $value;
                }
            }
        }
    }       
   
    function
continueLine($line) {
       
$slashCount = 0;
       
$index = strlen($line) - 1;
        while ((
$index >= 0) && (substr($line, $index--, 1) == '\\'))
           
$slashCount++;
        return (
$slashCount % 2 == 1);
    }
   
   
/**
     * Finds the first occurance of any character of $choices in $txt
     */
   
function findFirstIn( $txt, $choices, $start = null)
    {
          
$pos = -1;
          
$arr = array();
           for (
$i=0; $i<strlen($choices); $i++) {
              
array_push($arr, substr($choices, $i, 1));
           }
           foreach(
$arr as $v ) {
              
$p = strpos( $txt, $v, $start );
               if (
$p===FALSE)
                   continue;
               if ((
$p<$pos)||($pos==-1))
                  
$pos = $p;
           }
           return
$pos;
    }

    function
toArray() {
        return
$this->properties;
    }

}
?>
phpcoder at cyberpimp dot pimpdomain dot com
13.01.2005 22:31
Here's a much better way of reading and writing INI files.  (much fewer character restrictions, automatic comment header, binary safe, etc.)

<?php
/*
Function to replace PHP's parse_ini_file() with much fewer restritions, and
a matching function to write to a .INI file, both of which are binary safe.

Version 1.0

Copyright (C) 2005 Justin Frim <phpcoder@cyberpimp.pimpdomain.com>

Sections can use any character excluding ASCII control characters and ASCII
DEL.  (You may even use [ and ] characters as literals!)

Keys can use any character excluding ASCII control characters, ASCII DEL,
ASCII equals sign (=), and not start with the user-defined comment
character.

Values are binary safe (encoded with C-style backslash escape codes) and may
be enclosed by double-quotes (to retain leading & trailing spaces).

User-defined comment character can be any non-white-space ASCII character
excluding ASCII opening bracket ([).

readINIfile() is case-insensitive when reading sections and keys, returning
an array with lower-case keys.
writeINIfile() writes sections and keys with first character capitalization.
Invalid characters are converted to ASCII dash / hyphen (-).  Values are
always enclosed by double-quotes.

writeINIfile() also provides a method to automatically prepend a comment
header from ASCII text with line breaks, regardless of whether CRLF, LFCR,
CR, or just LF line break sequences are used!  (All line breaks are
translated to CRLF)
*/

function readINIfile ($filename, $commentchar) {
 
$array1 = file($filename);
 
$section = '';
  foreach (
$array1 as $filedata) {
   
$dataline = trim($filedata);
   
$firstchar = substr($dataline, 0, 1);
    if (
$firstchar!=$commentchar && $dataline!='') {
     
//It's an entry (not a comment and not a blank line)
     
if ($firstchar == '[' && substr($dataline, -1, 1) == ']') {
       
//It's a section
       
$section = strtolower(substr($dataline, 1, -1));
      }else{
       
//It's a key...
       
$delimiter = strpos($dataline, '=');
        if (
$delimiter > 0) {
         
//...with a value
         
$key = strtolower(trim(substr($dataline, 0, $delimiter)));
         
$value = trim(substr($dataline, $delimiter + 1));
          if (
substr($value, 1, 1) == '"' && substr($value, -1, 1) == '"') { $value = substr($value, 1, -1); }
         
$array2[$section][$key] = stripcslashes($value);
        }else{
         
//...without a value
         
$array2[$section][strtolower(trim($dataline))]='';
        }
      }
    }else{
     
//It's a comment or blank line.  Ignore.
   
}
  }
  return
$array2;
}

function
writeINIfile ($filename, $array1, $commentchar, $commenttext) {
 
$handle = fopen($filename, 'wb');
  if (
$commenttext!='') {
   
$comtext = $commentchar.
     
str_replace($commentchar, "\r\n".$commentchar,
       
str_replace ("\r", $commentchar,
         
str_replace("\n", $commentchar,
           
str_replace("\n\r", $commentchar,
             
str_replace("\r\n", $commentchar, $commenttext)
            )
          )
        )
      )
    ;
    if (
substr($comtext, -1, 1)==$commentchar && substr($comtext, -1, 1)!=$commentchar) {
     
$comtext = substr($comtext, 0, -1);
    }
   
fwrite ($handle, $comtext."\r\n");
  }
  foreach (
$array1 as $sections => $items) {
   
//Write the section
   
if (isset($section)) { fwrite ($handle, "\r\n"); }
   
//$section = ucfirst(preg_replace('/[\0-\37]|[\177-\377]/', "-", $sections));
   
$section = ucfirst(preg_replace('/[\0-\37]|\177/', "-", $sections));
   
fwrite ($handle, "[".$section."]\r\n");
    foreach (
$items as $keys => $values) {
     
//Write the key/value pairs
      //$key = ucfirst(preg_replace('/[\0-\37]|=|[\177-\377]/', "-", $keys));
     
$key = ucfirst(preg_replace('/[\0-\37]|=|\177/', "-", $keys));
      if (
substr($key, 0, 1)==$commentchar) { $key = '-'.substr($key, 1); }
     
$value = ucfirst(addcslashes($values,''));
     
fwrite ($handle, '    '.$key.' = "'.$value."\"\r\n");
    }
  }
 
fclose($handle);
}

?>
georg at linux dot ee
9.01.2005 23:15
<?php
   
/**
     * Function to create lower-case key references to parse_ini_file() result.
     * Copyright (C) 2005  Joosep-Georg Järvemaa <georg@linux.ee>
     *
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
     * version 2.1 of the License, or (at your option) any later version.
     *
     * This library is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser General Public
     * License along with this library; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */

    /**
     * Creates lower-case references to configuration array loaded from .INI.
     *
     * Function uses recursion to dive into configuration sub-sections and
     * marks already checked sections with additional key '_ns_ini_lcrefs'.
     *
     * @param arr Configuration array.
     */
   
function ns_ini_lcrefs(& $arr) {

        foreach (
array_keys($arr) as $_k) {

            if (
is_array($arr[$_k]) && !isset($arr[$_k]['_ns_ini_lcrefs']))
               
ns_ini_lcrefs($arr[$_k]);

            if ((
$_lc_k = strtolower($_k)) != $_k)
               
$arr[$_lc_k] =& $arr[$_k];

        }

       
$arr['_ns_ini_lcrefs'] = true;

    }
// function ns_ini_lcrefs()

    /* EOF */
?>
Nick Deppe
20.10.2004 4:03
I just noticed that the code I wrote before had an error in it.  I have the fix posted here: 

That is what happens when you don't error check the code first.  Duh.

Here is yet another version of write_ini_file.  This version takes data types into account.  If the file is numeric or boolean, the value is written in the ini file without quotes.  Else it will be written with quotes.

Please note that if a string that CAN be converted into a number WILL be converted into a number because I used the is_numeric function.  If you want to make sure that the data type is strictly preserved, use the is_integer and is_double functions in place of the is_numeric function.

<?php

if(!function_exists('write_ini_file')) {
  function
write_ini_file($path, $assoc_array) {

   foreach(
$assoc_array as $key => $item) {
     if(
is_array($item)) {
      
$content .= "\n[{$key}]\n";
       foreach (
$item as $key2 => $item2) {
         if(
is_numeric($item2) || is_bool($item2))
          
$content .= "{$key2} = {$item2}\n";
         else
          
$content .= "{$key2} = \"{$item2}\"\n";
       }       
     } else {
       if(
is_numeric($item) || is_bool($item))
        
$content .= "{$key} = {$item}\n";
       else
        
$content .= "{$key} = \"{$item}\"\n";
     }
   }       

   if(!
$handle = fopen($path, 'w')) {
     return
false;
   }

   if(!
fwrite($handle, $content)) {
     return
false;
   }

  
fclose($handle);
   return
true;

  }

}

?>
bkw at weisshuhn dot de
27.09.2004 23:56
Beware that currently you cannot have a closing square bracket (]) in any of the values if you are using sections, no matter how you quote.
See: http://bugs.php.net/bug.php?id=28804

This bug also seems to affect PEAR::Config.
tomasz.frelik(at)enzo.pl
9.08.2004 1:49
Here is a better version of write_ini_file() function, which can found below. This version allows you to use sections and still have "global" variables in ini file. The structure of resulting ini file mirrors the structure of the array passed to the function. You can have sections or no, it's up to you.

function write_ini_file($path, $assoc_array) {

    foreach ($assoc_array as $key => $item) {
        if (is_array($item)) {
            $content .= "\n[$key]\n";
            foreach ($item as $key2 => $item2) {
                $content .= "$key2 = \"$item2\"\n";
            }       
        } else {
            $content .= "$key = \"$item\"\n";
        }
    }       
   
    if (!$handle = fopen($path, 'w')) {
        return false;
    }
    if (!fwrite($handle, $content)) {
        return false;
    }
    fclose($handle);
    return true;
}
hfuecks at phppatterns dot com
15.07.2004 17:20
parse_ini_file seems to have changed it's signature between PHP 4.3.x and PHP 5.0.0 (can't find any relevant changelog / cvs entries referring to this).

In PHP 4.3.x and below return value was a boolean FALSE if the ini file could not be found. With PHP 5.0.0 the return value is an empty array if the file is not found.
php at isaacschlueter dot com
22.06.2004 8:47
Even better than putting the <?php at the head of the file is to do something like this:

--
config.ini.php--
; <?
php die( 'Please do not access this page directly.' ); ?>
; This is the settings page, do not modify the above line.
setting = value
...
Jomel (k95vz5f02 AT sneakemail DOT com)
19.06.2004 17:02
based entirely on LIU student's code (thanks), here's a write_ini_file function you can use whether or not the array you are writing is sorted into sections.
It is designed so that $arr1 equals $arr2 in both the cases below, using sections:
<?php
$arr1
= parse_ini_file($filename, true);
write_ini_file(parse_ini_file($filename, true), $filename, true);
$arr2 = parse_ini_file($filename, true);
?>
and without sections:
<?php
$arr1
= parse_ini_file($filename);
write_ini_file(parse_ini_file($filename), $filename);
$arr2 = parse_ini_file($filename);
?>
i.e. files written using write_ini_file will be semantically identical (as far as parse_ini_file can see) to the originals.

Here is the code:

<?php
if (!function_exists('write_ini_file')) {
    function
write_ini_file($assoc_arr, $path, $has_sections=FALSE) {
       
$content = "";

        if (
$has_sections) {
            foreach (
$assoc_arr as $key=>$elem) {
               
$content .= "[".$key."]\n";
                foreach (
$elem as $key2=>$elem2) {
                   
$content .= $key2." = \"".$elem2."\"\n";
                }
            }
        }
        else {
            foreach (
$assoc_arr as $key=>$elem) {
               
$content .= $key." = \"".$elem."\"\n";
            }
        }

        if (!
$handle = fopen($path, 'w')) {
            return
false;
        }
        if (!
fwrite($handle, $content)) {
            return
false;
        }
       
fclose($handle);
        return
true;
    }
}
?>

Incidentally I wrapped it inside an if (!function_exists(...)) block so you can just put this wherever it's needed in your code without having to worry about it being declared several times.
Warning: if you read an ini file then write it using <?php write_ini_file(parse_ini_file($fname), $fname); ?>, any sections will obviously be lost.
Note also: unquoted values will be quoted and varname=true will become varname = "1" when writing an ini file back to itself using <?php write_ini_file(parse_ini_file($fname, true), $fname, true); ?> or <?php write_ini_file(parse_ini_file($fname), $fname); ?>. This should make no difference, but it might cause the types of the variables to change in case you plan on using === or !== comparisions.
forceone at justduck.net
15.06.2004 5:00
A better version of parse_ini_str that takes into account values that are named the same.

<?php
function parse_ini_str($Str,$ProcessSections = TRUE) {
  
$Section = NULL;
  
$Data = array();
   if (
$Temp = strtok($Str,"\r\n")) {
      do {
         switch (
$Temp{0}) {
            case
';':
            case
'#':
               break;
            case
'[':
               if (!
$ProcessSections) {
                  break;
               }
              
$Pos = strpos($Temp,'[');
              
$Section = substr($Temp,$Pos+1,strpos($Temp,']',$Pos)-1);
              
$Data[$Section] = array();
               break;
         default:
           
$Pos = strpos($Temp,'=');
            if (
$Pos === FALSE) {
               break;
            }
           
$Value = array();
           
$Value["NAME"] = trim(substr($Temp,0,$Pos));
           
$Value["VALUE"] = trim(substr($Temp,$Pos+1),' "');
           
            if (
$ProcessSections) {
              
$Data[$Section][] = $Value;
            }
            else {
              
$Data[] = $Value;
            }
            break;
         }
      } while (
$Temp = strtok("\r\n"));
   }
   return
$Data;
}
?>

Example:

[Files]
File=File1
File=File2

would return:

array (
   'Files' => array (
      0 => array (
         'NAME' => 'File',
         'VALUE' => File1',
      ),
      1 => array (
         'NAME' => 'File',
         'VALUE' => 'File2',
      ),
   ),
)
LIU student
19.03.2004 15:08
[Editor's note: The fwrite()-line should look like: "if (fwrite($handle, $content) === false) {" to avoid returning false when the array is empty --victor@php.net]


function writeIni($assoc_arr, $path){
    $content = "";
   
    foreach ( $assoc_arr as $key=>$elem ){
        $content .= "[".$key."]\n";
        foreach ( $elem as $key2=>$elem2){
            $content .= $key2." = \"".$elem2."\"\n";
        }
    }
   

    if (!$handle = fopen($path, 'w')) {
           return false;
    }
       if (!fwrite($handle, $content)) {
       return false;
       }  
    fclose($handle);
    return true;
}
waikeatNOSPAM at archerlogic dot com
9.11.2003 15:37
I found that this function will not work on remote files.
I tried

$someArray = parse_ini_file("http://www.someweb.com/setting.ini");

and it reports

Cannot Open 'http://www.someweb.com/setting.ini' for reading ...
rus dot grafx at usa dot net
11.10.2003 3:15
Instead of using parse_ini_file() function I would recommend to use PEAR's Config package which is MUCH more flexible (assuming that you don't mind using PEAR and OOP). Have a closer look at http://pear.php.net/package/Config
dshearin at excite dot com
20.06.2003 5:47
I found another pitfall to watch out for. The key (to the left of the equal sign) can't be the same as one of the predefined values, like yes, no, on, off, etc. I was working on a script that read in an ini file that matched the country codes of top level domains to the full name of the country. I kept getting a parse error everytime it got to the entry for Norway ("no"). I fixed the problem by sticking a dot in front of each of the country codes.

10.05.2003 13:05
If your configuration file holds any sensitive information (such as database login details), remember NOT to place it within your document root folder! A common mistake is to replace config.inc.php files, which are formatted in PHP:
<?php
$database
['host'] = 'localhost';
// etc...
?>

With config.ini files which are written in plain text:
[database]
host = localhost

The file config.ini can be read by anyone who knows where it's located, if it's under your document root folder. Remember to place it above!
kieran dot huggins at rogers dot com
7.01.2003 19:24
Just a quick note for all those running into trouble escaping double quotes:

I got around this by "base64_encode()"-ing my content on the way in to the ini file, and "base64_decode()"-ing on the way out.

Because base64 uses the "=" sign, you will have to encapsulate the entire value in double quotes so the line looks like this:

    varname = "TmlhZ2FyYSBGYWxscywgT04="

When base64'd, your strings will retain all \n, \t...etc...  URL's retain everything perfectly :-)

I hope some of you find this useful!

Cheers, Kieran
fbeyer at clickhand dot de
29.11.2002 18:37
Besides the features mentioned above (eg. core constants, booleans), you can also access user-defined constants in ini files! This is handy if you want to create a bit-field, for example:

+++ PHP +++

// Define pizza toppings
define('PIZZA_HAM',           1);
define('PIZZA_PINEAPPLE',     2);
define('PIZZA_ONION',         4);
define('PIZZA_MOZARELLA',     8);
define('PIZZA_GARLIC',        16);

// Read predefined pizzas
$pizzas = parse_ini_file('pizzas.ini');

if ($pizzas[$user_pizza] & PIZZA_ONION) {
    // Add onions to the pizza
}

+++ INI +++

[pizzas]

; Define pizzas
hawaii = PIZZA_HAM | PIZZA_PINEAPPLE
stinky = PIZZA_ONION | PIZZA_GARLIC
bob at kludgebox dot com
26.03.2002 19:27
And for the extra-paranoid like myself, add a rule into your httpd.conf file so that *.ini (or *.inc) in my case can't be sent to a browser:

<Files *.inc> 
    Order deny,allow
    Deny from all
</Files>
JoshuaStarr at aelana dot com
15.01.2002 4:41
It should be noted that in all of our attempts you cannot escape a double quote in the value when read with the parse_ini_file() function.

;============================
; Example Configuration File
;============================
[category]
title = "Best Scripting Language"
desc = "See <a href=\"http://www.php.net/\">PHP</a>!"

If this file is read by parse_ini_file() the link value will not be set because of the escaped double quotes.



PHP Powered Diese Seite bei php.net
The PHP manual text and comments are covered by the Creative Commons Attribution 3.0 License © the PHP Documentation Group - Impressum - mail("TO:Reinhard Neidl",...)