摘要:問題發(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)閉所有錯誤報告表示顯示二函數(shù)說明設(shè)置應(yīng)該報告何種錯誤說明函數(shù)能夠在運(yùn)行時設(shè)置指令。后果是導(dǎo)致腳本終止不再繼續(xù)運(yùn)行。初始化啟動過程中發(fā)生的警告非致命錯誤。用戶產(chǎn)少的警告信息。出外的所有錯誤和警告信息。 錯誤報告級別:指定了在什么情況下,腳本代碼中的錯誤(這里的錯誤是廣義的錯誤,包括E_NOTICE注意、E_WARNING警告、E_ERROR致命錯誤等)會以錯誤報告...
摘要:英文原始文檔地址中文文檔地址當(dāng)被激活時,只要決定顯示通知,警告,錯誤等,就會顯示堆棧跟蹤。堆棧跟蹤中的變量默認(rèn)情況下,將在它生成的堆棧跟蹤中顯示可變信息。 文檔內(nèi)容來自xdebug.org/docs,翻譯時xdebug版本為2.6。我在官方文檔基礎(chǔ)上針對中文排版和教程內(nèi)容的編排做了一些優(yōu)化,希望中文文檔看起來更容易理解。 英文原始文檔地址:https://xdebug.org/docs...
摘要:為系統(tǒng)增加的第一行代碼不會影響該腳本在下的運(yùn)行,因此您也可以用該方法編寫跨平臺的腳本程序。指定會話頁面在客戶端中的有限期分鐘缺省下為分鐘。最原始的博主沒有找到,只能在此聲明,特為轉(zhuǎn)載。 這幾天需要用PHP寫一個定時抓取網(wǎng)頁的服務(wù)器應(yīng)用. 在網(wǎng)上搜了一下解決辦法, 發(fā)現(xiàn)OSchina的 一個問題的解答很精彩(值得一看,謝謝大牛們的精彩回答O(∩_∩)O~), 提出幾種解決辦法.現(xiàn)總結(jié)如下...
摘要:當(dāng)程序開發(fā)完成,成為正式產(chǎn)品時,我們希望將沒有預(yù)測到的報錯信息記錄到錯誤日志中,而不是將這些報錯信息展示給用戶,因為用戶極有可能利用這些暴露出腳本路徑數(shù)據(jù)庫信息或其他的報錯信息進(jìn)行一些破壞性的黑客行動。 程序報錯總是在所難免,盡管我們書寫代碼時已經(jīng)格外小心。 在開發(fā)php程序時,我們希望遇到php報錯,可以第一時間展示給我們,以便于調(diào)試。當(dāng)程序開發(fā)完成,成為正式產(chǎn)品時,我們希望將沒有預(yù)...
閱讀 3179·2023-04-25 19:09
閱讀 3888·2021-10-22 09:54
閱讀 1764·2021-09-29 09:35
閱讀 2919·2021-09-08 09:45
閱讀 2263·2021-09-06 15:00
閱讀 2775·2019-08-29 15:32
閱讀 1041·2019-08-28 18:30
閱讀 376·2019-08-26 13:43