从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 相关文章推荐
wiki-shan写的php在线加密的解密程序
Sep 07 PHP
中英文字符串翻转函数
Dec 09 PHP
php 获取一个月第一天与最后一天的代码
May 16 PHP
PHP学习之字符串比较和查找
Apr 17 PHP
PHP常用特殊运算符号和函数总结(php新手入门必看)
Feb 02 PHP
phpmailer发送邮件之后,返回收件人是否阅读了邮件的方法
Jul 19 PHP
php中动态修改ini配置
Oct 14 PHP
制作个性化的WordPress登陆界面的实例教程
May 21 PHP
PHP邮箱验证示例教程
Jun 01 PHP
php json相关函数用法示例
Mar 28 PHP
php判断数组是否为空的实例方法
May 10 PHP
解决PHPstudy Apache无法启动的问题【亲测有效】
Oct 30 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中call_user_func_array()函数的用法演示
2012/02/05 PHP
php在程序中将网页生成word文档并提供下载的代码
2012/10/09 PHP
深入PHP与浏览器缓存的分析
2013/06/03 PHP
php操作csv文件代码实例汇总
2014/09/22 PHP
PHP批量去除BOM头代码分享
2015/06/26 PHP
php实现微信公众号无限群发
2015/10/11 PHP
Ubuntu 16.04中Laravel5.4升级到5.6的步骤
2018/12/07 PHP
Yii2框架加载css和js文件的方法分析
2019/05/25 PHP
表单(FORM)的一些实用效果代码
2007/03/25 Javascript
原生javascript图片自动或手动切换示例附演示源码
2013/09/04 Javascript
异步动态加载JS并运行(示例代码)
2013/12/13 Javascript
JQuery boxy插件在IE中边角图片不显示问题的解决
2015/05/20 Javascript
浏览器环境下JavaScript脚本加载与执行探析之defer与async特性
2016/01/14 Javascript
bootstrap是什么_动力节点Java学院整理
2017/07/14 Javascript
AngularJS中filter的使用实例详解
2017/08/25 Javascript
vue学习之mintui picker选择器实现省市二级联动示例
2017/10/12 Javascript
微信小程序购物车、父子组件传值及calc的注意事项总结
2018/11/14 Javascript
js实现轮播图效果 z-index实现轮播图
2020/01/17 Javascript
[44:50]DOTA2上海特级锦标赛B组小组赛#2 VG VS Fnatic第二局
2016/02/26 DOTA
[48:22]VGJ.S vs VG 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
简单的Apache+FastCGI+Django配置指南
2015/07/22 Python
python 限制函数调用次数的实例讲解
2018/04/21 Python
Python minidom模块用法示例【DOM写入和解析XML】
2019/03/25 Python
Python编写打字训练小程序
2019/09/26 Python
详解HTML5表单新增属性
2016/12/21 HTML / CSS
波兰最大的宠物用品网上商店:FERA.PL
2019/08/11 全球购物
执行总经理岗位职责
2014/02/03 职场文书
婚纱摄影师求职信
2014/03/07 职场文书
移风易俗倡议书
2014/04/15 职场文书
会计专业自荐书
2014/07/08 职场文书
2014年酒店年度工作总结
2014/12/10 职场文书
考研英语辞职信
2015/05/13 职场文书
办公室管理规章制度
2015/08/04 职场文书
pandas中DataFrame数据合并连接(merge、join、concat)
2021/05/30 Python
python 多态 协议 鸭子类型详解
2021/11/27 Python
一文教你快速生成MySQL数据库关系图
2022/06/28 Redis