首頁 php教程 php手册 php中動態修改ini配置_php基礎

php中動態修改ini配置_php基礎

May 16, 2016 am 08:59 AM
ini php 配置

1,運行時改變配置
在前一篇文章中曾經談到,ini_set函數可以在php執行的過程中,動態修改php的部分配置。請注意,只是部分,並非所有的配置都可以動態修改。關於ini配置的可修改性,請參閱:http://php.net/manual/zh/configuration. changes.modes.php

我們直接進入ini_set的實現,函數雖然有點長,但是邏輯很清晰:

複製程式碼 程式碼如下:

PHP_FUNCTION(ini_set)
{
char *varname, *new_value;
int varname_len, new_value_len;
字元*舊值;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &varname, &varname_len, &new_value, &new_value_len) == 失敗) {
返回;
}

    // 去EG(ini_directives)中取得設定的值
old_value = zend_ini_string(varname, varname_len 1, 0);

    /* 複製返回此處,因為 alter 可能會釋放它! */
如果(舊值){
RETVAL_STRING(舊值, 1);
} 否則{
RETVAL_FALSE;
}

    // 如果開啟了安全模式,那麼以下這些ini配置可能涉及檔案操作,需要要輔助檢查uid
#define _CHECK_PATH(var, var_len, ini) php_ini_check_path(var, var_len, ini, sizeof(ini))
    /* safe_mode & basedir check */
    if (PG(safe_mode) || PG(open_basedir)) {
        if (_CHECK_PATH(varname, varname_len, "error_log") ||
            _CHECK_PATH(varname, varname_len, "java.class.path") ||
            _CHECK_PATH(varname, varname_len, "java.home") ||
            _CHECK_PATH(varname, varname_len, "mail.log") ||
            _CHECK_PATH(varname, varname_len, "java.library.path") ||
            _CHECK_PATH(varname, varname_len, "vpopmail.directory")) {
            if (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
                zval_dtor(return_value);
                RETURN_FALSE;
            }
            if (php_check_open_basedir(new_value TSRMLS_CC)) {
                zval_dtor(return_value);
                RETURN_FALSE;
            }
        }
    }

    // 在安全模式下,如下這些ini受到保護,不會被動態修改
    if (PG(safe_mode)) {
        if (!strncmp("max_execution_time", varname, sizeof("max_execution_time")) ||
            !strncmp("memory_limit", varname, sizeof("memory_limit")) ||
            !strncmp("child_terminate", varname, sizeof("child_terminate"))
        ) {
            zval_dtor(return_value);
            RETURN_FALSE;
        }
    }

    // 呼叫zend_alter_ini_entry_ex去動態修改ini設定
    if (zend_alter_ini_entry_ex(varname, varname_len 1, new_value, new_value_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == FAILURE) {
        zval_dtor(return_value);
        RETURN_FALSE;
    }
}

可以看到,除了一些必要的驗證工作,主要是呼叫zend_alter_ini_entry_ex。

我們繼續跟進到zend_alter_ini_entry_ex函數:

複製程式碼 程式碼如下:

ZEND_API int zend_alter_ini_entry_ex(char *name, uint name_length, char *new_value, uint new_value_length, int modify_type, int stage, int force_change TSRMLS_DC) /* {{{ */
{
    zend_ini_entry *ini_entry;
    char *duplicate;
    zend_bool modifiable;
    zend_bool modified;

    // 求出EG(ini_directives)對應的ini_entry
    if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == FAILURE) {
        return FAILURE;
    }

    // 是否被修改及可修改性
    modifiable = ini_entry->modifiable;
    modified = ini_entry->modified;

    if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
        ini_entry->modifiable = ZEND_INI_SYSTEM;
    }

    // 是否強制修改
    if (!force_change) {
        if (!(ini_entry->modifiable & modify_type)) {
            return FAILURE;
        }
    }

    // EG(modified_ini_directives)用來存放已修改過的ini_entry
    // 主要用做恢復
    if (!EG(modified_ini_directives)) {
        ALLOC_HASHTABLE(EG(modified_ini_directives));
        zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
    }
   
    // 將ini_entry中的值,值的長度,可修改範圍,保留到orig_xxx中去
    // 以便在請求結束的時候,可以對ini_entry做恢復
    if (!modified) {
        ini_entry->orig_value = ini_entry->value;
        ini_entry->orig_value_length = ini_entry->value_length;
        ini_entry->orig_modifiable = modifiable;
        ini_entry->modified = 1;
        zend_hash_add(EG(modified_ini_directives), name, name_length, &ini_entry, sizeof(zend_ini_entry*), NULL);
    }

    duplicate = estrndup(new_value, new_value_length);

    // 呼叫modify來更新XXX_G中對應的ini配置
    if (!ini_entry->on_modify || ini_entry->on_modify(ini_entry, duplicate, new_value_length, ini_entry-phpcnphpphpcnmh_argini, ini_value_length, ini_entry-php當== SUCCESS) {
        // 同上面,若有多次修改,則需要釋放前一次修改的數值
        if (modified && ini_entry->orig_value != ini_entry->value) {
            efree(ini_entry->value);
        }
        ini_entry->value = duplicate;
        ini_entry->value_length = new_value_length;
    } else {
        efree(duplicate);
        return FAILURE;
    }

    return SUCCESS;
}

