从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制作简单的内容采集器的代码
Nov 28 PHP
修改Zend引擎实现PHP源码加密的原理及实践
Apr 14 PHP
php 无限分类的树类代码
Dec 03 PHP
PHP为表单获取的URL 地址预设 http 字符串函数代码
May 26 PHP
php strstr查找字符串中是否包含某些字符的查找函数
Jun 03 PHP
php中$_POST与php://input的区别实例分析
Jan 07 PHP
Smarty变量用法详解
May 11 PHP
Thinkphp实现短信验证注册功能
Oct 18 PHP
Yii2 中实现单点登录的方法
Mar 09 PHP
PHP中常见的密码处理方式和建议总结
Oct 14 PHP
PHP扩展Swoole实现实时异步任务队列示例
Apr 13 PHP
php5对象复制、clone、浅复制与深复制实例详解
Aug 14 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中使用模板的方法
2008/05/24 PHP
php适配器模式介绍
2012/08/14 PHP
测试PHP连接MYSQL成功与否的代码
2013/08/16 PHP
PHP实现无限极分类图文教程
2014/11/25 PHP
使用GDB调试PHP代码,解决PHP代码死循环问题
2015/03/02 PHP
PHP SPL标准库之数据结构栈(SplStack)介绍
2015/05/12 PHP
php实现的网络相册图片防盗链完美破解方法
2015/07/01 PHP
php文件上传的两种实现方法
2016/04/04 PHP
jq选项卡鼠标延迟的插件实例
2013/05/13 Javascript
js 实现浏览历史记录示例
2014/04/20 Javascript
轻松创建nodejs服务器(7):阻塞操作的实现
2014/12/18 NodeJs
js无法获取到html标签的属性的解决方法
2016/07/26 Javascript
详解如何使用Node.js编写命令工具——以vue-cli为例
2017/06/29 Javascript
利用nvm管理多个版本的node.js与npm详解
2017/11/02 Javascript
JavaScript满天星导航栏实现方法
2018/03/08 Javascript
vue 监听键盘回车事件详解 @keyup.enter || @keyup.enter.native
2018/08/25 Javascript
小程序实现列表删除功能
2018/10/30 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
React路由鉴权的实现方法
2019/09/05 Javascript
jQuery实现数字华容道小游戏(实例代码)
2020/01/16 jQuery
把MySQL表结构映射为Python中的对象的教程
2015/04/07 Python
Python模块搜索概念介绍及模块安装方法介绍
2015/06/03 Python
Python判断值是否在list或set中的性能对比分析
2016/04/16 Python
Python文件夹与文件的相关操作(推荐)
2016/07/25 Python
解决csv.writer写入文件有多余的空行问题
2018/07/06 Python
Python如何在windows环境安装pip及rarfile
2020/06/15 Python
卸载tensorflow-cpu重装tensorflow-gpu操作
2020/06/23 Python
一个非常简单好用的Python图形界面库(PysimpleGUI)
2020/12/28 Python
ToysRus日本官网:玩具反斗城
2018/09/08 全球购物
新西兰廉价汽车租赁:Snap Rentals
2018/09/14 全球购物
建筑工程毕业生自我鉴定
2014/01/14 职场文书
前厅收银主管岗位职责
2014/02/04 职场文书
应届毕业生自荐信例文
2014/02/26 职场文书
2014年创先争优活动总结
2014/05/04 职场文书
社区党员志愿服务活动方案
2014/08/18 职场文书
2016年妇联“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书