成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

set_limit_time()、ini_set()解析

Atom / 2325人閱讀

摘要:問題發(fā)現(xiàn)今天寫了一個腳本,提交代碼的時候京哥給我,果斷幫我指出這個腳本的運(yùn)行時間限制不要這么寫要這么寫然后給我講了一堆原理,什么直接進(jìn)內(nèi)存啊,需要暫時修改原配置啊恩,還是高工懂得多,于是我開始對兩個函數(shù)進(jìn)行了測試。

問題發(fā)現(xiàn)

今天寫了一個腳本,提交代碼的時候京哥給我cr,果斷幫我指出這個腳本的運(yùn)行時間限制不要這么寫

ini_set("max_execution_time","30");

要這么寫

set_limit_time(30);

然后給我講了一堆原理,什么 set_limit_time() 直接進(jìn)內(nèi)存啊,ini_set("max_execution_time",) 需要暫時修改原配置啊...恩,還是高工懂得多,于是我開始對兩個函數(shù)進(jìn)行了測試。

測試

測試代碼如下:


這不測不要緊,一測就發(fā)現(xiàn)了問題,兩次測試都是先sleep了10s,然后返回

beginFatal error: Maximum execution time of 1 second exceeded in /Users/jdq/test.php on line 6

難道sleep不算腳本執(zhí)行的時間?答案應(yīng)該是肯定的,可是我以前測試后端接口超時的時候確實用的sleep,而且也超時返回了504,思考了一下應(yīng)該是php-fpm的配置覆蓋了php的ini配置的原因吧,所以sleep的時間也視為一個cgi進(jìn)程的執(zhí)行時間。(此處推斷有待確定)回歸正題,馬上修改了測試代碼


兩個腳本都是執(zhí)行了1s,直接fatal。那這兩個函數(shù)又是在什么階段起作用的呢,修改測試代碼為


返回結(jié)果分別為

ini_set結(jié)果:
1528297536
begin
Fatal error: Maximum execution time of 5 seconds exceeded in /Users/jdq/test.php on line 11
1528297546

set_time_limit結(jié)果:
1528297751
begin
Fatal error: Maximum execution time of 5 seconds exceeded in /Users/jdq/test.php on line 11
1528297761

這兩個函數(shù)都是在執(zhí)行的時候才開始限定腳本執(zhí)行時間,感覺并沒有什么區(qū)別,所以我找了找兩個函數(shù)的源碼。

函數(shù)源碼 set_limit_time()

源碼如下(以下均為php7.1源碼)

PHP_FUNCTION(set_time_limit)
{
    zend_long new_timeout;
    char *new_timeout_str;
    int new_timeout_strlen;
    zend_string *key;
    //做了一些參數(shù)校驗
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &new_timeout) == FAILURE) {
        return;
    }

    new_timeout_strlen = (int)zend_spprintf(&new_timeout_str, 0, ZEND_LONG_FMT, new_timeout);
    //看到配置項max_execution_time這里我心里就開始哈哈哈了
    key = zend_string_init("max_execution_time", sizeof("max_execution_time")-1, 0);
    //其實調(diào)用了zend_alter_ini_entry_chars_ex這個函數(shù)
    if (zend_alter_ini_entry_chars_ex(key, new_timeout_str, new_timeout_strlen, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0) == SUCCESS) {
        RETVAL_TRUE;
    } else {
        RETVAL_FALSE;
    }
    zend_string_release(key);
    efree(new_timeout_str);
}

通過源碼我們可以看出,set_limit_time 函數(shù)就是調(diào)用了 zend_alter_ini_entry_chars_ex 對配置項 max_execution_time 進(jìn)行了一番操作,這個函數(shù)的源代碼

ZEND_API int zend_alter_ini_entry_chars_ex(zend_string *name, const char *value, size_t value_length, int modify_type, int stage, int force_change) /* {{{ */
{
    int ret;
    zend_string *new_value;

    new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
    //執(zhí)行了zend_alter_ini_entry_ex這個函數(shù)
    ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change);
    zend_string_release(new_value);
    return ret;
}

所以可以看出,set_limit_time 最終實現(xiàn)要是 zend_alter_ini_entry_ex ,下面我們將討論這個函數(shù)。

ini_set

源碼如下

