從php核心代碼分析require和include的區(qū)別
普及
在php手冊(cè)中:
require() is identical to include() except upon failure it will also produce a fatal E_ERROR level error. In other words, it will halt the script whereas include() only emits a warning (E_WARNING) which allows the script to continue.
就是說(shuō)在失敗的時(shí)候,require是會(huì)中止php運(yùn)行的,而include是可以繼續(xù)運(yùn)行的。
倒底有什么樣的區(qū)別呢?我們帶著這個(gè)疑問(wèn)來(lái)一起進(jìn)入PHP的核心代碼。
下面是一個(gè)PHP運(yùn)行過(guò)程的圖(這個(gè)圖是出自哪里的?鳥(niǎo)哥畫(huà)的?)
補(bǔ)習(xí)一下:lex是代碼掃描器,掃描代碼用的,yacc是Yet Another Compiler Compiler,作用是把任何一種代碼的語(yǔ)法轉(zhuǎn)成yacc語(yǔ)法,yacc就是解析器(真TMD繞)。
lex在c下的后綴是*.l yacc是*.y
正題
下面看操作記錄:
cc@cc-laptop:/opt/workspace$ svn checkout http://svn.php.net/repository/php/php-src/branches/PHP_5_3 php-src-5.3
從svn取最新的php源代碼。
開(kāi)始深入:
cc@cc-laptop:/opt/workspace/php-src-5.3$ find . -type f -name “*.l” -exec grep -Hn “require_once” {} \;
./Zend/zend_language_scanner.l:1093:”require_once” {
尋找lex代碼掃描器文件中出現(xiàn)require_once的地方,zend_language_scanner.l的1093行。
1093 “require_once” {
1094 return T_REQUIRE_ONCE;
1095 }
然后再搜一下T_REQUIRE_ONCE,
cc@cc-laptop:/opt/workspace/php-src-5.3$ find . -type f -name “*.y” -exec grep -Hn “T_INCLUDE” {} \;
./Zend/zend_language_parser.y:52:%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
./Zend/zend_language_parser.y:985: | T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
./Zend/zend_language_parser.y:986: | T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }
在985行附近,有這樣一群代碼:
internal_functions_in_yacc:
T_ISSET ‘(‘ isset_variables ‘)' { $$ = $3; }
| T_EMPTY ‘(‘ variable ‘)' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); }
| T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
| T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }
| T_EVAL ‘(‘ expr ‘)' { zend_do_include_or_eval(ZEND_EVAL, &$$, &$3 TSRMLS_CC); }
| T_REQUIRE expr { zend_do_include_or_eval(ZEND_REQUIRE, &$$, &$2 TSRMLS_CC); }
| T_REQUIRE_ONCE expr { zend_do_include_or_eval(ZEND_REQUIRE_ONCE, &$$, &$2 TSRMLS_CC); }
;
于是乎,我們需要繼續(xù)深入尋找zend_do_include_or_eval,
cc@cc-laptop:/opt/workspace/php-src-5.3$ find . -type f -name “*.c” -exec grep -Hn “zend_do_include_or_eval” {} \;
./Zend/zend_compile.c:4317:void zend_do_include_or_eval(int type, znode *result, const znode *op1 TSRMLS_DC) /* {{{ */
zend_do_include_or_eval中組裝了一個(gè)結(jié)構(gòu)體,ZEND_INCLUDE_OR_EVAL。
再在zend_vm_def.h中找到ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMP|VAR|CV, ANY):
switch (Z_LVAL(opline->op2.u.constant)) {代碼略}
中間關(guān)鍵的一句是:
new_op_array = compile_filename(Z_LVAL(opline->op2.u.constant), inc_filename TSRMLS_CC);
在zend_complie.h文件中:
ZEND_API zend_op_array *compile_filename(int type, zval *filename TSRMLS_DC);
這個(gè)函數(shù)定義在zend_language_scaner.l文件中,找出最核心的代碼:
if (open_file_for_scanning(file_handle TSRMLS_CC)==FAILURE) {
// require與include的差別:錯(cuò)誤信息的顯示級(jí)別(有bailout和無(wú)bailout)
if (type==ZEND_REQUIRE) { //require時(shí)
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC);
zend_bailout();
} else {
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC);
}
compilation_successful=0;
} else {代碼略}
繼續(xù)追蹤zend_message_dispatcher可以在main/main.c文件中找到php_message_handler_for_zend函數(shù):
//include輸出錯(cuò)誤信息時(shí)的級(jí)別為:E_WARNING
case ZMSG_FAILED_INCLUDE_FOPEN:
php_error_docref(“function.include” TSRMLS_CC, E_WARNING, “Failed opening ‘%s' for inclusion (include_path='%s')”, php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path)));
break;
//require輸出錯(cuò)誤信息時(shí)的級(jí)別為:E_COMPILE_ERROR
代碼略
總結(jié)
和開(kāi)頭PHP手冊(cè)所說(shuō)完全一致,require和include的區(qū)別在于,出現(xiàn)錯(cuò)誤時(shí),一個(gè)是error一個(gè)是warning。
相關(guān)文章
PHP使用gearman進(jìn)行異步的郵件或短信發(fā)送操作詳解
這篇文章主要介紹了PHP使用gearman進(jìn)行異步的郵件或短信發(fā)送操作,總結(jié)分析了PHP基于gearman實(shí)現(xiàn)異步的郵件或短信發(fā)送相關(guān)配置、啟動(dòng)與操作注意事項(xiàng),需要的朋友可以參考下2020-02-02PHP如何使用array_unshift()在數(shù)組開(kāi)頭插入元素
這篇文章主要介紹了PHP如何使用array_unshift()在數(shù)組開(kāi)頭插入元素,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-091億條數(shù)據(jù)如何分表100張到Mysql數(shù)據(jù)庫(kù)中(PHP)
這篇文章主要介紹了當(dāng)數(shù)據(jù)量猛增的時(shí)候如何把一億條數(shù)據(jù)分表100張到Mysql數(shù)據(jù)庫(kù)中,需要的朋友可以參考下2015-07-07php寫(xiě)入mysql中文亂碼的實(shí)例解決方法
在本文里小編給大家整理的是關(guān)于php寫(xiě)入mysql中文亂碼的解決方法和知識(shí)點(diǎn),有需要的朋友們參考下。2019-09-09php使用memcoder將視頻轉(zhuǎn)成mp4格式的方法
這篇文章主要介紹了php使用memcoder將視頻轉(zhuǎn)成mp4格式的方法,涉及php操作視頻文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03PHP開(kāi)發(fā)注意事項(xiàng)總結(jié)
這篇文章主要介紹了PHP開(kāi)發(fā)注意事項(xiàng)總結(jié),非常詳細(xì),需要的朋友可以參考下2015-02-02php中時(shí)間軸開(kāi)發(fā)(剛剛、5分鐘前、昨天10:23等)
php中時(shí)間軸開(kāi)發(fā),即顯示為“剛剛”、“5分鐘前”、“昨天10:23”等2011-10-10