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 相关文章推荐
mysql中存储过程、函数的一些问题
Feb 14 PHP
支持php4、php5的mysql数据库操作类
Jan 10 PHP
使用php+apc实现上传进度条且在IE7下不显示的问题解决方法
Apr 25 PHP
深入php处理整数函数的详解
Jun 09 PHP
通过PHP current函数获取未知字符键名数组第一个元素的值
Jun 24 PHP
php获取字符串中各个字符出现次数的方法
Feb 23 PHP
php生成过去100年下拉列表的方法
Jul 20 PHP
PHP使用PHPexcel导入导出数据的方法
Nov 14 PHP
PHP线程的内存回收问题
Jul 08 PHP
PHP7新增运算符用法实例分析
Sep 26 PHP
PHP判断文件是否被引入的方法get_included_files用法示例
Nov 29 PHP
PHP的mysqli_stat()函数讲解
Jan 23 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入门源程序
2006/10/09 PHP
用PHP代码在网页上生成图片
2015/07/01 PHP
php注册系统和使用Xajax即时验证用户名是否被占用
2017/08/31 PHP
解决laravel(5.5)访问public报错的问题
2019/10/12 PHP
Javascript-Mozilla和IE中的一个函数直接量的问题
2007/01/09 Javascript
读jQuery之五(取DOM元素)
2011/06/20 Javascript
Windows系统下Node.js的简单入门教程
2015/06/23 Javascript
JS实现自动变化的导航菜单效果代码
2015/09/09 Javascript
javascript设计简单的秒表计时器
2020/09/05 Javascript
Node.js静态文件服务器改进版
2016/01/10 Javascript
基于jquery实现图片放大功能
2016/05/07 Javascript
基于MVC5和Bootstrap的jQuery TreeView树形控件(二)之数据支持json字符串、list集合
2016/08/11 Javascript
bootstrap模态框消失问题的解决方法
2016/12/02 Javascript
Reactjs实现通用分页组件的实例代码
2017/01/19 Javascript
Node.js爬取豆瓣数据实例分析
2018/03/05 Javascript
解决vue.js this.$router.push无效的问题
2018/09/03 Javascript
JavaScript常见事件处理程序实例总结
2019/01/05 Javascript
微信提示 在浏览器打开 效果实现过程解析
2019/09/10 Javascript
layui富文本编辑器前端无法取值的解决方法
2019/09/18 Javascript
django框架如何集成celery进行开发
2017/05/24 Python
基于数据归一化以及Python实现方式
2018/07/11 Python
django settings.py 配置文件及介绍
2019/07/15 Python
python PIL和CV对 图片的读取,显示,裁剪,保存实现方法
2019/08/07 Python
python并发编程 Process对象的其他属性方法join方法详解
2019/08/20 Python
Python字符串和正则表达式中的反斜杠('\')问题详解
2019/09/03 Python
Python中Pyspider爬虫框架的基本使用详解
2021/01/27 Python
Cynthia Rowley官网:全球领先的生活方式品牌
2020/10/27 全球购物
"序列点" 是什么
2016/07/29 面试题
如何将一个描述日期或日期/时间的字符串转换为一个Date对象
2015/10/13 面试题
超级搞笑检讨书
2014/01/15 职场文书
大学同学聚会邀请函
2014/01/19 职场文书
机关会计岗位职责
2014/04/08 职场文书
产品设计开发计划书
2014/05/07 职场文书
2019年手机市场的调研报告2篇
2019/10/10 职场文书
基于go interface{}==nil 的几种坑及原理分析
2021/04/24 Golang
CSS3实现的侧滑菜单
2021/04/27 HTML / CSS