PHP_FUNCTION(ini_set)
{
    zend_string *varname;
    zend_string *new_value;
    zend_string *val;
//參數(shù)處理
    ZEND_PARSE_PARAMETERS_START(2, 2)
        Z_PARAM_STR(varname)
        Z_PARAM_STR(new_value)
    ZEND_PARSE_PARAMETERS_END();
//去一張hash表根據(jù)配置項名字尋找當(dāng)前value,下面會說到,這個value通常會被釋放掉
    val = zend_ini_get_value(varname);

    /* copy to return here, because alter might free it! */
    if (val) {
        if (ZSTR_IS_INTERNED(val)) {
            RETVAL_INTERNED_STR(val);
        } else if (ZSTR_LEN(val) == 0) {
            RETVAL_EMPTY_STRING();
        } else if (ZSTR_LEN(val) == 1) {
            RETVAL_INTERNED_STR(ZSTR_CHAR((zend_uchar)ZSTR_VAL(val)[0]));
        } else if (!(GC_FLAGS(val) & GC_PERSISTENT)) {
            ZVAL_NEW_STR(return_value, zend_string_copy(val));
        } else {
            ZVAL_NEW_STR(return_value, zend_string_init(ZSTR_VAL(val), ZSTR_LEN(val), 0));
        }
    } else {
        RETVAL_FALSE;
    }
 //一堆我也不知道要干什么的校驗
#define _CHECK_PATH(var, var_len, ini) php_ini_check_path(var, var_len, ini, sizeof(ini))
    /* open basedir check */
    if (PG(open_basedir)) {
        if (_CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "error_log") ||
            _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.class.path") ||
            _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.home") ||
            _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "mail.log") ||
            _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.library.path") ||
            _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "vpopmail.directory")) {
            if (php_check_open_basedir(ZSTR_VAL(new_value))) {
                zval_dtor(return_value);
                RETURN_FALSE;
            }
        }
    }
#undef _CHECK_PATH
//最終要執(zhí)行zend_alter_ini_entry_ex這個函數(shù)
    if (zend_alter_ini_entry_ex(varname, new_value, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0) == FAILURE) {
        zval_dtor(return_value);
        RETURN_FALSE;
    }
}

ini_set 的實現(xiàn)也是要靠函數(shù) zend_alter_ini_entry_ex ,而 set_limit_time 只是其中一個配置項(參數(shù))為 max_execution_time 的實現(xiàn)而已,原來這兩個函數(shù)實現(xiàn)機(jī)制是一樣的,這時京哥的臉色已經(jīng)發(fā)生了微微的變化,哈哈哈......

那么zend_alter_ini_entry_ex又是如何實現(xiàn)的呢?

zend_alter_ini_entry_ex

源碼如下

ZEND_API int zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, int force_change) /* {{{ */
{
    zend_ini_entry *ini_entry;
    zend_string *duplicate;
    zend_bool modifiable;
    zend_bool modified;
//EG(modified_ini_directives)用于存放被修改過的ini_entry,根據(jù)name(配置名稱)尋找到對應(yīng)ini_entry
    if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
        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;
        }
    }

    if (!EG(modified_ini_directives)) {
        ALLOC_HASHTABLE(EG(modified_ini_directives));
        zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
    }
    //不管我們先后在php代碼中調(diào)用幾次ini_set,只有第一次ini_set時才會進(jìn)入這段邏輯,設(shè)置orig_value。從第二次調(diào)用ini_set開始,便不會再次執(zhí)行這段分支,因為此時的modified已經(jīng)被置為1了。因此,ini_entry->orig_value始終保存的是第一次修改之前的配置值(即最原始的配置)
    if (!modified) {
        ini_entry->orig_value = ini_entry->value;
        ini_entry->orig_modifiable = modifiable;
        ini_entry->modified = 1;
        zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
    }

    duplicate = zend_string_copy(new_value);
    
//調(diào)用on_modify是為了能夠更新模塊的全局變量。每一個ini_entry中都存儲了該模塊全局變量的地址以及對應(yīng)的偏移量,使得on_modify可以很迅速的進(jìn)行內(nèi)存修改。
    if (!ini_entry->on_modify
        || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
        if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */
            zend_string_release(ini_entry->value);
        }
        ini_entry->value = duplicate;
    } else {
        zend_string_release(duplicate);
        return FAILURE;
    }

    return SUCCESS;
}

