PHP Conference Japan 2024

parse_ini_file

(PHP 4, PHP 5, PHP 7, PHP 8)

parse_ini_file解析設定檔

說明

parse_ini_file(字串 $filename, 布林值 $process_sections = false, 整數 $scanner_mode = INI_SCANNER_NORMAL): 陣列|false

parse_ini_file() 函式會載入指定於 filename 參數的 ini 檔案,並以關聯式陣列的形式返回其中的設定。

ini 檔案的結構與 php.ini 相同。

參數

filename

要解析的 ini 檔案檔名。如果使用相對路徑,則會相對於目前工作目錄,然後是 include_path 來評估。

process_sections

process_sections 參數設定為 true,您將獲得一個多維陣列,其中包含區段名稱和設定。 process_sections 的預設值為 false

scanner_mode

可以是 INI_SCANNER_NORMAL(預設)或 INI_SCANNER_RAW。如果提供 INI_SCANNER_RAW,則選項值將不會被解析。

從 PHP 5.6.1 開始,也可以指定為 INI_SCANNER_TYPED。在此模式下,布林值、null 和整數類型會盡可能保留。字串值 "true""on""yes" 會轉換為 true"false""off""no""none" 會被視為 false"null" 在類型化模式下會轉換為 null。此外,所有數值字串都會盡可能轉換為整數類型。

返回值

成功時,設定會以關聯式 陣列 返回,失敗時則返回 false

範例

範例 #1 sample.ini 的內容

; This is a sample configuration file
; Comments start with ';', as in php.ini

[first_section]
one = 1
five = 5
animal = BIRD

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

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

urls[svn] = "http://svn.php.net"
urls[git] = "http://git.php.net"

範例 #2 parse_ini_file() 範例

常數(但不包含像 __FILE__ 這樣的「魔術常數」)也可以在 ini 檔案中解析,因此如果您在執行 parse_ini_file() 之前將常數定義為 ini 值,它將會整合到結果中。只有 ini 值會被評估,而且該值必須僅為常數。例如

<?php

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

// 不含區段的解析
$ini_array = parse_ini_file("sample.ini");
print_r($ini_array);

// 含區段的解析
$ini_array = parse_ini_file("sample.ini", true);
print_r($ini_array);

?>

以上範例將輸出類似以下的內容:

Array
(
    [one] => 1
    [five] => 5
    [animal] => Dodo bird
    [path] => /usr/local/bin
    [URL] => http://www.example.com/~username
    [phpversion] => Array
        (
            [0] => 5.0
            [1] => 5.1
            [2] => 5.2
            [3] => 5.3
        )

    [urls] => Array
        (
            [svn] => http://svn.php.net
            [git] => http://git.php.net
        )

)
Array
(
    [first_section] => Array
        (
            [one] => 1
            [five] => 5
            [animal] => Dodo bird
        )

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

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

            [urls] => Array
                (
                    [svn] => http://svn.php.net
                    [git] => http://git.php.net
                )

        )

)

範例 #3 使用 parse_ini_file() 解析 php.ini 檔案

<?php
// 一個用於比較下方結果的簡單函式
function yesno($expression)
{
return(
$expression ? 'Yes' : 'No');
}

// 使用 php_ini_loaded_file() 函式取得 php.ini 的路徑
$ini_path = php_ini_loaded_file();

// 解析 php.ini
$ini = parse_ini_file($ini_path);

// 顯示並比較數值,請注意使用 get_cfg_var()
// 將在此處提供已解析和已載入的相同結果
echo '(已解析) magic_quotes_gpc = ' . yesno($ini['magic_quotes_gpc']) . PHP_EOL;
echo
'(已載入) magic_quotes_gpc = ' . yesno(get_cfg_var('magic_quotes_gpc')) . PHP_EOL;
?>

以上範例將輸出類似以下的內容:

(parsed) magic_quotes_gpc = Yes
(loaded) magic_quotes_gpc = Yes

範例 #4 值內插

除了評估常數之外,某些字元在 ini 值中具有特殊含義。此外,可以使用 ${} 語法讀取環境變數和先前定義的配置選項(請參閱 get_cfg_var())。

; | 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)

; Interpolate the PATH environment variable
path = ${PATH}

