PHP原理之异常机制深入分析


Posted in PHP onAugust 08, 2010

PHP的异常机制的原理是什么?

在PHP每一个可独立执行的op array最后的ZEND_HANDLE_EXCEPTION是用来干什么呢?

让我们从一个问题说起, 上周的时候, blue5tar提了一个问题:”对于下面的代码, onError明明执行了, 但是onException却没有执行, 为什么?”.

<?php 
function onError($errCode, $errMesg, $errFile, $errLine) { 
echo "Error Occurred\n"; 
throw new Exception($errMesg); 
} function onException($e) { 
echo $e->getMessage(); 
} 
set_error_handler("onError"); 
set_exception_handler("onException"); 
/* 我从不会以我的名字命名文件, 所以这个文件不存在 */ 
require("laruence.php");

运行结果:
Error Occurred 
PHP Fatal error: main(): Failed opening required 'laruence.php

首先, 我们要知道, Require在包含一个找不到的问题的时候, 会前后抛出俩个错误,
1. WARNING : 在PHP试图打开这个文件的时候抛出. 
2. E_COMPILE_ERROR : 从PHP打开文件的函数返回失败以后抛出

而我们知道, set_error_handler是不能捕获E_COMPILE_ERROR错误的:

The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called.

所以, 在onError中, 只能捕获到第一个WARNING错误, 而在onError中抛出的异常, 为什么没有被默认exception_handler捕获呢?

这就要说说PHP的异常机制了.

