从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 相关文章推荐
优化NFR之一 --MSSQL Hello Buffer Overflow
Oct 09 PHP
我的论坛源代码(七)
Oct 09 PHP
php面向对象全攻略 (五) 封装性
Sep 30 PHP
PHP编程过程中需要了解的this,self,parent的区别
Dec 30 PHP
PHP正则提取不包含指定网址的图片地址的例子
Apr 21 PHP
PHP把空格、换行符、中文逗号等替换成英文逗号的正则表达式
May 04 PHP
php中unserialize返回false的解决方法
Sep 22 PHP
php一维二维数组键排序方法实例总结
Nov 13 PHP
php中smarty实现多模版网站的方法
Jun 11 PHP
yii2利用自带UploadedFile实现上传图片的示例
Feb 16 PHP
php正则表达式基本知识与应用详解【经典教程】
Apr 17 PHP
Ajax+PHP实现的删除数据功能示例
Feb 12 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数据库调用类调用实例(详细注释)
2012/07/12 PHP
PHP中echo,print_r与var_dump区别分析
2014/09/29 PHP
PHP匿名函数和use子句用法实例
2016/03/16 PHP
PHP与Java对比学习日期时间函数
2016/07/03 PHP
php die()与exit()的区别实例详解
2016/12/03 PHP
PHP文件操作简单介绍及函数汇总
2020/12/11 PHP
js 学习笔记(三)
2009/12/29 Javascript
20个非常棒的Jquery实用工具 国外文章
2010/01/01 Javascript
JavaScript ( (__ = !$ + $)[+$] + ({} + $)[_/_] +({} + $)[_/_] )
2011/02/25 Javascript
jQuery 源码分析笔记(6) jQuery.data
2011/06/08 Javascript
IE6-IE9不支持table.innerHTML的解决方法分享
2012/09/14 Javascript
改变隐藏的input中value的值代码
2013/12/30 Javascript
js返回上一页并刷新的多种实现方法
2014/02/26 Javascript
使用jquery实现以post打开新窗口
2014/03/19 Javascript
JS判断字符串包含的方法
2015/05/05 Javascript
JavaScript实现强制重定向至HTTPS页面
2015/06/10 Javascript
javascript+HTML5 Canvas绘制转盘抽奖
2020/05/16 Javascript
Javascript从数组中随机取出不同元素的两种方法
2016/09/22 Javascript
详解Javascript几种跨域方式总结
2017/02/27 Javascript
小程序云函数调用API接口的方法
2019/05/17 Javascript
jQuery插件simplePagination的使用方法示例
2020/04/28 jQuery
在vscode中配置python环境过程解析
2019/09/28 Python
详解Pycharm与anaconda安装配置指南
2020/08/25 Python
pycharm专业版远程登录服务器的详细教程
2020/09/15 Python
CSS3轻松实现圆角效果
2017/11/09 HTML / CSS
分享CSS3制作卡片式图片的方法
2016/07/08 HTML / CSS
css3 flex布局 justify-content:space-between 最后一行左对齐
2020/01/02 HTML / CSS
CK美国官网:Calvin Klein
2016/08/26 全球购物
教师简历自我评价
2014/02/03 职场文书
工会优秀工作者事迹
2014/08/17 职场文书
党员自我评议对照检查材料
2014/09/27 职场文书
2014年电工工作总结
2014/11/20 职场文书
2015年幼儿园元旦游艺活动策划书
2014/12/09 职场文书
保护环境建议书作文400字
2015/09/14 职场文书
Python办公自动化之Excel(中)
2021/05/24 Python
基于CSS制作创意端午节专属加载特效
2022/06/01 HTML / CSS