深入理解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 相关文章推荐
PHP4实际应用经验篇(2)
Oct 09 PHP
PHP 循环列出目录内容的函数代码
May 26 PHP
php后台如何避免用户直接进入方法实例
Oct 15 PHP
PHP扩展CURL的用法详解
Jun 20 PHP
php判断文件上传类型及过滤不安全数据的方法
Dec 17 PHP
CodeIgniter读写分离实现方法详解
Jan 20 PHP
Symfony2使用第三方库Upload制作图片上传实例详解
Feb 04 PHP
Yii隐藏URL中index.php的方法
Jul 12 PHP
PHP使用SOAP调用API操作示例
Dec 25 PHP
php设计模式之建造器模式分析【星际争霸游戏案例】
Jan 23 PHP
tp5框架前台无限极导航菜单类实现方法分析
Mar 29 PHP
tp5.1 框架数据库高级查询技巧实例总结
May 25 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
Google Voice 短信发送接口PHP开源版(2010.5更新)
2010/07/22 PHP
使用PHP强制下载PDF文件示例
2014/01/17 PHP
Codeigniter上传图片出现“You did not select a file to upload”错误解决办法
2014/06/12 PHP
JavaScript 组件之旅(三):用 Ant 构建组件
2009/10/28 Javascript
写出更好的JavaScript之undefined篇(上)
2009/11/22 Javascript
javascript 程序库的比较(一)之DOM功能
2010/04/07 Javascript
JavaScript 语言的递归编程
2010/05/18 Javascript
jquery重新播放css动画所遇问题解决
2013/08/21 Javascript
JS弹出层单纯的绝对定位居中示例代码
2014/02/18 Javascript
查询json的数据结构的8种方式简介
2014/03/10 Javascript
javascript的正则匹配方法学习
2016/02/24 Javascript
深入理解JavaScript中为什么string可以拥有方法
2016/05/24 Javascript
KVM虚拟化技术之使用Qemu-kvm创建和管理虚拟机的方法
2016/10/05 Javascript
Bootstrap table表格简单操作
2017/02/07 Javascript
weex里Vuex state使用storage持久化详解
2017/09/09 Javascript
vue 之 .sync 修饰符示例详解
2018/04/21 Javascript
JavaScript面向对象的程序设计(犯迷糊的小羊)
2018/05/27 Javascript
React中使用外部样式的3种方式(小结)
2019/05/28 Javascript
JS+CSS实现3D切割轮播图
2020/03/21 Javascript
python定时检查启动某个exe程序适合检测exe是否挂了
2013/01/21 Python
Python+django实现文件上传
2016/01/17 Python
将自己的数据集制作成TFRecord格式教程
2020/02/17 Python
学会python自动收发邮件 代替你问候女友
2020/05/20 Python
Python下载网易云歌单歌曲的示例代码
2020/08/12 Python
python/golang实现循环链表的示例代码
2020/09/14 Python
Marmot土拨鼠官网:美国专业户外运动品牌
2018/01/11 全球购物
韩国美国时尚服装和美容在线全球市场:KOODING
2018/11/07 全球购物
出国留学介绍信
2014/01/13 职场文书
搞笑创意广告语
2014/03/17 职场文书
如何写一份好的英文求职信
2014/03/19 职场文书
自我查摆剖析材料
2014/10/11 职场文书
教师个人事迹材料
2014/12/17 职场文书
2015年城管个人工作总结范文
2015/04/20 职场文书
中学图书馆工作总结
2015/08/11 职场文书
创业计划书之牛肉汤快餐店
2019/10/08 职场文书
Django显示可视化图表的实践
2021/05/10 Python