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 相关文章推荐
解决phpmyadmin 乱码,支持gb2312和utf-8
Nov 20 PHP
php中使用cookie来保存用户登录信息的实现代码
Mar 08 PHP
解析关于wamp启动是80端口被占用的问题
Jun 21 PHP
ThinkPHP模板中判断volist循环的最后一条记录的验证方法
Jul 01 PHP
PHP实现将科学计数法转换为原始数字字符串的方法
Dec 16 PHP
php去除html标记的原生函数详解
Jan 27 PHP
thinkPHP中多维数组的遍历方法
Jan 09 PHP
PHP面向对象程序设计之类与反射API详解
Dec 02 PHP
magento后台无法登录解决办法的两种方法
Dec 09 PHP
PHP实现数组根据某个单元字段排序操作示例
Aug 01 PHP
ThinkPHP5.1+Ajax实现的无刷新分页功能示例
Feb 10 PHP
YII2框架中添加自定义模块的方法实例分析
Mar 18 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
php下获取http状态的实现代码
2014/05/09 PHP
实例分析基于PHP微信网页获取用户信息
2017/11/24 PHP
Prototype 1.5.0_rc1 及 Prototype 1.5.0 Pre0小抄本
2006/09/22 Javascript
JavaScript写的一个DIV 弹出网页对话框
2009/08/14 Javascript
IE和firefox浏览器的event事件兼容性汇总
2009/12/06 Javascript
JAVASCRIPT车架号识别/验证函数代码 汽车车架号验证程序
2012/01/08 Javascript
jQuery用unbind方法去掉hover事件及其他方法介绍
2013/03/18 Javascript
当鼠标滑过文本框自动选中输入框内容的JS代码分享
2013/11/26 Javascript
jquery中radio checked问题
2015/03/16 Javascript
每天一篇javascript学习小结(基础知识)
2015/11/10 Javascript
JavaScript 继承详解(六)
2016/10/11 Javascript
详解ElementUI之表单验证、数据绑定、路由跳转
2017/06/21 Javascript
Vue-Router实现组件间跳转的三种方法
2017/11/07 Javascript
解决vue2.0路由跳转未匹配相应用路由避免出现空白页面的问题
2018/08/24 Javascript
微信小程序实现拍照画布指定区域生成图片
2019/07/18 Javascript
微信小程序如何获取地址
2019/12/24 Javascript
栈和队列数据结构的基本概念及其相关的Python实现
2015/08/24 Python
使用实现pandas读取csv文件指定的前几行
2018/04/20 Python
用Python识别人脸,人种等各种信息
2019/07/15 Python
Python 获取命令行参数内容及参数个数的实例
2019/12/20 Python
python GUI库图形界面开发之PyQt5单行文本框控件QLineEdit详细使用方法与实例
2020/02/27 Python
pymysql 插入数据 转义处理方式
2020/03/02 Python
python filecmp.dircmp实现递归比对两个目录的方法
2020/05/22 Python
python代码实现将列表中重复元素之间的内容全部滤除
2020/05/22 Python
Pycharm-community-2020.2.3 社区版安装教程图文详解
2020/12/08 Python
Python使用Opencv实现边缘检测以及轮廓检测的实现
2020/12/31 Python
加拿大大码女装购物网站:Penningtons
2020/12/26 全球购物
What's the difference between deep copy and shallow copy? (深拷贝与浅拷贝有什么区别)
2015/11/10 面试题
会计专业职业规划:规划自我赢取未来
2014/02/12 职场文书
医院保洁服务方案
2014/06/11 职场文书
上海世博会口号
2014/06/19 职场文书
项目投资合作意向书
2014/07/29 职场文书
酒店仓管员岗位职责
2015/04/01 职场文书
创业计划书之家教中心
2019/09/25 职场文书
导游词之韩国济州岛
2019/10/28 职场文书
Nginx stream 配置代理(Nginx TCP/UDP 负载均衡)
2021/11/17 Servers