从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实现分页的一个示例
Oct 09 PHP
用PHP和ACCESS写聊天室(五)
Oct 09 PHP
一段php加密解密的代码
Jul 16 PHP
PHP下10件你也许并不了解的事情
Sep 11 PHP
PHP写的获取各搜索蜘蛛爬行记录代码
Aug 21 PHP
使用php判断服务器是否支持Gzip压缩功能
Sep 24 PHP
php判断对象是派生自哪个类的方法
Jun 20 PHP
AJAX PHP无刷新form表单提交的简单实现(推荐)
Sep 09 PHP
Laravel学习教程之View模块详解
Sep 18 PHP
实例讲解PHP验证邮箱是否合格
Jan 28 PHP
PHP如何将图片文件上传到另外一台服务器上
Aug 26 PHP
PHP编程一定要改掉的5个不良习惯
Sep 18 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
人尽可用的Windows技巧小贴士之下篇
2007/03/22 PHP
php通过COM类调用组件的实现代码
2012/01/11 PHP
thinkPHP分组后模板无法加载问题解决方法
2016/07/12 PHP
Open and Print a Word Document
2007/06/15 Javascript
解决使用attachEvent函数时,this指向被绑定的元素的问题的方法
2007/08/13 Javascript
input按钮的事件处理大全
2010/12/10 Javascript
jQuery图片的展开和收缩实现代码
2013/04/16 Javascript
使用JavaScript开发IE浏览器本地插件实例
2015/02/18 Javascript
在线引用最新jquery文件的实现方法
2016/08/26 Javascript
微信小程序 基础知识css样式media标签
2017/02/15 Javascript
JsonServer安装及启动过程图解
2020/02/28 Javascript
js 图片懒加载的实现
2020/10/21 Javascript
Python的Django框架使用入门指引
2015/04/15 Python
在Python程序中操作MySQL的基本方法
2015/07/29 Python
详解Python中 __get__和__getattr__和__getattribute__的区别
2016/06/16 Python
Python使用wxPython实现计算器
2018/01/30 Python
Python测试人员需要掌握的知识
2018/02/08 Python
Sanic框架路由用法实例分析
2018/07/16 Python
python通过链接抓取网站详解
2019/11/20 Python
浅谈Pycharm最有必要改的几个默认设置项
2020/02/14 Python
Django+Celery实现动态配置定时任务的方法示例
2020/05/26 Python
基于matplotlib中ion()和ioff()的使用详解
2020/06/16 Python
keras CNN卷积核可视化,热度图教程
2020/06/22 Python
详解使用canvas保存网页为pdf文件支持跨域
2018/11/23 HTML / CSS
美国在线奢侈品寄售商店:Luxury Garage Sale
2018/08/19 全球购物
财务经理的岗位职责
2013/12/17 职场文书
银行实习生的自我评价
2014/01/13 职场文书
房屋买卖协议书
2014/04/10 职场文书
爱国演讲稿400字
2014/05/07 职场文书
马丁路德金演讲稿
2014/05/19 职场文书
运动会口号大全
2014/06/07 职场文书
法定代表人授权委托书范本
2014/10/07 职场文书
2015年企业工作总结范文
2015/04/28 职场文书
2015年禁毒工作总结
2015/04/30 职场文书
Kubernetes部署实例并配置Deployment、网络映射、副本集
2022/04/01 Servers
Redis特殊数据类型Geospatial地理空间
2022/06/01 Redis