从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中jsonp的跨域实例
Jun 21 PHP
php获取从百度搜索进入网站的关键词的详细代码
Jan 08 PHP
PHP使用range协议实现输出文件断点续传代码实例
Jul 04 PHP
学习php中的正则表达式
Aug 17 PHP
PHP实现取得HTTP请求的原文
Aug 18 PHP
PIGCMS 如何关闭聊天机器人
Feb 12 PHP
PHP实现合并discuz用户
Aug 05 PHP
PHP构造二叉树算法示例
Jun 21 PHP
Ajax中的JSON格式与php传输过程全面解析
Nov 14 PHP
php + ajax 实现的写入数据库操作简单示例
May 16 PHP
php中yii框架实例用法
Dec 22 PHP
详解PHP服务器如何在有限的资源里最大提升并发能力
May 25 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 5.0对象模型深度探索之类的静态成员
2008/03/27 PHP
JS 面向对象的5钟写法
2009/07/31 Javascript
基于jQuery的message插件实现右下角弹出消息框
2011/01/11 Javascript
jQuery参数列表集合
2011/04/06 Javascript
ajax中get和post的说明及使用与区别
2012/12/23 Javascript
JS获取图片实际宽高及根据图片大小进行自适应
2013/08/11 Javascript
jquery与js函数冲突的两种解决方法
2013/09/09 Javascript
jquery遍历select元素(实例讲解)
2013/12/31 Javascript
jquery实现漫天雪花飞舞的圣诞祝福雪花效果代码分享
2015/08/20 Javascript
基于javascript如何传递特殊字符
2015/11/30 Javascript
基于JS实现数字+字母+中文的混合排序方法
2016/06/06 Javascript
浅谈JQuery+ajax+jsonp 跨域访问
2016/06/25 Javascript
jQuery中实现prop()函数控制多选框(全选,反选)
2016/08/19 Javascript
jQuery判断是否存在滚动条的简单方法
2016/09/17 Javascript
angularjs中使用ng-bind-html和ng-include的实例
2017/04/28 Javascript
原生JavaScrpit中异步请求Ajax实现方法
2017/11/03 Javascript
JS实现的JSON序列化操作简单示例
2018/07/02 Javascript
mpvue项目中使用第三方UI组件库的方法
2018/09/30 Javascript
JS基于ES6新特性async await进行异步处理操作示例
2019/02/02 Javascript
微信小程序生成海报分享朋友圈的实现方法
2019/05/06 Javascript
js实现随机点名功能
2020/12/23 Javascript
Python3中的真除和Floor除法用法分析
2016/03/16 Python
用python处理图片实现图像中的像素访问
2018/05/04 Python
利用python循环创建多个文件的方法
2018/10/25 Python
浅谈python写入大量文件的问题
2018/11/09 Python
python实现大文本文件分割
2019/07/22 Python
Python 异常处理Ⅳ过程图解
2019/10/18 Python
Django中使用Celery的方法步骤
2020/12/07 Python
英国网上电器商店:Electricshop
2020/03/15 全球购物
奥地利手表、香水、化妆品和珠宝购物网站:Brasty.at
2021/01/17 全球购物
软件测试英文面试题
2012/10/14 面试题
中西医结合临床医学专业大学生自荐信
2013/09/28 职场文书
幼儿园保育员辞职信
2014/01/12 职场文书
医院信息公开实施方案
2014/05/09 职场文书
小学班级标语口号大全
2015/12/26 职场文书
2016年优秀教师先进事迹材料
2016/02/26 职场文书