深入理解PHP原理之异常机制


Posted in PHP onAugust 21, 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在包含一个找不到的问题的时候, 会前后抛出俩个错误,
WARNING : 在PHP试图打开这个文件的时候抛出. 
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异常流程

注: 图中有一处不严谨, 即在确定是否最后一个catch块的时候, 会同时判断(is_a), 如果是才进入最后一个catch块执行.
而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 相关文章推荐
用cookies来跟踪识别用户
Oct 09 PHP
Discuz!5的PHP代码高亮显示插件(黑暗中的舞者更新)
Jan 29 PHP
php session 检测和注销
Mar 16 PHP
QQ登录 PHP OAuth示例代码
Jul 20 PHP
PHP图片等比例缩放生成缩略图函数分享
Jun 10 PHP
Discuz7.2版的faq.php SQL注入漏洞分析
Aug 06 PHP
高质量PHP代码的50个实用技巧必备(下)
Jan 22 PHP
PHP串行化与反串行化实例分析
Dec 27 PHP
Zend Framework数据库操作技巧总结
Feb 18 PHP
ThinkPHP删除栏目(实现批量删除栏目)
Jun 21 PHP
php之header的不同用法总结(实例讲解)
Nov 28 PHP
Laravel5框架添加自定义辅助函数的方法
Aug 01 PHP
php中var_export与var_dump的区别分析
Aug 21 #PHP
php visitFile()遍历指定文件夹函数
Aug 21 #PHP
php excel类 phpExcel使用方法介绍
Aug 21 #PHP
php下正则来匹配dede模板标签的代码
Aug 21 #PHP
php中使用ExcelFileParser处理excel获得数据(可作批量导入到数据库使用)
Aug 21 #PHP
使用VisualStudio开发php的图文设置方法
Aug 21 #PHP
利用Memcached在php下实现session机制 替换PHP的原生session支持
Aug 21 #PHP
You might like
php 保留字列表
2012/10/04 PHP
常用PHP数组排序函数归纳
2016/08/08 PHP
dojo 之基础篇(二)之从服务器读取数据
2007/03/24 Javascript
Javascript与flash交互通信基础教程
2008/08/07 Javascript
JavaScript 学习技巧
2010/02/17 Javascript
JavaScript 笔记二 Array和Date对象方法
2010/05/22 Javascript
使用jquery与图片美化checkbox和radio控件的代码(打包下载)
2010/11/11 Javascript
jquery中交替点击事件toggle方法的使用示例
2013/12/08 Javascript
jQuery中append()方法用法实例
2014/12/25 Javascript
JS实现CheckBox复选框全选全不选功能
2015/05/06 Javascript
跟我学习javascript的浮点数精度
2015/11/16 Javascript
JavaScript事件学习小结(五)js中事件类型之鼠标事件
2016/06/09 Javascript
Javascript中浏览器窗口的基本操作总结
2016/08/18 Javascript
浅谈js的ajax的异步和同步请求的问题
2016/10/07 Javascript
让html元素随浏览器的大小自适应垂直居中的实现方法
2016/10/12 Javascript
JS小数转换为整数的方法分析
2017/01/07 Javascript
jQuery的中 is(':visible') 解析及用法(必看)
2017/02/12 Javascript
JS对象序列化成json数据和json数据转化为JS对象的代码
2017/08/23 Javascript
Node.js自定义实现文件路由功能
2017/09/22 Javascript
vue 自定义指令自动获取文本框焦点的方法
2018/08/25 Javascript
Node.js中读取TXT文件内容fs.readFile()用法
2018/10/10 Javascript
vue 的 solt 子组件过滤过程解析
2019/09/07 Javascript
layui table动态表头 改变表格头部 重新加载表格的方法
2019/09/21 Javascript
vue仿ios列表左划删除
2019/09/26 Javascript
Python对小数进行除法运算的正确方法示例
2014/08/25 Python
详细解析Python当中的数据类型和变量
2015/04/25 Python
Python实现爬取百度贴吧帖子所有楼层图片的爬虫示例
2018/04/26 Python
基于python实现微信好友数据分析(简单)
2020/02/16 Python
html5中svg canvas和图片之间相互转化思路代码
2014/01/24 HTML / CSS
美国相机和电子产品零售商:Beach Camera
2020/11/26 全球购物
幼儿园中秋节活动方案2013
2014/01/29 职场文书
优秀教师申报材料
2014/12/16 职场文书
导游词之太湖
2019/10/08 职场文书
CSS3 Tab动画实例之背景切换动态效果
2021/08/23 HTML / CSS
Python torch.flatten()函数案例详解
2021/08/30 Python
mysql事务隔离级别详情
2021/10/24 MySQL