有3處邏輯需要我們仔細體會:

1)ini_entry中的modified欄位用來表示該配置是否被動態修改過。一旦該ini配置發生修改,modified就會被置為1。上述程式碼中有一段很關鍵:

複製程式碼 程式碼如下:

// 如果多次呼叫ini_set,則orig_value等始終保持最原始的值
if (!modified) {
    ini_entry->orig_value = ini_entry->value;
    ini_entry->orig_value_length = ini_entry->value_length;
    ini_entry->orig_modifiable = modifiable;
    ini_entry->modified = 1;
    zend_hash_add(EG(modified_ini_directives), name, name_length, &ini_entry, sizeof(zend_ini_entry*), NULL);
}

這段程式碼表示,不管我們先後在php程式碼中呼叫幾次ini_set,只有第一次ini_set時才會進入這段邏輯,設定好orig_value。從第二次呼叫ini_set開始,便不會再執行這段分支,因為此時的modified已經被置為1了。因此,ini_entry->orig_value始終保存的是第一次修改之前的配置值(即最原始的配置)。

2)為了能讓ini_set修改的配置立即生效,需要on_modify回呼函數。

如前一篇所述,呼叫on_modify是為了能夠更新模組的全域變數。再次回憶下,首先,模組全域變數中的配置已經不是字串型了,該用bool用bool、該用int用int。其次,每一個ini_entry中都儲存了該模組全域變數的位址以及對應的偏移量,使得on_modify可以很迅速的進行記憶體修改。另外別忘記,on_modify調用完了之後,仍需進一步更新ini_entry->value,這樣EG(ini_directives)中的配置值就是最新的了。

3)這裡出現了一張新的hash表,EG(modified_ini_directives)。

EG(modified_ini_directives)只用於存放被動態修改過的ini配置,如果一個ini配置被動態修改過,那麼它既存在於EG(ini_directives)中,又存在於EG(modified_ini_directives)。既然每個ini_entry都有modified字段做標記,那豈不是可以遍歷EG(ini_directives)來獲得所有被修改過的配置呢?

答案是肯定的。個人覺得,這裡的EG(modified_ini_directives)主要還是為了提升性能,醬直接遍歷EG(modified_ini_directives)就足夠了。此外,將EG(modified_ini_directives)的初始化延後到zend_alter_ini_entry_ex中,也可以看出php在細節上的效能最佳化點。

2,恢復配置
ini_set的作用時間和php.ini檔案的作用時間是不一樣的,一旦請求執行結束,則ini_set會失效。此外,當我們程式碼中呼叫了ini_restore函數,則先前透過ini_set設定的配置也會失效。

每一個php請求執行完畢之後,會觸發php_request_shutdown,它和php_request_startup是兩個相對應過程。如果php是掛接在apache/nginx下,每處理完一個http請求,就會呼叫php_request_shutdown;如果php以CLI模式來執行,則腳本執行完畢之後,也會呼叫php_request_shutdown。

在php_request_shutdown中,我們可以看到針對ini的復原處理:

複製程式碼 程式碼如下:

/* 7. Shutdown scanner/executor/compiler and restore ini entries */
zend_deactivate(TSRMLS_C);

