Ошибка парсинга ini файла что это
parse_ini_file() загружает ini-файл, указанный в аргументе filename , и возвращает его настройки в виде ассоциативного массива.
Структура ini-файла похожа на структуру php.ini .
Haskell и Parsec
Начните с установки Parsec (можно взять на официальном сайте или поискать готовые пакеты для вашей OS). Процесс установки для разных систем может быть различным, поэтому я не буду его здесь описывать.
Теперь переведём в Haskell нетерминал section. section намного сложнее inidata и поэтому я запишу его в do-нотации.
14 section = do
15 char '['
16 name ident
17 char ']'
18 stringSpaces
19 char ' \n '
20 spaces
21 el many entry
22 return (name, el)
Этот код — почти дословный перевод нетерминала section из нотации Бэкуса-Наура. Функция char создаёт примитивный парсер, который парсит один символ. Стоит обратить внимание на строки 16, 21 и 22. В строке 16 мы сохраняем значение нетерминала ident (имени секции), а в строке 21 сохраняем список записей, которые идут за заголовком секции. В строке 22 мы возвращаем прочитанные название секции и список записей (это соответствует типу Section).
Переходим к записям.
24 entry = do
25 k ident
26 stringSpaces
27 char '='
28 stringSpaces
29 v value
30 spaces
31 return (k, v)
Если вы поняли, как мы построили парсер для section, то тут никаких проблем быть не должно. В кратце: в строчках 25 и 29 сохраняем имя параметра и его значение, и возвращаем пару составленную из них (соответствует типу Entry).
Аналогично поступаем с нетерминалом для значения, но используя парсер noneOf, который обратен к oneOf.
34 value = many (noneOf " \n " ) >>= return . trim
Остался последний нетерминал — stringSpaces (нетерминал spaces уже есть в Parsec).
36 stringSpaces = many (char ' ' char ' \t ' )
С грамматикой всё. Осталось определить несколько полезных функций и, конечно, сам main.
Функция trim нужна для удаления лишних пробелов в начале и в конце строки.
38 trim = f . f
39 where f = reverse . dropWhile isSpace
Функция split разделяет текст на строки используя разделитель delim, причём сам разделитель остаётся в конце строки.
41 split delim = foldr f [[]]
42 where
43 f x rest @ (r : rs)
44 | x == delim = [delim] : rest
45 | otherwise = (x : r) : rs
Функция removeComments удаляет комментарии и пустые строки: она разбивает текст на строки, удаляет те из них, которые начинаются с ";" или "\n", а потом снова их склеивает.
47 removeComments = foldr ( ++ ) [] . filter comment . split ' \n '
48 where comment [] = False
49 comment (x : _) = (x /= ';' ) && (x /= ' \n ' )
Функция findValue ищет в IniData значение параметра по имени секции и имени параметра (вычисление происходит в монаде Maybe). Сначала находим секцию по имени, а потом среди записей из секции находим нужный параметр. Если в какой-то момент мы ничего не найдём, то функция просто вернёт Nothing.
51 findValue ini s p = do
52 el find ( \ x -> fst x == s) ini
53 v find ( \ x -> fst x == p) (snd el)
54 return $ snd $ v
Переходим к последнему шагу — функции main.
Теперь код полностью написан. Компилируем его и запускаем на тестовом примере.
$ ghc --make ini.hs -o ini_hs
[1 of 1] Compiling Main ( ini.hs, ini.o )
Linking ini_hs .
$ ./ini_hs /usr/lib/firefox-3.0.5/application.ini App ID
$ ./ini_hs /usr/lib/firefox-3.0.5/application.ini App IDD
Can't find requested parameter
Надеюсь, что данная статья поможет вам написать свой собственный парсер =)
Интересное замечание: вы можете сравнить парсер из этой статьи с парсером на С++ из статьи «Создаём парсер для ini-файлов на С++».
parse_ini_file() loads in the ini file specified in filename , and returns the settings in it in an associative array.
The structure of the ini file is the same as the php.ini 's.
Parameters
The filename of the ini file being parsed. If a relative path is used, it is evaluated relative to the current working directory, then the include_path.
By setting the process_sections parameter to true , you get a multidimensional array, with the section names and settings included. The default for process_sections is false
Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW . If INI_SCANNER_RAW is supplied, then option values will not be parsed.
As of PHP 5.6.1 can also be specified as INI_SCANNER_TYPED . In this mode boolean, null and integer types are preserved when possible. String values "true" , "on" and "yes" are converted to true . "false" , "off" , "no" and "none" are considered false . "null" is converted to null in typed mode. Also, all numeric strings are converted to integer type if it is possible.
User Contributed Notes 15 notes
Here is a quick parse_ini_file wrapper to add extend support to save typing and redundancy.
/**
* 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:
/*
[base]
host=localhost
user=testuser
pass=testpass
database=default
[archive : base]
database=archive
*/
?>
As if it were like this:
/*
[base]
host=localhost
user=testuser
pass=testpass
database=default
You may want, in some very special cases, to parse multi-dimensional array with N levels in your ini file. Something like setting[data][config][debug] = true will result in an error (expected " default"> function parse_ini_file_multi ( $file , $process_sections = false , $scanner_mode = INI_SCANNER_NORMAL ) $explode_str = '.' ;
$escape_char = "'" ;
// load ini file the normal way
$data = parse_ini_file ( $file , $process_sections , $scanner_mode );
if (! $process_sections ) $data = array( $data );
>
foreach ( $data as $section_key => $section ) // loop inside the section
foreach ( $section as $key => $value ) if ( strpos ( $key , $explode_str )) if ( substr ( $key , 0 , 1 ) !== $escape_char ) // key has a dot. Explode on it, then parse each subkeys
// and set value at the right place thanks to references
$sub_keys = explode ( $explode_str , $key );
$subs =& $data [ $section_key ];
foreach ( $sub_keys as $sub_key ) if (!isset( $subs [ $sub_key ])) $subs [ $sub_key ] = [];
>
$subs =& $subs [ $sub_key ];
>
// set the value at the right place
$subs = $value ;
// unset the dotted key, we don't need it anymore
unset( $data [ $section_key ][ $key ]);
>
// we have escaped the key, so we keep dots as they are
else $new_key = trim ( $key , $escape_char );
$data [ $section_key ][ $new_key ] = $value ;
unset( $data [ $section_key ][ $key ]);
>
>
>
>
if (! $process_sections ) $data = $data [ 0 ];
>
return $data ;
>
?>
The following file:
/*
[normal]
foo = bar
; use quotes to keep your key as it is
'foo.with.dots' = true
[dictionary]
foo[debug] = false
foo[path] = /some/path
[multi]
foo.data.config.debug = true
foo.data.password = 123456
*/
?>
will result in:
parse_ini_file_multi ( 'file.ini' , true );
Array
(
[ normal ] => Array
(
[ foo ] => bar
[ foo . with . dots ] => 1
)
[array] => Array
(
[ foo ] => Array
(
[ 0 ] => 1
[ 1 ] => 2
)
)
[ dictionary ] => Array
(
[ foo ] => Array
(
[ debug ] =>
[ path ] => / some / path
)
)
[ multi ] => Array
(
[ foo ] => Array
(
[ data ] => Array
(
[ config ] => Array
(
[ debug ] => 1
)
[ password ] => 123456
)
)
)
)
?>
.ini files or JSON file format as it is also known as, are very useful format to store stuff in. Especially large arrays.
Strangely enough there is this nice function to read the file, but no function to write it.
Use it as: put_ini_file(string $file, array $array)
function put_ini_file ( $file , $array , $i = 0 ) $str = "" ;
foreach ( $array as $k => $v ) if ( is_array ( $v )) $str .= str_repeat ( " " , $i * 2 ). "[ $k ]" . PHP_EOL ;
$str .= put_ini_file ( "" , $v , $i + 1 );
>else
$str .= str_repeat ( " " , $i * 2 ). " $k = $v " . PHP_EOL ;
>
if( $file )
return file_put_contents ( $file , $str );
else
return $str ;
>
?>
This is a simple (but slightly hackish) way of avoiding the character limitations (in values):
define ( 'QUOTE' , '"' );
$test = parse_ini_file ( 'test.ini' );
echo "" ;
print_r ( $test );
?>
contents of test.ini:
park yesterday = "I (walked) | " QUOTE"the"QUOTE " park yesterday & saw ~three~ dogs!"
Array
(
[ park yesterday ] => I ( walked ) | < to >"the" park yesterday & saw ~ three ~ dogs !
)
?>
The documentation states:
Characters ?<>|&~!()^" must not be used anywhere in the key and have a special meaning in the value.
Here's the results of my experiments on what they mean:
; | is used for bitwise OR
three = 2|3
; & is used for bitwise AND
four = 6&5
; ^ is used for bitwise XOR
five = 3^6
; ~ is used for bitwise negate
negative_two = ~1
; () is used for grouping
seven = (8|7)&(6|5)
; $ <. >is used for grabbing values from the environment, or previously defined values.
path = $
also = $
; ? I have no guess for
; ! I have no guess for
This function for save ini files
function array_to_ini ( $array , $out = "" )
<
$t = "" ;
$q = false ;
foreach( $array as $c => $d )
<
if( is_array ( $d )) $t .= array_to_ini ( $d , $c );
else
<
if( $c === intval ( $c ))
<
if(!empty( $out ))
<
$t .= "\r\n" . $out . " = \"" . $d . "\"" ;
if( $q != 2 ) $q = true ;
>
else $t .= "\r\n" . $d ;
>
else
<
$t .= "\r\n" . $c . " = \"" . $d . "\"" ;
$q = 2 ;
>
>
>
if( $q != true && !empty( $out )) return "[" . $out . "]\r\n" . $t ;
if(!empty( $out )) return $t ;
return trim ( $t );
>
function save_ini_file ( $array , $file )
<
$a = array_to_ini ( $array );
$ffl = fopen ( $file , "w" );
fwrite ( $ffl , $a );
fclose ( $ffl );
>
?>
Looks like in PHP 5.3.0 special characters like \n are extrapolated into real newlines. Gotta use \\n.
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 " TmlhZ2FyYSBGYWxscywgT04 note" >
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).
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:
$stores = parse_ini ( 'stores.ini' );
print_r ( $stores );
?>
An example ini file:
/*
;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.
*/
?>
Not mentioned in the documentation, this function acts like include:
"Files are included based on the file path given or, if none is given, the include_path specified. If the file isn't found in the include_path, include will finally check in the calling script's own directory and the current working directory before failing."
(At least for PHP 7; have not checked PHP 5.)
This core function won't handle ini key[][] = value(s), (multidimensional arrays), so if you need to support that kind of setup you will need to write your own function. one way to do it is to convert all the key = value(s) to array string Ошибка парсинга ini файла что это[][]=value(s), then use parse_str() to convert all those Ошибка парсинга ini файла что это[][]=value(s) that way you just read the ini file line by line, instead of doing crazy foreach() loops to handle those (multidimensional arrays) in each section, example.
ini file. config.php
; This is a sample configuration file
; Comments start with ';' , as in php . ini
[ first_section ]
one = 1
five = 5
animal = BIRD
[ third_section ]
phpversion [] = "5.0"
phpversion [] = "5.1"
phpversion [] = "5.2"
phpversion [] = "5.3"
a [][][] = b
a [][][][] = c
a [ test_test ][][] = d
test [ one ][ two ][ three ] = true
?>
echo parse_ini_file ( "C:\\services\\www\\docs\\config.php" );
// PHP Warning: syntax error, unexpected TC_SECTION, expecting '=' line 27 -> a[][][] = b
Here it simple function that handles (multidimensional arrays) without looping each key[][]= value(s)
function getIni ( $file , $sections = FALSE )
$return = array ();
$config = fopen ( $file , 'r' );
while ( ! feof ( $config ) )
<
$line = trim ( fgets ( $config , 1024 ) );
$line = ( $line == '' ) ? ' ' : $line ;
if ( $sections )
$header = 'config[' . trim ( substr ( $line , 1 , - 1 ) ) . ']' ;
>
else
$header = 'config' ;
>
$kv = array_map ( 'trim' , explode ( '=' , $line ) );
$kv [ 0 ] = str_replace ( ' ' , '+' , $kv [ 0 ] );
$kv [ 1 ] = str_replace ( ' ' , '+' , $kv [ 1 ] );
if ( ( $pos = strpos ( $kv [ 0 ], '[' ) ) !== FALSE )
$kv [ 0 ] = '[' . substr ( $kv [ 0 ], 0 , $pos ) . ']' . substr ( $kv [ 0 ], $pos );
>
else
$kv [ 0 ] = '[' . $kv [ 0 ] . ']' ;
>
$bt = strtolower ( $kv [ 1 ] );
if ( in_array ( $bt , array ( 'true' , 'false' , 'on' , 'off' ) ) )
$kv [ 1 ] = ( $bt == 'true' || $bt == 'on' ) ? TRUE : FALSE ;
>
$keeper [] = $header . $kv [ 0 ] . '=' . $kv [ 1 ];
>
>
return $return [ 'config' ];
>
print_r ( $config -> getIni ( "C:\\services\\www\\docs\\config.php" ), $sections );
Если Вы нашли эту статью через поисковые системы, то у Вас наверняка проблема с парсингом ini-файла. Я долго думал к какой категории отнести эту статью: Zend Framework или PHP.
Недавно передо мной стояла задача: добавить мультиязычность (возможность использовать 2 и более языка) в один из проектов на Zend Framework. А для хранения строк я решил воспользоваться ini-адаптером (т.е. хранить данные в ini файлах). Но этому я планирую посвятить отдельную статью, да и все таки ошибка, которая возникла, относится непосредственно к PHP
[php]Warning: syntax error, unexpected TC_CONSTANT in /home/stafox/*/application/languages/en.ini on line 3[/php]
Вот вид того самого en.ini:
[php] home = home
downloads = downloads
no_limits = no limits
link = link
…
[/php]
Ну вот вообще не нравилась эта третья строка, а точнее слово no, т.к. если его убрать, все отлично. Чтобы выяснить в чем проблема — обратимся к документации PHP
Замечание:
Существует зарезервированные слова, которые нельзя использовать в качестве ключей в ini-файлах. Такими словами являются: null, yes, no, true, false, on, off, none. Значения null, no и false преобразуются в «», yes и true преобразуются в 1. Символыне должны использоваться в ключах и иметь какой-либо особый смысл в значениях.
Таким образом, получается, что такие слова как no, null и т.д. использовать нельзя. Что же делать, спросите Вы, если это необходимо? Для этого обратим внимание на первое замечание.
Замечание:
Если значение в ini-файле содержит прочие символы, кроме букв и цифр, оно должно заключаться в двойные кавычки («).
Т.е. наш файл нужно привести к вот такому виду:
[php] home = "home"
downloads = "downloads"
no_limits = "no limits"
link = "link"
…
[/php] И все будет работать как надо!
В данной статье я расскажу как написать свой парсер ini-файлов на Haskell. За основу возьму контекстно-свободную грамматику, построенную в моей предыдущей статье. Для построения парсера будет использоваться библиотека Parsec, которая позволяет строить свои собственные парсеры комбинируя готовые примитивные парсеры при помощи парсерных комбинаторов.
Важно: в данной статье предполагается, что читатель знаком с основами Haskell. Если это не так, то я советую сначала прочитать пару статей для новичков (их можно найти в том числе и на Хабре).
Примеры
Константы (но не "магические константы" вроде __FILE__ ) также могут обрабатываться в ini-файлах, так что если вы объявите константу в виде значения для ini-файла до вызова parse_ini_file() , то константа будет корректно обработана. Только значения опций будут обрабатываться и значение должно быть просто константой. Например:
define ( 'BIRD' , 'Птица додо' );
// Обрабатываем без секций
$ini_array = parse_ini_file ( "sample.ini" );
print_r ( $ini_array );
// Обрабатываем с секциями
$ini_array = parse_ini_file ( "sample.ini" , true );
print_r ( $ini_array );
Результатом выполнения данного примера будет что-то подобное:
// Простая функция для сравнения результатов
function yesno ( $expression )
return( $expression ? 'Да' : 'Нет' );
>
// Получаем путь к php.ini с помощью функции php_ini_loaded_file()
$ini_path = php_ini_loaded_file ();
// Обрабатываем php.ini
$ini = parse_ini_file ( $ini_path );
// Выводим и сравниваем значения, учтите, что использование get_cfg_var()
// даст одинаковые результаты для используемых здесь значений parsed (загруженное из файла) и loaded (используемое в данный момент)
echo '(parsed) magic_quotes_gpc = ' . yesno ( $ini [ 'magic_quotes_gpc' ]) . PHP_EOL ;
echo '(loaded) magic_quotes_gpc = ' . yesno ( get_cfg_var ( 'magic_quotes_gpc' )) . PHP_EOL ;
?>
Результатом выполнения данного примера будет что-то подобное:
Помимо оценки констант, некоторые символы имеют особое значение в значении ini-файлах. Кроме того, переменные среды и ранее определённые параметры конфигурации (смотрите get_cfg_var() ) могут быть прочитаны с использованием синтаксиса $<> .
Некоторые символы имеют особое значение в строках с двойными кавычками и должны быть экранированы префиксом обратной косой черты. Прежде всего, это двойная кавычка " в качестве маркера границы и сама обратная косая черта \ (если за ней следует один из специальных символов):
Для путей, подобных Windows, сделано исключение: можно не экранировать обратную косую черту в конце, если за строкой в кавычках следует разрыв строки:
Если нужно экранировать двойные кавычки, за которыми следует перенос строки в многострочном значении, можно использовать конкатенацию значений следующим образом (за одной строкой в двойных кавычках непосредственно следует другая):
Другой символ со специальным значением - это $ (знак доллара). Он должен быть экранирован, если за ним следует открытая фигурная скобка:
Экранирующие символы не поддерживаются в режиме INI_SCANNER_RAW (в этом режиме все символы обрабатываются "как есть").
Обратите внимание, что синтаксический анализатор ini не поддерживает стандартные последовательности экранирования ( \n , \t и т.д.). При необходимости выполните постобработку результата parse_ini_file() с помощью функции stripcslashes() .
Грамматика
Examples
Constants (but not "magic constants" like __FILE__ ) may also be parsed in the ini file so if you define a constant as an ini value before running parse_ini_file() , it will be integrated into the results. Only ini values are evaluated, and the value must be just the constant. For example:
define ( 'BIRD' , 'Dodo bird' );
// Parse without sections
$ini_array = parse_ini_file ( "sample.ini" );
print_r ( $ini_array );
// Parse with sections
$ini_array = parse_ini_file ( "sample.ini" , true );
print_r ( $ini_array );
The above example will output something similar to:
// A simple function used for comparing the results below
function yesno ( $expression )
return( $expression ? 'Yes' : 'No' );
>
// Get the path to php.ini using the php_ini_loaded_file() function
$ini_path = php_ini_loaded_file ();
// Parse php.ini
$ini = parse_ini_file ( $ini_path );
// Print and compare the values, note that using get_cfg_var()
// will give the same results for parsed and loaded here
echo '(parsed) magic_quotes_gpc = ' . yesno ( $ini [ 'magic_quotes_gpc' ]) . PHP_EOL ;
echo '(loaded) magic_quotes_gpc = ' . yesno ( get_cfg_var ( 'magic_quotes_gpc' )) . PHP_EOL ;
?>
The above example will output something similar to:
In addition to evaluating constants, certain characters have special meaning in an ini value. Additionally, environment variables and previously defined configuration options (see get_cfg_var() ) may be read using $<> syntax.
Some characters have special meaning in double-quoted strings and must be escaped by the backslash prefix. First of all, these are the double quote " as the boundary marker, and the backslash \ itself (if followed by one of the special characters):
There is an exception made for Windows-like paths: it's possible to not escape trailing backslash if the quoted string is directly followed by a linebreak:
If one does need to escape double quote followed by linebreak in a multiline value, it's possible to use value concatenation in the following way (there is one double-quoted string directly followed by another one):
Another character with special meaning is $ (the dollar sign). It must be escaped if followed by the open curly brace:
Escaping characters is not supported in the INI_SCANNER_RAW mode (in this mode all characters are processed "as is").
Note that the ini parser doesn't support standard escape sequences ( \n , \t , etc.). If necessary, post-process the result of parse_ini_file() with stripcslashes() function.
See Also
Notes
Note:
This function has nothing to do with the php.ini file. It is already processed by the time you run your script. This function can be used to read in your own application's configuration files.
Note:
If a value in the ini file contains any non-alphanumeric characters it needs to be enclosed in double-quotes (").
Note: There are reserved words which must not be used as keys for ini files. These include: null , yes , no , true , false , on , off , none . Values null , off , no and false result in "" , and values on , yes and true result in "1" , unless INI_SCANNER_TYPED mode is used. Characters ?<>|&~!()^" must not be used anywhere in the key and have a special meaning in the value.
Note:
Entries without an equal sign are ignored. For example, "foo" is ignored whereas "bar =" is parsed and added with an empty value. For example, MySQL has a "no-auto-rehash" setting in my.cnf that does not take a value, so it is ignored.
Note:
ini files are generally treated as plain text by web servers and thus served to browsers if requested. That means for security you must either keep your ini files outside of your docroot or reconfigure your web server to not serve them. Failure to do either of those may introduce a security risk.
Возвращаемые значения
В случае успешного выполнения, настройки возвращаются в виде ассоциативного массива ( array ). В случае возникновения ошибки возвращается false .
Список параметров
Имя обрабатываемого ini-файла. Если используется относительный путь, он оценивается относительно текущего рабочего каталога, а затем include_path.
Установив параметр process_sections в true , вы получаете многомерный массив, который включает как название отдельных настроек, так и секции. По умолчанию process_sections равен false
Может принимать следующие значения: INI_SCANNER_NORMAL (по умолчанию) или INI_SCANNER_RAW . Если указано значение INI_SCANNER_RAW , то значения опций не будут обрабатываться.
С версии PHP 5.6.1 также можно задать INI_SCANNER_TYPED . В этом режиме типы boolean, null и integer будут, по возможности, сохраняться. Строковые значения "true" , "on" и "yes" будут преобразованы в true . "false" , "off" , "no" и "none" в false . "null" преобразуется в null . Кроме этого, все числовые строки будут, по возможности, преобразованы к целым числам.
User Contributed Notes 15 notes
Here is a quick parse_ini_file wrapper to add extend support to save typing and redundancy.
/**
* 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:
/*
[base]
host=localhost
user=testuser
pass=testpass
database=default
[archive : base]
database=archive
*/
?>
As if it were like this:
/*
[base]
host=localhost
user=testuser
pass=testpass
database=default
You may want, in some very special cases, to parse multi-dimensional array with N levels in your ini file. Something like setting[data][config][debug] = true will result in an error (expected " default"> function parse_ini_file_multi ( $file , $process_sections = false , $scanner_mode = INI_SCANNER_NORMAL ) $explode_str = '.' ;
$escape_char = "'" ;
// load ini file the normal way
$data = parse_ini_file ( $file , $process_sections , $scanner_mode );
if (! $process_sections ) $data = array( $data );
>
foreach ( $data as $section_key => $section ) // loop inside the section
foreach ( $section as $key => $value ) if ( strpos ( $key , $explode_str )) if ( substr ( $key , 0 , 1 ) !== $escape_char ) // key has a dot. Explode on it, then parse each subkeys
// and set value at the right place thanks to references
$sub_keys = explode ( $explode_str , $key );
$subs =& $data [ $section_key ];
foreach ( $sub_keys as $sub_key ) if (!isset( $subs [ $sub_key ])) $subs [ $sub_key ] = [];
>
$subs =& $subs [ $sub_key ];
>
// set the value at the right place
$subs = $value ;
// unset the dotted key, we don't need it anymore
unset( $data [ $section_key ][ $key ]);
>
// we have escaped the key, so we keep dots as they are
else $new_key = trim ( $key , $escape_char );
$data [ $section_key ][ $new_key ] = $value ;
unset( $data [ $section_key ][ $key ]);
>
>
>
>
if (! $process_sections ) $data = $data [ 0 ];
>
return $data ;
>
?>
The following file:
/*
[normal]
foo = bar
; use quotes to keep your key as it is
'foo.with.dots' = true
[dictionary]
foo[debug] = false
foo[path] = /some/path
[multi]
foo.data.config.debug = true
foo.data.password = 123456
*/
?>
will result in:
parse_ini_file_multi ( 'file.ini' , true );
Array
(
[ normal ] => Array
(
[ foo ] => bar
[ foo . with . dots ] => 1
)
[array] => Array
(
[ foo ] => Array
(
[ 0 ] => 1
[ 1 ] => 2
)
)
[ dictionary ] => Array
(
[ foo ] => Array
(
[ debug ] =>
[ path ] => / some / path
)
)
[ multi ] => Array
(
[ foo ] => Array
(
[ data ] => Array
(
[ config ] => Array
(
[ debug ] => 1
)
[ password ] => 123456
)
)
)
)
?>
.ini files or JSON file format as it is also known as, are very useful format to store stuff in. Especially large arrays.
Strangely enough there is this nice function to read the file, but no function to write it.
Use it as: put_ini_file(string $file, array $array)
function put_ini_file ( $file , $array , $i = 0 ) $str = "" ;
foreach ( $array as $k => $v ) if ( is_array ( $v )) $str .= str_repeat ( " " , $i * 2 ). "[ $k ]" . PHP_EOL ;
$str .= put_ini_file ( "" , $v , $i + 1 );
>else
$str .= str_repeat ( " " , $i * 2 ). " $k = $v " . PHP_EOL ;
>
if( $file )
return file_put_contents ( $file , $str );
else
return $str ;
>
?>
This is a simple (but slightly hackish) way of avoiding the character limitations (in values):
define ( 'QUOTE' , '"' );
$test = parse_ini_file ( 'test.ini' );
echo "" ;
print_r ( $test );
?>
contents of test.ini:
park yesterday = "I (walked) | " QUOTE"the"QUOTE " park yesterday & saw ~three~ dogs!"
Array
(
[ park yesterday ] => I ( walked ) | < to >"the" park yesterday & saw ~ three ~ dogs !
)
?>
The documentation states:
Characters ?<>|&~!()^" must not be used anywhere in the key and have a special meaning in the value.
Here's the results of my experiments on what they mean:
; | is used for bitwise OR
three = 2|3
; & is used for bitwise AND
four = 6&5
; ^ is used for bitwise XOR
five = 3^6
; ~ is used for bitwise negate
negative_two = ~1
; () is used for grouping
seven = (8|7)&(6|5)
; $ <. >is used for grabbing values from the environment, or previously defined values.
path = $
also = $
; ? I have no guess for
; ! I have no guess for
This function for save ini files
function array_to_ini ( $array , $out = "" )
<
$t = "" ;
$q = false ;
foreach( $array as $c => $d )
<
if( is_array ( $d )) $t .= array_to_ini ( $d , $c );
else
<
if( $c === intval ( $c ))
<
if(!empty( $out ))
<
$t .= "\r\n" . $out . " = \"" . $d . "\"" ;
if( $q != 2 ) $q = true ;
>
else $t .= "\r\n" . $d ;
>
else
<
$t .= "\r\n" . $c . " = \"" . $d . "\"" ;
$q = 2 ;
>
>
>
if( $q != true && !empty( $out )) return "[" . $out . "]\r\n" . $t ;
if(!empty( $out )) return $t ;
return trim ( $t );
>
function save_ini_file ( $array , $file )
<
$a = array_to_ini ( $array );
$ffl = fopen ( $file , "w" );
fwrite ( $ffl , $a );
fclose ( $ffl );
>
?>
Looks like in PHP 5.3.0 special characters like \n are extrapolated into real newlines. Gotta use \\n.
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 " TmlhZ2FyYSBGYWxscywgT04 note" >
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).
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:
$stores = parse_ini ( 'stores.ini' );
print_r ( $stores );
?>
An example ini file:
/*
;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.
*/
?>
Not mentioned in the documentation, this function acts like include:
"Files are included based on the file path given or, if none is given, the include_path specified. If the file isn't found in the include_path, include will finally check in the calling script's own directory and the current working directory before failing."
(At least for PHP 7; have not checked PHP 5.)
This core function won't handle ini key[][] = value(s), (multidimensional arrays), so if you need to support that kind of setup you will need to write your own function. one way to do it is to convert all the key = value(s) to array string Ошибка парсинга ini файла что это[][]=value(s), then use parse_str() to convert all those Ошибка парсинга ini файла что это[][]=value(s) that way you just read the ini file line by line, instead of doing crazy foreach() loops to handle those (multidimensional arrays) in each section, example.
ini file. config.php
; This is a sample configuration file
; Comments start with ';' , as in php . ini
[ first_section ]
one = 1
five = 5
animal = BIRD
[ third_section ]
phpversion [] = "5.0"
phpversion [] = "5.1"
phpversion [] = "5.2"
phpversion [] = "5.3"
a [][][] = b
a [][][][] = c
a [ test_test ][][] = d
test [ one ][ two ][ three ] = true
?>
echo parse_ini_file ( "C:\\services\\www\\docs\\config.php" );
// PHP Warning: syntax error, unexpected TC_SECTION, expecting '=' line 27 -> a[][][] = b
Here it simple function that handles (multidimensional arrays) without looping each key[][]= value(s)
function getIni ( $file , $sections = FALSE )
$return = array ();
$config = fopen ( $file , 'r' );
while ( ! feof ( $config ) )
<
$line = trim ( fgets ( $config , 1024 ) );
$line = ( $line == '' ) ? ' ' : $line ;
if ( $sections )
$header = 'config[' . trim ( substr ( $line , 1 , - 1 ) ) . ']' ;
>
else
$header = 'config' ;
>
$kv = array_map ( 'trim' , explode ( '=' , $line ) );
$kv [ 0 ] = str_replace ( ' ' , '+' , $kv [ 0 ] );
$kv [ 1 ] = str_replace ( ' ' , '+' , $kv [ 1 ] );
if ( ( $pos = strpos ( $kv [ 0 ], '[' ) ) !== FALSE )
$kv [ 0 ] = '[' . substr ( $kv [ 0 ], 0 , $pos ) . ']' . substr ( $kv [ 0 ], $pos );
>
else
$kv [ 0 ] = '[' . $kv [ 0 ] . ']' ;
>
$bt = strtolower ( $kv [ 1 ] );
if ( in_array ( $bt , array ( 'true' , 'false' , 'on' , 'off' ) ) )
$kv [ 1 ] = ( $bt == 'true' || $bt == 'on' ) ? TRUE : FALSE ;
>
$keeper [] = $header . $kv [ 0 ] . '=' . $kv [ 1 ];
>
>
return $return [ 'config' ];
>
print_r ( $config -> getIni ( "C:\\services\\www\\docs\\config.php" ), $sections );
День добрый. проблема состоит в том чтобы написать функции для чтения и записи ини файла одной программы, но в ключах содержатся "()" так что parse_ini_file не подходит, нашел функцию которая парсит файл:
помогите с остальными функциями для чтения и записи ини файла ну чтобы примерно синтаксис использования был таков:
Цитата (T1grOK @ 10.04.2011 - 07:14) |
Версия php какая? Потому как в 5.3.1, есть специальный механизм для работы с ini файлами. |
parse_ini_file отлично подходит для работы с ini файлами.
чтение производится через ключи получившегося массива, запись этой функцией:
Цитата (Raito-kun @ 10.04.2011 - 14:30) |
можно поподробней насчет |
как я писал выше в конфиг файле в ключах присутствую "()" скобки parse_ini_file, к сожалению, не воспринимает этого из-за чего и получается ошибка при чтении и записи файла, а изменить этот ключ я не могу так как он используется не только моим приложением.
я не понимаю, специально протестил сейчас запись и чтение такого конфига:
все отлично считалось и записалось.
кинь кусок в котором по твоему мнению возникает ошибка, переименуй, я попробую.
Цитата (Raito-kun @ 10.04.2011 - 14:55) |
я не понимаю, специально протестил сейчас запись и чтение такого конфига: |
ты считывал функцией parse_ini_file? а какая версия php у тебя?
вот кусок файла на котором происходит ошибка чтения
да. версия 5.2.10. да тут не в версии дело, если у тебя есть поддержка этой функции.
у тебя секции в ini-файле присутствуют? не забываешь ставить второй аргумент true?
Цитата (Raito-kun @ 10.04.2011 - 15:06) |
да. версия 5.2.10. да тут не в версии дело, если у тебя есть поддержка этой функции. у тебя секции в ini-файле присутствуют? не забываешь ставить второй аргумент true? |
просто если удаляю этот параметр в котором ощибка файл нормально читается и записывается. покажи плиз полность функции которыми ты пользуешься для чтения и для записи.
Нет дело не в пробеле, только что специально проверил, убрал скобки , пробел оставил. все нормально. ставлю скобки появляется ошибка
Цитата (Raito-kun @ 10.04.2011 - 15:07) |
нет, все-таки у меня проблем не возникло |
действительно варнинг возникает, но не мешает работе. почему - не знаю. поставь
пока не найдем причину. или проигнорь, если проблем не возникает.
Цитата (Raito-kun @ 10.04.2011 - 15:22) |
действительно варнинг возникает, но не мешает работе. почему - не знаю. поставь пока не найдем причину. или проигнорь, если проблем не возникает. |
в общем записывает функция верно. не считывает и ругается правильно, т.к. скобочек действительно не должно быть. надо думать.
варнинг убирается собачкой перед вызовом функции, другое дело что ты не сможешь считать информацию.
Цитата (Raito-kun @ 10.04.2011 - 15:44) |
в общем записывает функция верно. не считывает и ругается правильно, т.к. скобочек действительно не должно быть. надо думать. |
Цитата (Raito-kun @ 10.04.2011 - 15:44) |
варнинг убирается собачкой перед вызовом функции, другое дело что ты не сможешь считать информацию. |
Воспользовался функцией из 1-го топика для открытия, все прошло чисто без ошибок и чтение и запись и перезапись! вроде пока что полет нормальный. )))
Спасибо тебе большое за помощь)) искренне благодарен.
теперь еще остается вопрос как считать конкретый ключ из нужной секции и поместить его значение в переменную)))
Ваше спасибо тебе. Эт просто для меня неоценимо)))
Цитата (Raito-kun @ 10.04.2011 - 17:26) |
ну для этого нужно пройти по всем ключам и заменить на свой нужный: |
Вот файл который нужно редактировать, это файл программы TFTPD32:
Return Values
The settings are returned as an associative array on success, and false on failure.
Список параметров
Имя обрабатываемого ini-файла. Если используется относительный путь, он оценивается относительно текущего рабочего каталога, а затем include_path.
Установив параметр process_sections в true , вы получаете многомерный массив, который включает как название отдельных настроек, так и секции. По умолчанию process_sections равен false
Может принимать следующие значения: INI_SCANNER_NORMAL (по умолчанию) или INI_SCANNER_RAW . Если указано значение INI_SCANNER_RAW , то значения опций не будут обрабатываться.
С версии PHP 5.6.1 также можно задать INI_SCANNER_TYPED . В этом режиме типы boolean, null и integer будут, по возможности, сохраняться. Строковые значения "true" , "on" и "yes" будут преобразованы в true . "false" , "off" , "no" и "none" в false . "null" преобразуется в null . Кроме этого, все числовые строки будут, по возможности, преобразованы к целым числам.
Примечания
Замечание:
Эта функция не имеет никакого отношения к файлу php.ini . К моменту выполнения вашего скрипта, он уже обработан. Эта функция может быть использована для загрузки настроек вашего собственного приложения.
Замечание:
Если значение в ini-файле содержит прочие символы, кроме букв и цифр, оно должно заключаться в двойные кавычки (").
Замечание: Существует зарезервированные слова, которые нельзя использовать в качестве ключей в ini-файлах. Такими словами являются: null , yes , no , true , false , on , off , none . Значения null , off , no и false преобразуются в "" , а значения on , yes и true в "1" , но только если не используется режим INI_SCANNER_TYPED . Символы ?<>|&~!()^" не должны использоваться в ключах и иметь какой-либо особый смысл в значениях.
Замечание:
Записи без знака равенства игнорируются. Например, "foo" игнорируется, тогда как "bar =" обрабатывается и добавляется с пустым значением. Например, в MySQL есть опция "no-auto-rehash", устанавливаемая в my.cnf , которая не имеет значения и игнорируется.
Замечание:
ini-файлы обычно обрабатываются веб-серверами как простой текст и, таким образом, по запросу передаются браузерам. Это означает, что в целях безопасности вы должны либо хранить свои ini-файлы вне корневого каталога документов, либо перенастроить веб-сервер, чтобы они не обслуживались. Невыполнение любого из этих требований может создать угрозу безопасности.
Смотрите также
Читайте также: