从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获取post中的json数据的实现方法
Jun 08 PHP
微博短链接算法php版本实现代码
Sep 15 PHP
php 生成唯一id的几种解决方法
Mar 08 PHP
关于Iframe如何跨域访问Cookie和Session的解决方法
Apr 15 PHP
Zend Studio 实用快捷键一览表(精心整理)
Aug 10 PHP
为百度UE编辑器上传图片添加水印功能
Apr 16 PHP
php+flash+jQuery多图片上传源码分享
Jul 27 PHP
golang与PHP输出excel示例
Jul 22 PHP
Yii2.0中的COOKIE和SESSION用法
Aug 12 PHP
php有效防止图片盗用、盗链的两种方法
Nov 01 PHP
ThinkPHP 模板substr的截取字符串函数详解
Jan 09 PHP
PHP封装的page分页类定义与用法完整示例
Dec 24 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
PHP的关于变量和日期处理的一些面试题目整理
2015/08/10 PHP
ECSHOP完美解决Deprecated: preg_replace()报错的问题
2016/05/17 PHP
PHP数组遍历的几种常见方式总结
2019/02/15 PHP
选择TreeView控件的树状数据节点的JS方法(jquery)
2010/02/06 Javascript
JS禁用浏览器退格键实现思路及代码
2013/10/29 Javascript
手机号码,密码正则验证
2014/09/04 Javascript
html的DOM中document对象images集合用法实例
2015/01/21 Javascript
jQuery中DOM操作实例分析
2015/01/23 Javascript
javascript回到顶部特效
2016/07/30 Javascript
Three.js的使用及绘制基础3D图形详解
2017/04/27 Javascript
vue router嵌套路由在history模式下刷新无法渲染页面问题的解决方法
2018/01/25 Javascript
详解微信小程序调起键盘性能优化
2018/07/24 Javascript
使用JS location实现搜索框历史记录功能
2019/12/23 Javascript
ES6新增的数组知识实例小结
2020/05/23 Javascript
python使用mailbox打印电子邮件的方法
2015/04/30 Python
Django中URL视图函数的一些高级概念介绍
2015/07/20 Python
在Python程序和Flask框架中使用SQLAlchemy的教程
2016/06/06 Python
python3利用smtplib通过qq邮箱发送邮件方法示例
2017/12/03 Python
python3实现跳一跳点击跳跃
2018/01/08 Python
TensorFlow如何实现反向传播
2018/02/06 Python
Python3使用Matplotlib 绘制精美的数学函数图形
2019/04/11 Python
OpenCV 轮廓检测的实现方法
2019/07/03 Python
django数据模型on_delete, db_constraint的使用详解
2019/12/24 Python
在python中实现求输出1-3+5-7+9-......101的和
2020/04/02 Python
html5理解head_动力节点Java学院整理
2017/07/13 HTML / CSS
HTML5中通过li-canvas轻松实现单图、多图、圆角图绘制,单行文字、多行文字等
2018/11/30 HTML / CSS
英国高街电视:High Street TV
2018/05/22 全球购物
Speedo速比涛法国官方网站:泳衣、泳镜、泳帽、泳裤
2019/07/30 全球购物
《最可爱的人》教学反思
2014/02/14 职场文书
三八红旗手先进事迹材料
2014/05/13 职场文书
中学清明节活动总结
2014/07/04 职场文书
公司向个人借款协议书范本
2014/10/09 职场文书
初三英语教学计划
2015/01/23 职场文书
2016开学第一课心得体会
2016/01/23 职场文书
用几道面试题来看JavaScript执行机制
2021/04/30 Javascript
Sentry的安装、配置、使用教程(Sentry日志手机系统)
2022/07/23 Python