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/
parse_ini_file
(PHP 4, PHP 5)
parse_ini_file — Analyse un fichier de configuration
Description
parse_ini_file() charge le fichier filename et retourne les configurations qui s'y trouvent sous forme d'un tableau associatif.
La structure des fichiers de configuration lus est similaire à celle de php.ini.
Liste de paramètres
- filename
-
Le nom du fichier de configuration à analyser.
- process_sections
-
En passant le deuxième paramètre optionnel à process_sections , vous obtiendrez un tableau multidimensionnel avec les noms des sections. La valeur par défaut de ce paramètre est FALSE
Valeurs de retour
La configuration est retournée sous la forme d'un tableau associatif.
Historique
| Version | Description |
|---|---|
| 5.2.4 | Les noms de section et les clés, composés de numéros, sont maintenant évalués comme des entiers, ceux commençant par un 0 seront évalués comme des octales, et ceux commençant par 0x, comme des hexadécimaux. |
| 5.0.0 | Les valeurs entourées par des guillemets, peuvent contenir des nouvelles lignes. |
| 4.2.1 | Cette fonction est maintenant affectée par le safe mode et l'open_basedir. |
Exemples
Exemple #1 Contenu du fichier sample.ini
; Ceci est un fichier de configuration ; Les commentaires commencent par ';', comme dans php.ini [first_section] one = 1 five = 5 animal = BIRD [second_section] path = "/usr/local/bin" URL = "http://www.example.com/~username"
Exemple #2 Exemple avec parse_ini_file()
Les constantes peuvent aussi être utilisées dans le fichier .ini, ce qui fait que si vous définissez une constante avant d'exécuter parse_ini_file(), elle sera intégrée dans les résultats. Seules les valeurs de configuration sont remplacées par leur équivalent en constantes. Par exemple :
<?php
define('BIRD', 'Dodo bird');
// Analyse sans sections
$ini_array = parse_ini_file("sample.ini");
print_r($ini_array);
// Analyse avec sections
$ini_array = parse_ini_file("sample.ini", true);
print_r($ini_array);
?>
L'exemple ci-dessus va afficher quelque chose de similaire à :
Array ( [one] => 1 [five] => 5 [animal] => Dodo bird [path] => /usr/local/bin [URL] => http://www.example.com/~username ) Array ( [first_section] => Array ( [one] => 1 [five] => 5 [animal] = Dodo bird ) [second_section] => Array ( [path] => /usr/local/bin [URL] => http://www.example.com/~username ) )
Notes
Note: Cette fonction n'a rien a voir avec le fichier php.ini. Ce dernier a déjà était traité lorsque vous commencez à exécuter votre script. Cette fonction peut vous permettre de lire vos propres fichiers de configuration.
Note: Si une valeur du fichier ini contient des données non-alphanumériques, il faut la protéger en la plaçant entre guillemets doubles (").
Note: Il y a des mots réservés qui ne doivent pas être utilisés en tant que clés dans les fichiers ini. Cela inclut : null, yes, no, true et false. Les valeurs null, no et false donnent "", yes et true donnent "1". Les caractères {}|&~![()" ne doivent pas être utilisés n'importe où dans la clé et ont une signification spéciale dans la valeur.
parse_ini_file
02-Jul-2008 09:14
01-May-2008 12: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
)
?>
29-Oct-2007 03: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.
*/
?>
24-Oct-2007 10:26
Looks like in PHP 5.3.0 special characters like \n are extrapolated into real newlines. Gotta use \\n.
03-Oct-2007 03: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);
}
}
}
?>
26-Sep-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 );
}
29-Jun-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'));
?>
25-Jun-2007 07: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
)
)
25-May-2007 09: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;
?>
27-Mar-2007 10: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
02-Feb-2007 07: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;
}
?>
03-Jan-2007 04:54
The ahull version of the parse_ini_file_quotes_safe can not handle unicode... the original version from Julio L Garbayo can.
15-Nov-2006 06: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.
31-Oct-2006 08: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!
)
?>
this function won't parse a remote INI file, even with allow_url_fopen turned on.
01-Oct-2006 09: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
18-Sep-2006 02:46
or better
on first line :
;<?php exit(' you won\'t see my ini file'); ?>
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: (...)".
24-Mar-2006 05: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.
22-Feb-2006 02: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.
16-Feb-2006 01: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'.
14-Feb-2006 01: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 !
26-Jan-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;
}
?>
21-Oct-2005 08:45
[A feature request for a third parameter, to turn off the following insecure behaviour has been submitted: http://bugs.php.net/bug.php?id=34949 - I'm just documenting it here so that people are aware that they need to take the insecurity of the current behaviour into consideration when programming.]
Be warned that, in its current (2-argument) form, this function should be avoided when processing user-provided ini files, as data leakage may occur if the user provides an ini file with unquoted string values.
To avoid this problem, it's vital that if your program stores any sensitive data in constants, that you either pre-scan the ini file for unquoted strings, or that you do not use this function.
A suitable pre-parse parser might be as follows.
This assumes that there are no non-word (a-zA-Z0-9_) characters in your keys, and minimises whitespace.
It tries to convert intelligently, like so:
value1 = value ; this is a comment
value2 = value; with semicolon in
to
value1 = "value" ; this is a comment
value2 = "value; with semicolon in"
<?php
$file = file_get_contents('user_provided.ini');
$file2 = preg_replace('/^
(\s*\w+\s*=\s*) # Part \1 - the key and initial whitespace.
( # Part \2 - the value to be quoted
(?:(?!\s;)[^"\r\n]) # Anything but \r, ", \s;, \n
*? # As little as possible of that, minimise whitespace.
)
( # Part \3 - everything after the value
\s* # Optional whitespace.
(?:\s;.*)? # Optional comment preceded by a space
)
$/mx', '\1"\2"\3', $file);
file_put_contents('user_provided.ini2', $file2);
?>
22-Sep-2005 10: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,