深入理解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 相关文章推荐
使用VisualStudio开发php的图文设置方法
Aug 21 PHP
php守护进程 加linux命令nohup实现任务每秒执行一次
Jul 04 PHP
优化PHP代码技巧的小结
Jun 02 PHP
PHP根据传来的16进制颜色代码自动改变背景颜色
Jun 13 PHP
thinkphp的静态缓存用法分析
Nov 29 PHP
php使用递归函数实现数字累加的方法
Mar 16 PHP
PHP+redis实现添加处理投票的方法
Nov 14 PHP
PHP程序中使用adodb连接不同数据库的代码实例
Dec 19 PHP
windows server 2008/2012安装php iis7 mysql环境搭建教程
Jun 30 PHP
利用PHP将图片转换成base64编码的实现方法
Sep 13 PHP
php中mkdir()函数的权限问题分析
Sep 24 PHP
PHP编程获取图片的主色调的方法【基于Imagick扩展】
Aug 02 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数据结构与算法(PHP描述) 快速排序 quick sort
2012/06/21 PHP
PHP+iFrame实现页面无需刷新的异步文件上传
2014/09/16 PHP
PHP读取大文件的多种方法介绍
2016/04/04 PHP
laravel 如何实现引入自己的函数或类库
2019/10/15 PHP
JavaScript 学习笔记(四)
2009/12/31 Javascript
仿猪八戒网左下角的文字滚动效果
2011/10/28 Javascript
jquery实现带单选按钮的表格行选中时高亮显示
2013/08/01 Javascript
js使用DOM设置单选按钮、复选框及下拉菜单的方法
2015/01/20 Javascript
微信内置浏览器私有接口WeixinJSBridge介绍
2015/05/25 Javascript
jQuery菜单插件用法实例
2015/07/25 Javascript
vue2 中如何实现动态表单增删改查实例
2017/06/09 Javascript
ReactNative踩坑之配置调试端口的解决方法
2017/07/28 Javascript
JS猜数字游戏实例讲解
2020/06/30 Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
2020/11/02 Javascript
jQuery+ajax实现文件上传功能
2020/12/22 jQuery
[52:20]DOTA2-DPC中国联赛正赛 SAG vs XGBO3 第一场 3月5日
2021/03/11 DOTA
深入Python解释器理解Python中的字节码
2015/04/01 Python
Python使用正则表达式过滤或替换HTML标签的方法详解
2017/09/25 Python
几种实用的pythonic语法实例代码
2018/02/24 Python
浅析Python 3 字符串中的 STR 和 Bytes 有什么区别
2018/10/14 Python
python读取大文件越来越慢的原因与解决
2019/08/08 Python
详解Python文件修改的两种方式
2019/08/22 Python
Django使用uwsgi部署时的配置以及django日志文件的处理方法
2019/08/30 Python
python计算n的阶乘的方法代码
2019/10/25 Python
Python Scrapy框架:通用爬虫之CrawlSpider用法简单示例
2020/04/11 Python
YSL圣罗兰美妆美国官网:Yves Saint Lauret US
2016/11/21 全球购物
Tod’s英国官方网站:意大利奢华手工制作手袋和鞋履
2019/03/15 全球购物
个人实用的自我评价范文
2013/11/23 职场文书
教师先进工作者事迹材料
2014/05/01 职场文书
年度评优评先方案
2014/06/03 职场文书
食品工程专业求职信
2014/06/15 职场文书
舞蹈教育学专业自荐信
2014/06/15 职场文书
民主评议党员个人自我评价
2015/03/03 职场文书
2015年司机年终工作总结
2015/05/14 职场文书
婚宴主持词
2015/06/30 职场文书
mybatis源码解读之executor包语句处理功能
2022/02/15 Java/Android