; Interpolate the configuration option 'memory_limit'
configured_memory_limit = ${memory_limit}

範例 #5 跳脫字元

某些字元在雙引號字串中具有特殊含義,必須使用反斜線前綴進行跳脫。首先,這些是雙引號 " 作為邊界標記,以及反斜線 \ 本身(如果後跟其中一個特殊字元)。

quoted = "She said \"Exactly my point\"." ; Results in a string with quote marks in it.
hint = "Use \\\" to escape double quote" ; Results in: Use \" to escape double quote

Windows 類型的路徑有一個例外:如果引號字串後直接跟隨換行符,則可以不跳脫尾隨反斜線。

save_path = "C:\Temp\"

如果確實需要在多行值中跳脫後跟換行符的雙引號,則可以使用以下方式進行值串接(一個雙引號字串後直接跟隨另一個雙引號字串)。

long_text = "Lorem \"ipsum\"""
 dolor" ; Results in: Lorem "ipsum"\n dolor

另一個具有特殊含義的字元是 $(美元符號)。如果後跟左大括號,則必須跳脫。

code = "\${test}"

INI_SCANNER_RAW 模式下不支援跳脫字元(在此模式下,所有字元都「按原樣」處理)。

請注意,ini 解析器不支援標準跳脫序列(\n\t 等)。如有必要,請使用 stripcslashes() 函式後處理 parse_ini_file() 的結果。

注意事項

注意:

此函式與 php.ini 檔案無關。它在您執行腳本時已被處理。此函式可用於讀取您自己的應用程式設定檔。

注意:

如果 ini 檔案中的值包含任何非字母數字字元,則需要用雙引號 (") 括起來。

注意有一些保留字不能用作 ini 檔案的鍵名。這些包括:nullyesnotruefalseonoffnone。值 nulloffnofalse 的結果為 "",而值 onyestrue 的結果為 "1",除非使用了INI_SCANNER_TYPED 模式。字元 ?{}|&~!()^" 不能在鍵名中的任何位置使用,並且在值中具有特殊含義。

注意:

沒有等號的項目將被忽略。例如,"foo" 會被忽略,而 "bar =" 會被解析並添加一個空值。例如,MySQL 在 my.cnf 中有一個 "no-auto-rehash" 設定,它不需要值,因此會被忽略。

注意:

ini 檔案通常被網路伺服器視為純文字,因此如果瀏覽器請求,就會提供給瀏覽器。這意味著為了安全起見,您必須將 ini 檔案放在您的文件根目錄之外,或者重新配置您的網路伺服器,使其不提供這些檔案。如果不執行這兩項操作中的任何一項,都可能造成安全風險。

另請參閱

新增註解

使用者貢獻的註解 13 則註解

jeremygiberson at gmail dot com
15 年前
這是一個快速的 parse_ini_file 包裝函式,用於新增擴充支援以節省輸入和減少冗餘。
<?php
/**
* 解析 INI 檔案,透過命名空間上的 ":base" 後綴添加繼承功能。
*
* @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);
// 必要時建立命名空間
if(!isset($config[$name])) $config[$name] = array();
// 繼承基礎命名空間
if(isset($p_ini[$extends])){
foreach(
$p_ini[$extends] as $prop => $val)
$config[$name][$prop] = $val;
}
// 覆寫/設定目前命名空間的值
foreach($properties as $prop => $val)
$config[$name][$prop] = $val;
}
return
$config;
}
?>

處理這個 ini 檔案
<?php
/*
[base]
host=localhost
user=testuser
pass=testpass
database=default

[users:base]
database=users

[archive : base]
database=archive
*/
?>
如同它是像這樣
<?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
*/
?>
Rekam
10 年前
在某些非常特殊的情況下,您可能想要在 ini 檔案中解析 N 層的多維陣列。類似 setting[data][config][debug] = true 的設定將會導致錯誤(預期出現 "=")。

這裡有一個小函式可以用來處理這個問題,使用點號(可自訂)。
<?php
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;
}
?>

以下檔案
<?php
/*
[normal]
foo = bar
; 使用引號以保持鍵值不變
'foo.with.dots' = true

[array]
foo[] = 1
foo[] = 2

[dictionary]
foo[debug] = false
foo[path] = /some/path

[multi]
foo.data.config.debug = true
foo.data.password = 123456
*/
?>