進入zend_deactivate,可以進一步看到呼叫了zend_ini_deactivate函數,由zend_ini_deactivate來負責將php的配置進行復原。

複製程式碼 程式碼如下:

zend_try {
    zend_ini_deactivate(TSRMLS_C);
} zend_end_try();

具體來看看zend_ini_deactivate的實作:

複製程式碼 程式碼如下:

ZEND_API int zend_ini_deactivate(TSRMLS_D) /* {{{ */
{
    if (EG(modified_ini_directives)) {
        // 遍歷EG(modified_ini_directives)中這張表
        // 對每一個ini_entry呼叫zend_restore_ini_entry_wrapper
        zend_hash_apply(EG(modified_ini_directives), (apply_func_t) zend_restore_ini_entry_wrapper TSRMLS_CC);
       
        // 回收作業
        zend_hash_destroy(EG(modified_ini_directives));
        FREE_HASHTABLE(EG(modified_ini_directives));
        EG(modified_ini_directives) = NULL;
    }
    return SUCCESS;
}

從zend_hash_apply來看,真正恢復ini的任務最終落地到了zend_restore_ini_entry_wrapper回呼函數。

複製程式碼 程式碼如下:

static int zend_restore_ini_entry_wrapper(zend_ini_entry **ini_entry TSRMLS_DC)
{
    // zend_restore_ini_entry_wrapper就是zend_restore_ini_entry_cb的封裝
    zend_restore_ini_entry_cb(*ini_entry, ZEND_INI_STAGE_DEACTIVATE TSRMLS_CC);
    return 1;
}

static int zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage TSRMLS_DC)
{
    int result = FAILURE;

    // 只看修改過的ini項
    if (ini_entry->modified) {
        if (ini_entry->on_modify) {
            // 使用orig_value,且對XXX_G內的相關欄位重新設定
            zend_try {
                result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry-php-phpd _arg2, ini_entry->mh_arg3, stage TSRMLS_CC);
            } zend_end_try();
        }
        if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
            /* runtime failure is OK */
            return 1;
        }
        if (ini_entry->value != ini_entry->orig_value) {
            efree(ini_entry->value);
        }
       
        // ini_entry本身恢復至最原始的數值
        ini_entry->value = ini_entry->orig_value;
        ini_entry->value_length = ini_entry->orig_value_length;
        ini_entry->modifiable = ini_entry->orig_modifiable;
        ini_entry->modified = 0;
        ini_entry->orig_value = NULL;
        ini_entry->orig_value_length = 0;
        ini_entry->orig_modifiable = 0;
    }
    return 0;
}

邏輯都蠻清晰的,相信讀者可以看懂。總結一下關於ini配置的復原流程:

複製程式碼 程式碼如下:

php_request_shutdown--->zend_deactivate--->zend_ini_deactivate--->zend_restore_ini_entry_wrapper--->zend_restore_ini_entry_cb

3,配置的銷毀
在sapi生命週期結束的時候,例如apache關閉,cli程式執行完畢等等。一旦進入到這個階段,之前所說的configuration_hash,EG(ini_directives)等都需要被銷毀,其用到的記憶體空間需要被釋放。

1,php會依序結束所有的模組,在每個模組的PHP_MSHUTDOWN_FUNCTION中呼叫UNREGISTER_INI_ENTRIES。 UNREGISTER_INI_ENTRIES和REGISTER_INI_ENTRIES對應,但UNREGISTER_INI_ENTRIES並不負責模組全域空間的釋放,XXX_globals這塊記憶體放在靜態資料區上,無須人為回收。

UNREGISTER_INI_ENTRIES主要做的事情,是將某個模組的ini_entry配置從EG(ini_directives)表中刪除。刪除之後,ini_entry本身的空間會被回收,但ini_entry->value不一定會被回收。

當所有模組的PHP_MSHUTDOWN_FUNCTION都呼叫UNREGISTER_INI_ENTRIES一遍之後,EG(ini_directives)中只剩下了Core模組的ini配置。此時,就需要手動呼叫UNREGISTER_INI_ENTRIES,來完成對Core模組配置的刪除工作。

複製程式碼 程式碼如下:

