深入理解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 相关文章推荐
Linux下实现PHP多进程的方法分享
Aug 16 PHP
PHP分多步骤填写发布信息的简单方法实例代码
Sep 23 PHP
PHP处理excel cvs表格的方法实例介绍
May 13 PHP
CI框架学习笔记(一) - 环境安装、基本术语和框架流程
Oct 26 PHP
PHP代码判断设备是手机还是平板电脑(两种方法)
Oct 19 PHP
php 参数过滤、数据过滤详解
Oct 26 PHP
PHP数据库连接mysql与mysqli对比分析
Jan 04 PHP
PHP之十六个魔术方法详细介绍
Nov 01 PHP
PHP排序算法之冒泡排序(Bubble Sort)实现方法详解
Apr 20 PHP
Laravel框架之解决前端显示图片问题
Oct 24 PHP
php封装的page分页类完整实例代码
Feb 01 PHP
PHP pthreads v3在centos7平台下的安装与配置操作方法
Feb 21 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 手机归属地查询 api
2010/02/08 PHP
PHP警告Cannot use a scalar value as an array的解决方法
2012/01/11 PHP
解析如何用php screw加密php源代码
2013/06/20 PHP
PHP删除数组中空值的方法介绍
2014/04/14 PHP
PHP SPL标准库中的常用函数介绍
2015/05/11 PHP
Laravel执行migrate命令提示:No such file or directory的解决方法
2016/03/16 PHP
laravel 解决强制跳转 https的问题
2019/10/22 PHP
获取页面高度,窗口高度,滚动条高度等参数值getPageSize,getPageScroll
2006/09/22 Javascript
JavaScript中void(0)的具体含义解释
2007/02/27 Javascript
javascript最常用与实用的创建类的代码
2010/08/12 Javascript
angular.element方法汇总
2015/01/07 Javascript
JavaScript知识点总结(十一)之js中的Object类详解
2016/05/31 Javascript
PHP+jquery+ajax实现分页
2016/12/09 Javascript
JS中Array数组学习总结
2017/01/18 Javascript
原生JS实现几个常用DOM操作API实例
2017/01/19 Javascript
JS脚本实现网页自动秒杀点击
2018/01/11 Javascript
Vue仿支付宝支付功能
2018/05/25 Javascript
vuejs 切换导航条高亮(路由菜单高亮)的方法示例
2018/05/29 Javascript
详解React中setState回调函数
2018/06/14 Javascript
Vue.js组件间通信方式总结【推荐】
2018/11/23 Javascript
[08:54]《一刀刀一天》之DOTA全时刻18:十九支奔赴西雅图队伍全部出炉
2014/06/04 DOTA
[02:42]2014DOTA2国际邀请赛 三冰专访:我会打到Ti20
2014/07/13 DOTA
wxpython 学习笔记 第一天
2009/02/09 Python
多线程爬虫批量下载pcgame图片url 保存为xml的实现代码
2013/01/17 Python
Python OpenCV处理图像之滤镜和图像运算
2018/07/10 Python
python调用tcpdump抓包过滤的方法
2018/07/18 Python
Django模型中字段属性choice使用说明
2020/03/30 Python
python实现四人制扑克牌游戏
2020/04/22 Python
利用Vscode进行Python开发环境配置的步骤
2020/06/22 Python
香港莎莎官网Sasa.com:亚洲著名国际化妆品商城
2019/11/10 全球购物
中软Java笔试题
2012/11/11 面试题
给物业的表扬信
2014/01/21 职场文书
社区戒毒工作方案
2014/06/04 职场文书
2014年施工员工作总结
2014/11/18 职场文书
浅谈spring boot使用thymeleaf版本的问题
2021/08/04 Java/Android
微信小程序APP页面的之间的相互传递参数以及自定义组件
2022/04/19 Javascript