从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 项目的方法
Jan 02 PHP
中国站长站 For Dede4.0 采集规则
May 27 PHP
PHP获取网站域名和地址的代码
Aug 17 PHP
PHP+Ajax异步通讯实现用户名邮箱验证是否已注册( 2种方法实现)
Dec 28 PHP
基于PHP+Ajax实现表单验证的详解
Jun 25 PHP
19个超实用的PHP代码片段
Mar 14 PHP
PHP的拦截器实例分析
Nov 03 PHP
php cli模式下获取参数的方法
May 05 PHP
php单元测试phpunit入门实例教程
Nov 17 PHP
Ecshop 后台添加新功能栏目及管理权限设置教程
Nov 21 PHP
php使用scandir()函数扫描指定目录下所有文件示例
Jun 08 PHP
PHP使用Session实现上传进度功能详解
Aug 06 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生成excel时单元格内换行问题的解决方法
2010/08/26 PHP
跟我学Laravel之请求(Request)的生命周期
2014/10/15 PHP
在win系统安装配置 Memcached for PHP 5.3 图文教程
2015/03/03 PHP
用php代码限制国内IP访问我们网站
2015/09/26 PHP
学习php设计模式 php实现命令模式(command)
2015/12/08 PHP
Thinkphp单字母函数使用指南
2016/05/08 PHP
你可能不再需要JQUERY
2021/03/09 Javascript
jQuery UI的Dialog无法提交问题的解决方法
2011/01/11 Javascript
将nodejs打包工具整合到鼠标右键的方法
2013/05/11 NodeJs
下拉列表选择项的选中在不同浏览器中的兼容性问题探讨
2013/09/18 Javascript
多种方法判断Javascript对象是否存在
2013/09/22 Javascript
angularJS实现不同视图同步刷新详解
2018/10/09 Javascript
axios实现简单文件上传功能
2019/09/25 Javascript
Vue.js watch监视属性知识点总结
2019/11/11 Javascript
Vue.js仿Select下拉框效果
2020/02/18 Javascript
JavaScript实现轮播图片完整代码
2020/03/07 Javascript
[03:01]完美盛典趣味短片 DOTA2年度最佳&拉胯英雄
2019/12/07 DOTA
pip 错误unused-command-line-argument-hard-error-in-future解决办法
2014/06/01 Python
Python中的元类编程入门指引
2015/04/15 Python
python获取从命令行输入数字的方法
2015/04/29 Python
python实现简单socket通信的方法
2016/04/19 Python
python魔法方法-属性访问控制详解
2016/07/25 Python
Python实现将数据框数据写入mongodb及mysql数据库的方法
2018/04/02 Python
python框架Django实战商城项目之工程搭建过程图文详解
2020/03/09 Python
Python加载数据的5种不同方式(收藏)
2020/11/13 Python
Python操作PostgreSql数据库的方法(基本的增删改查)
2020/12/29 Python
表单button的outline在firefox浏览器下的问题
2012/12/24 HTML / CSS
英国工具中心:UK Tool Centre
2017/07/10 全球购物
澳大利亚旅游网站:Lastminute
2017/08/07 全球购物
JACK & JONES英国官方网站:欧洲领先的男装生产商
2017/09/27 全球购物
商务主管岗位职责
2013/12/08 职场文书
请假条格式范文
2014/04/10 职场文书
本科生就业推荐信
2014/05/19 职场文书
毕业设计答辩开场白
2015/05/29 职场文书
详解分布式系统中如何用python实现Paxos
2021/05/18 Python
浅谈python数据类型及其操作
2021/05/25 Python