了解opcode(深入理解PHP原理之Opcodes的同学都知道, 在PHP5.3以前, 每一个可独立运行的op array(文件, 函数, 方法)的最后一条opcode都是ZEND_HANDLE_EXCEPTION, 而这个opcode是做什么用的呢?

原来在PHP中, 当有异常被throw的时候, 会跳到每一个op array的最后一行, 来执行这条ZEND_HANDLE_EXCEPTION, 伪码如下:

void on_throw_exception(zval *exception TSRMLS_DC) { 
1. 判断是否已经有异常抛出 
2. 记录exception 
3. 记录下一条要执行的op line的序号 
4. 下一条要执行的op line序号 = 当前op array的最后一条 
}

恩, 就和改写ip寄存器一样, 改写下一条要执行的op line的序号, 就改变了程序的流向, 这样, 就会进入到了ZEND_HANDLE_EXCEPTION的处理逻辑中.

而在ZEND_HANDLE_EXCEPTION中, 会判断这个异常是否在try catch中,

如果是 则把下一条要执行的op line, 置为第一个catch的op line, 并继续执行. 
如果不是 则销毁一些不需要的变量, 和opline, 然后直接结束执行过程

有的同学要问了:”那set_exception_handler设置的异常默认处理函数(user_exception_handler)什么时候起作用呢?”

恩, 是在执行完成退出执行LOOP以后才判断是否有默认异常处理函数, 如果有才调用:

//执行 
zend_execute(EG(active_op_array) TSRMLS_CC); 
if (EG(exception)) { 
if (EG(user_exception_handler)) { 
调用用户定义的默认异常处理函数 
} else { 
未捕获的异常 
} 
} else { 
没有异常 
} 
destroy_op_array(EG(active_op_array) TSRMLS_CC); 
efree(EG(active_op_array));

PHP原理之异常机制深入分析
PHP异常流程
而PHP在遇到Fatal Error的时候, 会直接zend_bailout, 而zend_bailout会导致程序流程直接跳过上面代码段, 也可以理解为直接exit了(longjmp), 这就导致了user_exception_handler没有机会发生作用.

了解到这些, 我想文章开头的问题的为什么? 也就很清晰了吧?

最后, 关于ZEND_HANDLE_EXCEPTION, 也许有同学会有疑问: 如果是这样, 那为什么每一个可独立执行的op array最后都有这个ZEND_HANDLE_EXCEPTION呢? 最简单的, 如果一个函数中不会throw, 那么这个opcode 是明显不需要的啊? 嘿嘿, 你很聪明, PHP 5.3开始, 已经按照你的想法调整了.. 只有在throw时刻, 才会动态的生成ZEND_HANDLE_EXCEPTION opline.

PHP5 changelog:

Changed exception handling. Now each op_array doesn't contain ZEND_HANDLE_EXCEPTION opcode in the end. (Dmitry)

PHP 相关文章推荐
php中支持多种编码的中文字符串截取函数!
Mar 20 PHP
自动把纯文本转换成Web页面的php代码
Aug 27 PHP
php学习笔记 [预定义数组(超全局数组)]
Jun 09 PHP
PHP封装的一个支持HTML、JS、PHP重定向的多功能跳转函数
Jun 19 PHP
PHP整合七牛实现上传文件
Jul 03 PHP
PHP+AJAX实现投票功能的方法
Sep 28 PHP
调试WordPress中定时任务的相关PHP脚本示例
Dec 10 PHP
iOS10推送通知开发教程
Sep 19 PHP
使用PHP连接多种数据库的实现代码(mysql,access,sqlserver,Oracle)
Dec 21 PHP
PHP实现在windows下配置sendmail并通过mail()函数发送邮件的方法
Jun 20 PHP
Laravel解决nesting level错误和隐藏index.php的问题
Oct 12 PHP
php优化查询foreach代码实例讲解
Mar 24 PHP
php include加载文件两种方式效率比较
Aug 08 #PHP
将一维或多维的数组连接成一个字符串的php代码
Aug 08 #PHP
php简单提示框alert封装函数
Aug 08 #PHP
php递归实现无限分类生成下拉列表的函数
Aug 08 #PHP
php用数组返回无限分类的列表数据的代码
Aug 08 #PHP
php自定义函数之递归删除文件及目录
Aug 08 #PHP
php自动获取目录下的模板的代码
Aug 08 #PHP
You might like
Eclipse中php插件安装及Xdebug配置的使用详解
2013/04/25 PHP
PHP中调用C/C++制作的动态链接库的教程
2016/03/10 PHP
PHP常见字符串操作函数与用法总结
2019/03/04 PHP
php使用filter_var函数判断邮箱,url,ip格式示例
2019/07/06 PHP
jQuery简单实现网页选项卡特效
2014/11/24 Javascript
Nodejs学习笔记之测试驱动
2015/04/16 NodeJs
jquery预加载图片的方法
2015/05/27 Javascript
JavaScript的removeChild()函数用法详解
2015/12/27 Javascript
jquery插件jquery.dragscale.js实现拖拽改变元素大小的方法(附demo源码下载)
2016/02/25 Javascript
深入理解逻辑表达式的用法 与或非的用法
2016/06/06 Javascript
JavaScript必知必会(二) null 和undefined
2016/06/08 Javascript
通过Ajax使用FormData对象无刷新上传文件方法
2016/12/08 Javascript
简单实现js菜单栏切换效果
2017/03/04 Javascript
webpack2.0配置postcss-loader的方法
2017/08/17 Javascript
vue中关闭eslint的方法分析
2018/08/04 Javascript
vue 纯js监听滚动条到底部的实例讲解
2018/09/03 Javascript
webpack 如何同时输出压缩和未压缩的文件的实现步骤
2020/06/05 Javascript
vant时间控件使用方法详解
2020/12/24 Javascript
[04:14]从西雅图到上海——玩家自制DOTA2主题歌曲应援TI9
2019/07/11 DOTA
Python中for循环和while循环的基本使用方法
2015/08/21 Python
使用python检测主机存活端口及检查存活主机
2015/10/12 Python
机器学习经典算法-logistic回归代码详解
2017/12/22 Python
Python+selenium实现截图图片并保存截取的图片
2018/01/05 Python
使用python itchat包爬取微信好友头像形成矩形头像集的方法
2019/02/21 Python
python 实现查找文件并输出满足某一条件的数据项方法
2019/06/12 Python
解决Pycharm的项目目录突然消失的问题
2020/01/20 Python
python tkinter之 复选、文本、下拉的实现
2020/03/04 Python
Pycharm 跳转回之前所在页面的操作
2021/02/05 Python
英国护肤品购物网站:Beauty Expert
2016/08/19 全球购物
土耳其时尚购物网站:Morhipo
2017/09/04 全球购物
Currentbody德国站:健康与美容技术专家
2020/04/05 全球购物
大三学生入党思想汇报
2014/01/02 职场文书
婚假请假条怎么写
2014/04/10 职场文书
无财产无子女离婚协议书范文
2014/09/14 职场文书
出纳试用期工作总结2015
2015/05/28 职场文书
Python Django搭建文件下载服务器的实现
2021/05/10 Python