从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 相关文章推荐
php $_SERVER["REQUEST_URI"]获取值的通用解决方法
Jun 21 PHP
thinkphp 多表 事务详解
Jun 17 PHP
如何使用“PHP” 彩蛋进行敏感信息获取
Aug 07 PHP
php获取参数的几种方法总结
Feb 18 PHP
php出现web系统多域名登录失败的解决方法
Sep 30 PHP
PHP动态编译出现Cannot find autoconf的解决方法
Nov 05 PHP
php简单smarty入门程序实例
Jun 11 PHP
Laravel手动分页实现方法详解
Oct 09 PHP
Yii2选项卡的简单使用
May 26 PHP
PHP 实现公历日期与农历日期的互转换
Sep 13 PHP
PHP实现QQ、微信和支付宝三合一收款码实例代码
Feb 19 PHP
PHP7 foreach() 函数修改
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
yii2 上传图片的示例代码
2018/11/02 PHP
Laravel6.2中用于用户登录的新密码确认流程详解
2019/10/16 PHP
一个用javascript写的select支持上下键、首字母筛选以及回车取值的功能
2009/09/09 Javascript
JavaScript ( (__ = !$ + $)[+$] + ({} + $)[_/_] +({} + $)[_/_] )
2011/02/25 Javascript
javascript将数字转换整数金额大写的方法
2015/01/27 Javascript
JS+CSS实现简易实用的滑动门菜单效果
2015/09/18 Javascript
浅析js绑定事件的常用方法
2016/05/15 Javascript
jQuery控制文本框只能输入数字和字母及使用方法
2016/05/26 Javascript
基于JavaScript实现单选框下拉菜单添加文件效果
2016/06/26 Javascript
JavaScript学习笔记整理_用于模式匹配的String方法
2016/09/19 Javascript
Vue组件开发初探
2017/02/14 Javascript
网页中右键功能的实现方法之contextMenu的使用
2017/02/20 Javascript
详解Vue2.0里过滤器容易踩到的坑
2017/06/01 Javascript
原生js二级联动效果
2017/06/20 Javascript
vue.js移动端app实战1:初始配置详解
2017/07/24 Javascript
vue watch深度监听对象实现数据联动效果
2018/08/16 Javascript
Vue+element-ui 实现表格的分页功能示例
2018/08/18 Javascript
Async/Await替代Promise的6个理由
2019/06/15 Javascript
IntelliJ IDEA编辑器配置vue高亮显示
2019/09/26 Javascript
js 计算月/周的第一天和最后一天代码
2020/02/01 Javascript
python基于pyDes库实现des加密的方法
2017/04/29 Python
浅谈Python traceback的优雅处理
2018/08/31 Python
Django如何继承AbstractUser扩展字段
2020/11/27 Python
浅谈HTML5新增及移除的元素
2016/06/27 HTML / CSS
床上用品全球在线购物:BeddingInn
2016/12/18 全球购物
将世界上最美丽的摄影作品转化为艺术作品:Photos.com
2017/11/28 全球购物
Maison Lab荷兰:名牌Outlet购物
2018/08/10 全球购物
美国床垫连锁店:Mattress Firm
2021/02/13 全球购物
行政助理求职自荐信
2013/10/26 职场文书
巾帼志愿者活动方案
2014/08/17 职场文书
政风行风评议工作总结
2014/10/21 职场文书
上课睡觉万能检讨书
2015/02/17 职场文书
回复函格式及范文
2015/07/14 职场文书
浅谈PHP7中的一些小技巧
2021/05/29 PHP
详解Redis的三种常用的缓存读写策略步骤
2022/05/06 Redis
python数字图像处理之对比度与亮度调整示例
2022/06/28 Python