void php_module_shutdown(TSRMLS_D)
{
    ...
   
    // zend_shutdown會依序關閉除了Core之外的所有php模組
    // 關閉時會呼叫各模組的PHP_MSHUTDOWN_FUNCTION
    zend_shutdown(TSRMLS_C);
   
    ...

    // 至此,EG(ini_directives)只剩下了Core模組的設定
    // 這裡手動清理一下
    UNREGISTER_INI_ENTRIES();
   
    // 回收configuration_hash
    php_shutdown_config();

    // 回收EG(ini_directives)
    zend_ini_shutdown(TSRMLS_C);

    ...
}

當手動呼叫UNREGISTER_INI_ENTRIES完成之後,EG(ini_directives)已經不包含任何的元素,理論上講,此時的EG(ini_directives)是一張空的hash表。

2,configuration_hash的回收發生在EG(ini_directives)之後,上面貼出的程式碼中有關於php_shutdown_config的函數呼叫。 php_shutdown_config主要負責回收configuration_hash。

複製程式碼 程式碼如下:

int php_shutdown_config(void)
{
    // 回收configuration_hash
    zend_hash_destroy(&configuration_hash);
   
    ...
   
    return SUCCESS;
}

注意zend_hash_destroy並不會釋放configuration_hash本身的空間,同XXX_G存取的模組全域空間一樣,configuration_hash也是全域變量,無需手動回收。

3,當php_shutdown_config完成時,只剩下EG(ini_directives)的自身空間還沒被釋放。因此最後一步呼叫zend_ini_shutdown。 zend_ini_shutdown用來釋放EG(ini_directives)。在前文已經提到,此時的EG(ini_directives)理論上是一張空的hash表,因此該HashTable本身所佔用的空間需要被釋放。

複製程式碼 程式碼如下:

ZEND_API int zend_ini_shutdown(TSRMLS_D)
{
    // EG(ini_directives)是動態分配的空間,需要回收
    zend_hash_destroy(EG(ini_directives));
    free(EG(ini_directives));
    return SUCCESS;
}

4,總結
用一張圖大致描述一下和ini配置相關的流程:

php中動態修改ini配置_php基礎

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

適用於 Ubuntu 和 Debian 的 PHP 8.4 安裝和升級指南 適用於 Ubuntu 和 Debian 的 PHP 8.4 安裝和升級指南 Dec 24, 2024 pm 04:42 PM

PHP 8.4 帶來了多項新功能、安全性改進和效能改進,同時棄用和刪除了大量功能。 本指南介紹如何在 Ubuntu、Debian 或其衍生版本上安裝 PHP 8.4 或升級到 PHP 8.4

如何設定 Visual Studio Code (VS Code) 進行 PHP 開發 如何設定 Visual Studio Code (VS Code) 進行 PHP 開發 Dec 20, 2024 am 11:31 AM

Visual Studio Code,也稱為 VS Code,是一個免費的原始碼編輯器 - 或整合開發環境 (IDE) - 可用於所有主要作業系統。 VS Code 擁有大量針對多種程式語言的擴展,可以輕鬆編寫

我後悔之前不知道的 7 個 PHP 函數 我後悔之前不知道的 7 個 PHP 函數 Nov 13, 2024 am 09:42 AM

如果您是經驗豐富的PHP 開發人員,您可能會感覺您已經在那裡並且已經完成了。操作

您如何在PHP中解析和處理HTML/XML? 您如何在PHP中解析和處理HTML/XML? Feb 07, 2025 am 11:57 AM

本教程演示瞭如何使用PHP有效地處理XML文檔。 XML(可擴展的標記語言)是一種用於人類可讀性和機器解析的多功能文本標記語言。它通常用於數據存儲

在PHP API中說明JSON Web令牌(JWT)及其用例。 在PHP API中說明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

php程序在字符串中計數元音 php程序在字符串中計數元音 Feb 07, 2025 pm 12:12 PM

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

解釋PHP中的晚期靜態綁定(靜態::)。 解釋PHP中的晚期靜態綁定(靜態::)。 Apr 03, 2025 am 12:04 AM

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

什麼是PHP魔術方法(__ -construct,__destruct,__call,__get,__ set等)並提供用例? 什麼是PHP魔術方法(__ -construct,__destruct,__call,__get,__ set等)並提供用例? Apr 03, 2025 am 12:03 AM

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。

See all articles