从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 相关文章推荐
实用函数7
Nov 08 PHP
php 连接mysql连接被重置的解决方法
Feb 15 PHP
解析php多线程下载远程多个文件
Jun 25 PHP
PHP自毁程序(慎用)
Jul 09 PHP
Laravel 中获取上一篇和下一篇数据
Jul 27 PHP
thinkphp多表查询两表有重复相同字段的完美解决方法
Sep 22 PHP
Yii2 加载css、js 载静态资源的方法
Mar 10 PHP
详解Yii2高级版引入bootstrap.js的一个办法
Mar 21 PHP
laravel如何开启跨域功能示例详解
Aug 31 PHP
Laravel 5.5 实现禁用用户注册示例
Oct 24 PHP
PHP的图像处理实例小结【文字水印、图片水印、压缩图像等】
Dec 20 PHP
PHP对接阿里云虚拟号的实现(号码隐私保护)
Apr 06 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
西德产收音机
2021/03/01 无线电
php 中的str_replace 函数总结
2007/04/27 PHP
10 个经典PHP函数
2013/10/17 PHP
PHP文件上传类实例详解
2016/04/08 PHP
Yii2.0 模态弹出框+ajax提交表单
2016/05/22 PHP
PHP 7安装调试工具Xdebug扩展的方法教程
2017/06/17 PHP
基于jquery插件实现常见的幻灯片效果
2013/11/01 Javascript
文档对象模型DOM通俗讲解
2013/11/01 Javascript
jQuery取得select选择的文本与值的示例
2013/12/09 Javascript
javascript中CheckBox全选终极方案
2015/05/20 Javascript
jQuery幻灯片带缩略图轮播效果代码分享
2015/08/17 Javascript
javascript判断复选框是否选中的方法
2015/10/16 Javascript
vue2.0开发实践总结之入门篇
2016/12/06 Javascript
JS中如何实现复选框全选功能
2016/12/19 Javascript
微信小程序(三):网络请求
2017/01/13 Javascript
Vue 2.0在IE11中打开项目页面空白的问题解决
2017/07/16 Javascript
AngularJs ng-change事件/指令的用法小结
2017/11/01 Javascript
NodeJs form-data格式传输文件的方法
2017/12/13 NodeJs
150行Node.js实现的dns代理工具
2019/08/02 Javascript
vue excel上传预览和table内容下载到excel文件中
2019/12/10 Javascript
js实现文字头像的生成代码
2020/03/07 Javascript
Vue项目配置跨域访问和代理proxy设置方式
2020/09/08 Javascript
[01:49]一目了然!DOTA2DotA快捷操作对比第二弹
2014/05/16 DOTA
[03:57]DOTA2英雄梦之声_第03期_幻影刺客
2014/06/21 DOTA
在Python中分别打印列表中的每一个元素方法
2018/11/07 Python
python读取当前目录下的CSV文件数据
2020/03/11 Python
捷克电器和DJ设备网上商店:Electronic-star
2017/07/18 全球购物
Notino希腊:购买香水和美容产品
2019/07/25 全球购物
如何写自我评价?自我评价写什么好?
2014/03/14 职场文书
秘书英文求职信
2014/04/16 职场文书
人力资源管理求职信
2014/08/07 职场文书
党的群众路线专项整治方案
2014/11/03 职场文书
出生公证书
2015/01/23 职场文书
工程部部长岗位职责
2015/02/12 职场文书
2015年业务工作总结范文
2015/04/10 职场文书
2015年新农村建设指导员工作总结
2015/07/24 职场文书