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


Posted in PHP onJanuary 02, 2011

深入理解PHP之require/include顺序 https://3water.com/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运行过程的图(这个图是出自哪里的?鸟哥画的?)
从php核心代码分析require和include的区别

补习一下: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。

PHP 相关文章推荐
Ajax PHP分页演示
Jan 02 PHP
PHP 多维数组排序实现代码
Aug 05 PHP
php购物网站支付paypal使用方法
Nov 28 PHP
从php核心代码分析require和include的区别
Jan 02 PHP
php从数组中随机抽取一些元素的代码
Nov 05 PHP
php使用ICQ网关发送手机短信
Oct 30 PHP
在PHP上显示JFreechart画的统计图方法
Nov 03 PHP
php导出CSV抽象类实例
Sep 24 PHP
使用新浪微博API的OAuth认证发布微博实例
Mar 27 PHP
PHP5.4起内置web服务器使用方法
Aug 09 PHP
PHP实现的mysql操作类【MySQL与MySQLi方式】
Oct 07 PHP
PHP 出现 http500 错误的解决方法
Mar 09 PHP
深入理解PHP之require/include顺序 推荐
Jan 02 #PHP
PHP中foreach循环中使用引用要注意的地方
Jan 02 #PHP
PHP开发中四种查询返回结果分析
Jan 02 #PHP
linux下删除7天前日志的代码(php+shell)
Jan 02 #PHP
PHP中=赋值操作符对不同数据类型的不同行为
Jan 02 #PHP
完美实现GIF动画缩略图的php代码
Jan 02 #PHP
php实现无限级分类实现代码(递归方法)
Jan 01 #PHP
You might like
第三节 定义一个类 [3]
2006/10/09 PHP
php session_start()关于Cannot send session cache limiter - headers already sent错误解决方法
2009/11/27 PHP
PHP 实现字符串翻转(包含中文汉字)的实现代码
2017/04/01 PHP
PHP实现的杨辉三角求解算法分析
2019/03/11 PHP
抽出www.templatemonster.com的鼠标悬停加载大图模板的代码
2007/07/11 Javascript
jquery属性过滤选择器使用示例
2013/06/18 Javascript
JS取文本框中最小值的简单实例
2013/11/29 Javascript
js控制input框只读实现示例
2014/01/20 Javascript
jQuery异步加载数据并添加事件示例
2014/08/24 Javascript
js在指定位置增加节点函数insertBefore()用法实例
2015/01/12 Javascript
jQuery处理图片加载失败的常用方法
2015/06/08 Javascript
使用AngularJS和PHP的Laravel实现单页评论的方法
2015/06/19 Javascript
实例代码详解javascript实现窗口抖动及qq窗口抖动
2016/01/04 Javascript
JavaScript如何实现组合列表框中元素移动效果
2016/03/01 Javascript
JavaScript算法系列之快速排序(Quicksort)算法实例详解
2016/09/04 Javascript
Bootstrap modal 多弹窗之叠加关闭阴影遮罩问题的解决方法
2017/02/27 Javascript
Vue.js开发环境快速搭建教程
2017/03/17 Javascript
利用JavaScript实现栈的数据结构示例代码
2017/08/02 Javascript
JS在if中的强制类型转换方式
2018/07/15 Javascript
vue移动端微信授权登录插件封装的实例
2018/08/28 Javascript
JavaScript私有变量实例详解
2019/01/24 Javascript
修改Vue打包后的默认文件名操作
2020/08/12 Javascript
python机器学习理论与实战(四)逻辑回归
2018/01/19 Python
Python中多个数组行合并及列合并的方法总结
2018/04/12 Python
深入浅析python的第三方库pandas
2020/02/13 Python
opencv 阈值分割的具体使用
2020/07/08 Python
利用css3径向渐变做一张优惠券的示例
2018/03/22 HTML / CSS
NEW LOOK官网:英国时装零售巨头之一,快时尚品牌
2017/01/11 全球购物
Calphalon美国官网:美国顶级锅具品牌
2020/02/05 全球购物
大学生职业生涯规划范文
2013/12/31 职场文书
幼儿教育感言
2014/02/05 职场文书
公司担保书格式范文
2014/05/12 职场文书
人事主管岗位职责
2015/02/04 职场文书
银行实习推荐信
2015/03/27 职场文书
公司出差管理制度范本
2015/08/05 职场文书
Pandas-DataFrame知识点汇总
2022/03/16 Python