从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 相关文章推荐
5.PHP的其他功能
Oct 09 PHP
用PHP4访问Oracle815
Oct 09 PHP
一键删除顽固的空文件夹 软件下载
Jan 26 PHP
Thinkphp使用mongodb数据库实现多条件查询方法
Jun 26 PHP
PHP性能分析工具XHProf安装使用教程
May 13 PHP
PHP反射机制原理与用法详解
Feb 15 PHP
php根据用户名和手机号查询是否存在手机号码
Feb 16 PHP
PHP使用第三方即时获取物流动态实例详解
Apr 27 PHP
Ubuntu彻底删除PHP7.0的方法
Jul 27 PHP
PHP利用pdo_odbc实现连接数据库示例【基于ThinkPHP5.1搭建的项目】
May 13 PHP
PHP利用缓存处理用户注册时的邮箱验证,成功后用户数据存入数据库操作示例
Dec 31 PHP
深入理解PHP+Mysql分布式事务与解决方案
Dec 03 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
为了这两部电子管收音机,买了6套全新电子管和10粒刻度盘灯泡
2021/03/02 无线电
PHP静态新闻列表自动生成代码
2007/06/14 PHP
PHP开发中的错误收集,不定期更新。
2011/02/03 PHP
判断Keep-Alive模式的HTTP请求的结束的实现代码
2011/08/06 PHP
php-cli简介(不会Shell语言一样用Shell)
2013/06/03 PHP
php程序内部post数据的方法
2015/03/31 PHP
学习php设计模式 php实现桥梁模式(bridge)
2015/12/07 PHP
CodeIgniter配置之autoload.php自动加载用法分析
2016/01/20 PHP
php Session无效分析资料整理
2016/11/29 PHP
简单实现php上传文件功能
2017/09/21 PHP
用js实现的检测浏览器和系统的函数
2009/04/09 Javascript
JavaScript 学习笔记二 字符串拼接
2010/03/28 Javascript
js获取元素在浏览器中的绝对位置
2010/07/24 Javascript
JS判断页面加载状态以及添加遮罩和缓冲动画的代码
2012/10/11 Javascript
jQuery ajaxSubmit 实现ajax提交表单局部刷新
2016/07/04 Javascript
Vue表单及表单绑定方法
2018/09/04 Javascript
微信小程序实现星星评价效果
2018/11/02 Javascript
Vue快速实现通用表单验证功能
2019/12/05 Javascript
jquery实现垂直手风琴导航栏
2020/02/18 jQuery
Node.js API详解之 console模块用法详解
2020/05/12 Javascript
JavaScript中的函数式编程详解
2020/08/22 Javascript
[03:43]2014DOTA2西雅图国际邀请赛 newbee战队巡礼
2014/07/07 DOTA
[04:29]DOTA2亚洲邀请赛小组赛第一日 TOP10精彩集锦
2015/02/01 DOTA
[02:38]DOTA2 夜魇暗潮2020活动介绍官方视频
2020/11/04 DOTA
python爬虫学习笔记之Beautifulsoup模块用法详解
2020/04/09 Python
python中如何写类
2020/06/29 Python
python+requests接口自动化框架的实现
2020/08/31 Python
俄罗斯最大的隐形眼镜销售网站:Ochkov.Net
2021/02/07 全球购物
个人生活学习自我评价范文
2013/11/26 职场文书
海南地接欢迎词
2014/01/14 职场文书
物理教学随笔感言
2014/02/22 职场文书
公司承诺书范文
2014/05/19 职场文书
公务员年度个人总结
2015/02/12 职场文书
收入证明申请书
2015/06/12 职场文书
十大最强飞行系宝可梦,BUG燕上榜,第二是飞行系王者
2022/03/18 日漫
JavaScript原型链中函数和对象的理解
2022/06/16 Javascript