摘要:問題發(fā)現(xiàn)今天寫了一個(gè)腳本,提交代碼的時(shí)候京哥給我,果斷幫我指出這個(gè)腳本的運(yùn)行時(shí)間限制不要這么寫要這么寫然后給我講了一堆原理,什么直接進(jìn)內(nèi)存啊,需要暫時(shí)修改原配置啊恩,還是高工懂得多,于是我開始對(duì)兩個(gè)函數(shù)進(jìn)行了測(cè)試。
問題發(fā)現(xiàn)
今天寫了一個(gè)腳本,提交代碼的時(shí)候京哥給我cr,果斷幫我指出這個(gè)腳本的運(yùn)行時(shí)間限制不要這么寫
ini_set("max_execution_time","30");
要這么寫
set_limit_time(30);
然后給我講了一堆原理,什么 set_limit_time() 直接進(jìn)內(nèi)存啊,ini_set("max_execution_time",) 需要暫時(shí)修改原配置啊...恩,還是高工懂得多,于是我開始對(duì)兩個(gè)函數(shù)進(jìn)行了測(cè)試。
測(cè)試測(cè)試代碼如下:
這不測(cè)不要緊,一測(cè)就發(fā)現(xiàn)了問題,兩次測(cè)試都是先sleep了10s,然后返回
beginFatal error: Maximum execution time of 1 second exceeded in /Users/jdq/test.php on line 6難道sleep不算腳本執(zhí)行的時(shí)間?答案應(yīng)該是肯定的,可是我以前測(cè)試后端接口超時(shí)的時(shí)候確實(shí)用的sleep,而且也超時(shí)返回了504,思考了一下應(yīng)該是php-fpm的配置覆蓋了php的ini配置的原因吧,所以sleep的時(shí)間也視為一個(gè)cgi進(jìn)程的執(zhí)行時(shí)間。(此處推斷有待確定)回歸正題,馬上修改了測(cè)試代碼
兩個(gè)腳本都是執(zhí)行了1s,直接fatal。那這兩個(gè)函數(shù)又是在什么階段起作用的呢,修改測(cè)試代碼為
返回結(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這兩個(gè)函數(shù)都是在執(zhí)行的時(shí)候才開始限定腳本執(zhí)行時(shí)間,感覺并沒有什么區(qū)別,所以我找了找兩個(gè)函數(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ù)校驗(yàn) 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); //看到配置項(xiàng)max_execution_time這里我心里就開始哈哈哈了 key = zend_string_init("max_execution_time", sizeof("max_execution_time")-1, 0); //其實(shí)調(diào)用了zend_alter_ini_entry_chars_ex這個(gè)函數(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 對(duì)配置項(xiàng) max_execution_time 進(jìn)行了一番操作,這個(gè)函數(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這個(gè)函數(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 最終實(shí)現(xiàn)要是 zend_alter_ini_entry_ex ,下面我們將討論這個(gè)函數(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ù)配置項(xiàng)名字尋找當(dāng)前value,下面會(huì)說到,這個(gè)value通常會(huì)被釋放掉 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; } //一堆我也不知道要干什么的校驗(yàn) #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這個(gè)函數(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 的實(shí)現(xiàn)也是要靠函數(shù) zend_alter_ini_entry_ex ,而 set_limit_time 只是其中一個(gè)配置項(xiàng)(參數(shù))為 max_execution_time 的實(shí)現(xiàn)而已,原來這兩個(gè)函數(shù)實(shí)現(xiàn)機(jī)制是一樣的,這時(shí)京哥的臉色已經(jīng)發(fā)生了微微的變化,哈哈哈......
那么zend_alter_ini_entry_ex又是如何實(shí)現(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(配置名稱)尋找到對(duì)應(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時(shí)才會(huì)進(jìn)入這段邏輯,設(shè)置orig_value。從第二次調(diào)用ini_set開始,便不會(huì)再次執(zhí)行這段分支,因?yàn)榇藭r(shí)的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是為了能夠更新模塊的全局變量。每一個(gè)ini_entry中都存儲(chǔ)了該模塊全局變量的地址以及對(duì)應(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í)行時(shí)間的目的,所以這也解釋了為什么ini_set 在執(zhí)行結(jié)束就會(huì)失效。先說這些,過幾天我會(huì)整理一下把整個(gè)PHP生命周期的ini加載過程詳細(xì)總結(jié)一下。
參考文章:http://www.cnblogs.com/driftc...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/28761.html
摘要:常見錯(cuò)誤處理類型語法錯(cuò)誤環(huán)境錯(cuò)誤邏輯錯(cuò)誤常見錯(cuò)誤級(jí)別最低級(jí)別的錯(cuò)誤不推薦,不建議,使用一些過期函數(shù)的時(shí)候會(huì)出現(xiàn),程序繼續(xù)執(zhí)行通知級(jí)別的錯(cuò)誤使用一些未定義變量常量或者數(shù)組沒有加引號(hào)的時(shí)候會(huì)出現(xiàn),程序繼續(xù)執(zhí)行警告級(jí)別的錯(cuò)誤程序出問題了,需要修改 常見錯(cuò)誤處理類型 語法錯(cuò)誤 環(huán)境錯(cuò)誤 邏輯錯(cuò)誤 常見錯(cuò)誤級(jí)別 Deprecated 最低級(jí)別的錯(cuò)誤 不推薦,不建議,使用一些過期函數(shù)的時(shí)候...
摘要:至,有同樣的行為。表示關(guān)閉所有錯(cuò)誤報(bào)告表示顯示二函數(shù)說明設(shè)置應(yīng)該報(bào)告何種錯(cuò)誤說明函數(shù)能夠在運(yùn)行時(shí)設(shè)置指令。后果是導(dǎo)致腳本終止不再繼續(xù)運(yùn)行。初始化啟動(dòng)過程中發(fā)生的警告非致命錯(cuò)誤。用戶產(chǎn)少的警告信息。出外的所有錯(cuò)誤和警告信息。 錯(cuò)誤報(bào)告級(jí)別:指定了在什么情況下,腳本代碼中的錯(cuò)誤(這里的錯(cuò)誤是廣義的錯(cuò)誤,包括E_NOTICE注意、E_WARNING警告、E_ERROR致命錯(cuò)誤等)會(huì)以錯(cuò)誤報(bào)告...
摘要:英文原始文檔地址中文文檔地址當(dāng)被激活時(shí),只要決定顯示通知,警告,錯(cuò)誤等,就會(huì)顯示堆棧跟蹤。堆棧跟蹤中的變量默認(rèn)情況下,將在它生成的堆棧跟蹤中顯示可變信息。 文檔內(nèi)容來自xdebug.org/docs,翻譯時(shí)xdebug版本為2.6。我在官方文檔基礎(chǔ)上針對(duì)中文排版和教程內(nèi)容的編排做了一些優(yōu)化,希望中文文檔看起來更容易理解。 英文原始文檔地址:https://xdebug.org/docs...
摘要:為系統(tǒng)增加的第一行代碼不會(huì)影響該腳本在下的運(yùn)行,因此您也可以用該方法編寫跨平臺(tái)的腳本程序。指定會(huì)話頁面在客戶端中的有限期分鐘缺省下為分鐘。最原始的博主沒有找到,只能在此聲明,特為轉(zhuǎn)載。 這幾天需要用PHP寫一個(gè)定時(shí)抓取網(wǎng)頁的服務(wù)器應(yīng)用. 在網(wǎng)上搜了一下解決辦法, 發(fā)現(xiàn)OSchina的 一個(gè)問題的解答很精彩(值得一看,謝謝大牛們的精彩回答O(∩_∩)O~), 提出幾種解決辦法.現(xiàn)總結(jié)如下...
摘要:當(dāng)程序開發(fā)完成,成為正式產(chǎn)品時(shí),我們希望將沒有預(yù)測(cè)到的報(bào)錯(cuò)信息記錄到錯(cuò)誤日志中,而不是將這些報(bào)錯(cuò)信息展示給用戶,因?yàn)橛脩魳O有可能利用這些暴露出腳本路徑數(shù)據(jù)庫信息或其他的報(bào)錯(cuò)信息進(jìn)行一些破壞性的黑客行動(dòng)。 程序報(bào)錯(cuò)總是在所難免,盡管我們書寫代碼時(shí)已經(jīng)格外小心。 在開發(fā)php程序時(shí),我們希望遇到php報(bào)錯(cuò),可以第一時(shí)間展示給我們,以便于調(diào)試。當(dāng)程序開發(fā)完成,成為正式產(chǎn)品時(shí),我們希望將沒有預(yù)...
閱讀 3259·2023-04-25 19:09
閱讀 3964·2021-10-22 09:54
閱讀 1831·2021-09-29 09:35
閱讀 2993·2021-09-08 09:45
閱讀 2428·2021-09-06 15:00
閱讀 2836·2019-08-29 15:32
閱讀 1115·2019-08-28 18:30
閱讀 423·2019-08-26 13:43