結果將會是
<?php
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
)
)
)
)
?>
dschnepper at box dot com
8 年前
文件說明如下:
字元 ?{}|&~!()^" 不得在鍵值中使用,且在值中具有特殊含義。

以下是我對它們含義進行實驗的結果

; | 用於位元 OR 運算
three = 2|3

; & 用於位元 AND 運算
four = 6&5

; ^ 用於位元 XOR 運算
five = 3^6

; ~ 用於位元反轉運算
negative_two = ~1

; () 用於群組運算
seven = (8|7)&(6|5)

; ${...} 用於從環境變數或先前定義的值中獲取值。
path = ${PATH}
also = ${five}

; ? 我不確定其用途
; ! 我不確定其用途
YAPs
8 年前
這個函式用於儲存 ini 檔案

<?php
函式 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);
}

函式
save_ini_file($array,$file)
{
$a=array_to_ini($array);
$ffl=fopen($file,"w");
fwrite($ffl,$a);
fclose($ffl);
}
?>
info () gaj ! design
7 年前
文件中未提及,此函式的作用類似 include (引入檔案)

檔案包含基於給定的檔案路徑,如果未給定路徑,則基於指定的 include_path。如果在 include_path 中找不到檔案,include 最終會在呼叫腳本自身的目錄和目前工作目錄中檢查,然後才會回報失敗。

(至少在 PHP 7 中是如此;尚未檢查 PHP 5。)
kieran dot huggins at rogers dot com
21 年前
給所有在跳脫雙引號時遇到問題的人的快速提醒

我的解決方法是在寫入 ini 檔案時使用 "base64_encode()" 編碼內容,讀取時則用 "base64_decode()" 解碼。

因為 base64 使用 "=" 符號,您必須將整個值用雙引號括起來,使該行看起來像這樣

varname = "TmlhZ2FyYSBGYWxscywgT04="

經過 base64 編碼後,您的字串將保留所有 \n、\t...等等... URL 也能完美保留所有內容 :-)

希望你們覺得這很有用!

Cheers, Kieran
Justin Hall
18 年前
這是一種避免字元限制(在值中)的簡單(但稍微有點 hack)的方法

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

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

test.ini 的內容

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

輸出

<?php
Array
(
[
park yesterday] => I (walked) | {to} "the" park yesterday & saw ~three~ dogs!
)
?>
goulven.ch AT gmail DOT com
17 年前
警告:parse_ini_files 無法處理包含等號 (=) 的值。

以下函式支援區段、註解、陣列和任何區段外的鍵值對。
請注意,類似的鍵會互相覆蓋(除非在不同的區段中)。

<?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;
}
?>

用法範例
<?php
$stores
= parse_ini('stores.ini');
print_r( $stores );
?>

ini 檔案範例
<?php
/*
;註解行以 ';' 開頭
global_value1 = 字串值
global_value1 = 另一個字串值

; 空白行會被忽略
[Section1]
key = value
; 鍵和值周圍的空白也會被忽略
otherkey=other value
otherkey=yet another value
; 此鍵值對將覆蓋前者。
*/
?>
simon dot riget at gmail dot com
11 年前
.ini 檔案格式,或如同眾所周知的 JSON 檔案格式,是一種非常實用的資料儲存格式,尤其適用於大型陣列。

很奇怪的是,PHP 提供了一個讀取 .ini 檔案的方便函式,卻沒有提供寫入的函式。

所以,這裡提供一個寫入 .ini 檔案的函式。

使用方法:put_ini_file(string $file, array $array)

<?php
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;
}
?>
theking2(at)king.ma
9 個月前
為了讓設定值在應用程式的每個角落都可使用,我使用了一個使用者自訂常數 $SETTINGS。它的初始化方式如下:
<?php

define
( 'SETTINGS', parse_ini_file('settings.ini', true) );

