当前位置:首页 > PHP教程 > PHP高级教程

从php核心代码分析require和include的区别

深入理解php之require/include顺序 http://www.jb51.net/article/25867.htm
普及
在php手册中:

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.

就是说在失败的时候,require是会中止php运行的,而include是可以继续运行的。
倒底有什么样的区别呢?我们带着这个疑问来一起进入php的核心代码。
下面是一个php运行过程的图(这个图是出自哪里的?鸟哥画的?)

补习一下:lex是代码扫描器,扫描代码用的,yacc是yet another compiler compiler,作用是把任何一种代码的语法转成yacc语法,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源代码。

开始深入:

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代码扫描器文件中出现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); }
;

于是乎,我们需要继续深入寻找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中组装了一个结构体,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)) {代码略}

中间关键的一句是:
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);

这个函数定义在zend_language_scaner.l文件中,找出最核心的代码:

if (open_file_for_scanning(file_handle tsrmls_cc)==failure) {
// require与include的差别:错误信息的显示级别(有bailout和无bailout)
if (type==zend_require) { //require时
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 {代码略}

继续追踪zend_message_dispatcher可以在main/main.c文件中找到php_message_handler_for_zend函数:

//include输出错误信息时的级别为: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输出错误信息时的级别为:e_compile_error
代码略

总结
和开头php手册所说完全一致,require和include的区别在于,出现错误时,一个是error一个是warning。


【说明】本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:)!