可以看出該函數(shù)是同過通過 on_modify 回調(diào)函數(shù)直接修改了內(nèi)存中的全局變量而達(dá)到控制執(zhí)行時間的目的,所以這也解釋了為什么ini_set 在執(zhí)行結(jié)束就會失效。先說這些,過幾天我會整理一下把整個PHP生命周期的ini加載過程詳細(xì)總結(jié)一下。

參考文章:http://www.cnblogs.com/driftc...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/28761.html

相關(guān)文章

  • php的異常和處理

    摘要:常見錯誤處理類型語法錯誤環(huán)境錯誤邏輯錯誤常見錯誤級別最低級別的錯誤不推薦,不建議,使用一些過期函數(shù)的時候會出現(xiàn),程序繼續(xù)執(zhí)行通知級別的錯誤使用一些未定義變量常量或者數(shù)組沒有加引號的時候會出現(xiàn),程序繼續(xù)執(zhí)行警告級別的錯誤程序出問題了,需要修改 常見錯誤處理類型 語法錯誤 環(huán)境錯誤 邏輯錯誤 常見錯誤級別 Deprecated 最低級別的錯誤 不推薦,不建議,使用一些過期函數(shù)的時候...

    CarterLi 評論0 收藏0
  • php error_reporting()關(guān)閉報錯

    摘要:至,有同樣的行為。表示關(guān)閉所有錯誤報告表示顯示二函數(shù)說明設(shè)置應(yīng)該報告何種錯誤說明函數(shù)能夠在運(yùn)行時設(shè)置指令。后果是導(dǎo)致腳本終止不再繼續(xù)運(yùn)行。初始化啟動過程中發(fā)生的警告非致命錯誤。用戶產(chǎn)少的警告信息。出外的所有錯誤和警告信息。 錯誤報告級別:指定了在什么情況下,腳本代碼中的錯誤(這里的錯誤是廣義的錯誤,包括E_NOTICE注意、E_WARNING警告、E_ERROR致命錯誤等)會以錯誤報告...

    noONE 評論0 收藏0
  • Xdebug中文文檔-堆棧跟蹤

    摘要:英文原始文檔地址中文文檔地址當(dāng)被激活時,只要決定顯示通知,警告,錯誤等,就會顯示堆棧跟蹤。堆棧跟蹤中的變量默認(rèn)情況下,將在它生成的堆棧跟蹤中顯示可變信息。 文檔內(nèi)容來自xdebug.org/docs,翻譯時xdebug版本為2.6。我在官方文檔基礎(chǔ)上針對中文排版和教程內(nèi)容的編排做了一些優(yōu)化,希望中文文檔看起來更容易理解。 英文原始文檔地址:https://xdebug.org/docs...

    wzyplus 評論0 收藏0
  • PHP 實現(xiàn)定時任務(wù)的幾種方法

    摘要:為系統(tǒng)增加的第一行代碼不會影響該腳本在下的運(yùn)行,因此您也可以用該方法編寫跨平臺的腳本程序。指定會話頁面在客戶端中的有限期分鐘缺省下為分鐘。最原始的博主沒有找到,只能在此聲明,特為轉(zhuǎn)載。 這幾天需要用PHP寫一個定時抓取網(wǎng)頁的服務(wù)器應(yīng)用. 在網(wǎng)上搜了一下解決辦法, 發(fā)現(xiàn)OSchina的 一個問題的解答很精彩(值得一看,謝謝大牛們的精彩回答O(∩_∩)O~), 提出幾種解決辦法.現(xiàn)總結(jié)如下...

    huhud 評論0 收藏0
  • 開發(fā)模式與產(chǎn)品模式下的PHP報錯處理

    摘要:當(dāng)程序開發(fā)完成,成為正式產(chǎn)品時,我們希望將沒有預(yù)測到的報錯信息記錄到錯誤日志中,而不是將這些報錯信息展示給用戶,因為用戶極有可能利用這些暴露出腳本路徑數(shù)據(jù)庫信息或其他的報錯信息進(jìn)行一些破壞性的黑客行動。 程序報錯總是在所難免,盡管我們書寫代碼時已經(jīng)格外小心。 在開發(fā)php程序時,我們希望遇到php報錯,可以第一時間展示給我們,以便于調(diào)試。當(dāng)程序開發(fā)完成,成為正式產(chǎn)品時,我們希望將沒有預(yù)...

    baiy 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<