?>
有了正確的 settings.ini 檔案,您現在可以執行以下操作:
<?php
$db = new \PDO(
"mysql:host={SETTINGS['
db']['host']};dbname={SETTINGS['db']['name']};charset=utf8",
SETTINGS['
db']['user'],
SETTINGS['
db']['pass'], [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
]
);
?>

請務必在網站上隱藏您的 settings.ini 檔案,例如使用以下方式:
<?php
<FilesMatch "\.(?:ini|htaccess)$">
Order allow,deny
Deny from all
</FilesMatch>
?>
eciruam35 at gmail dot com
1 年前
修正 put_ini_file 函式中的一個小錯誤 (此處)

function put_ini_file($config, $file, $has_section = false, $write_to_file = true){
$fileContent = '';
如果 (!empty($config)) {
foreach ($config as $i => $v) {
如果 ($has_section) {
$fileContent .= "\n[$i]".PHP_EOL.put_ini_file($v, $file, false, false);
}
} else {
如果 (is_array($v)) {
foreach ($v as $t => $m) {
//--->>> 這裡 $fileContent .= "-->$i[$t] = ".(is_numeric($m) ? $m : '"'.$m.'"').PHP_EOL;
$fileContent .= "$i"."[] = ".(is_numeric($m) ? $m : '"'.$m.'"').PHP_EOL;
}
}
} else $fileContent .= "$i = ".(is_numeric($v) ? $v : '"'.$v.'"').PHP_EOL;
}
}
}

如果 ($write_to_file && strlen($fileContent)) return file_put_contents($file, $fileContent, LOCK_EX);
否則 return $fileContent;
}
manngo
1 年前
保護你的 .ini 檔案安全

「網頁伺服器通常將 ini 檔案視為純文字,因此如果瀏覽器請求,就會將其提供給瀏覽器。這表示為了安全起見,您必須將 ini 檔案放在網站根目錄之外,或重新設定您的網頁伺服器,使其不提供這些檔案。若未能執行上述任一項操作,都可能會造成安全風險。」

或者,您可以將檔案儲存為

stuff.ini.php

並在開頭加上

;<?php die('go away'); ?>

開頭的分號會被視為註釋,因此這一行對 ini 檔案沒有影響。

由於檔案的副檔名為 .php,如果您嘗試直接存取此檔案,它會透過 PHP 直譯器執行,並且 php 區塊將會被處理並結束。

檔案副檔名對 parse_ini_file() 函式沒有負面影響,而 .ini 的部分當然是個人喜好問題。
jbricci at ya-right dot com
9 年前
這個核心函式無法處理 ini 檔案中的 key[][] = value(s) 格式(多維陣列),所以如果您需要支援這種設定,就需要自己編寫函式。一種方法是將所有 key = value(s) 轉換為陣列字串 [key][][]=value(s),然後使用 parse_str() 來轉換所有這些 [key][][]=value(s),這樣您只需逐行讀取 ini 檔案,而不必在每個區段中使用複雜的 foreach() 迴圈來處理這些(多維陣列),例如……

ini 檔案…… config.php

<?php

; 這是一個範例設定檔
; 註解以 ';' 開頭,如同 php.ini

[first_section]
one = 1
five
= 5
animal
= BIRD

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

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

urls[svn] = "http://svn.php.net"
urls[git] = "http://git.php.net"

[fourth_section]

a[][][] = b
a
[][][][] = c
a
[test_test][][] = d
test
[one][two][three] = true

?>

echo parse_ini_file ( "C:\\services\\www\\docs\\config.php" );

結果如下...

// PHP 警告:語法錯誤,第 27 行出現非預期的 TC_SECTION,應為 '=' -> a[][][] = b

這裡有一個簡單的函式,可以處理(多維陣列)而無需迴圈處理每個 key[][]= value(s)

<?php

function getIni ( $file, $sections = FALSE )
{
$return = array ();

$keeper = array ();

$config = fopen ( $file, 'r' );

while ( !
feof ( $config ) )
{
$line = trim ( fgets ( $config, 1024 ) );

$line = ( $line == '' ) ? ' ' : $line;

switch (
$line{0} )
{
case
' ':
case
'#':
case
'/':
case
';':
case
'<':
case
'?':

break;

case
'[':

if (
$sections )
{
$header = 'config[' . trim ( substr ( $line, 1, -1 ) ) . ']';
}
else
{
$header = 'config';
}

break;

default:

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

fclose ( $config );

parse_str ( implode ( '&', $keeper ), $return );

return
$return['config'];
}

// usage...

$sections = TRUE;

print_r ( $config->getIni ( "C:\\services\\www\\docs\\config.php" ), $sections );

?>
To Top