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 相关文章推荐
在PHP中使用反射技术的架构插件使用说明
May 18 PHP
PHP面向对象的进阶学习(抽像类、接口、final、类常量)
May 07 PHP
将时间以距今多久的形式表示,PHP,js双版本
Sep 25 PHP
解析htaccess伪静态的规则
Jun 18 PHP
PHP上传文件时文件过大$_FILES为空的解决方法
Nov 26 PHP
php正则匹配html中带class的div并选取其中内容的方法
Jan 13 PHP
php批量删除cookie的简单实现方法
Jan 26 PHP
PHP实现的多文件上传类及用法示例
May 06 PHP
探究Laravel使用env函数读取环境变量为null的问题
Dec 06 PHP
Laravel框架生命周期与原理分析
Jun 12 PHP
详细对比php中类继承和接口继承
Oct 11 PHP
PHP实现图片压缩
Sep 09 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数组函数序列之in_array() 查找数组值是否存在
2011/10/29 PHP
解析ajax事件的调用顺序
2013/06/17 PHP
CodeIgniter CLI模式简介
2014/06/17 PHP
CodeIgniter实现更改view文件夹路径的方法
2014/07/04 PHP
PHP简单实现模拟登陆功能示例
2017/09/15 PHP
PHP语言对接抖音快手小红书视频/图片去水印API接口源码
2020/08/11 PHP
Zero Clipboard js+swf实现的复制功能使用方法
2010/03/07 Javascript
限制文本框输入N个字符的js代码
2010/05/13 Javascript
选择器中含有空格在使用示例及注意事项
2013/07/31 Javascript
js中的preventDefault与stopPropagation详解
2014/01/29 Javascript
Javascript实现飞动广告效果的方法
2015/05/25 Javascript
js实现人民币大写金额形式转换
2016/04/27 Javascript
JQuery实现动态操作表格
2017/01/11 Javascript
js禁止浏览器的回退事件
2017/04/20 Javascript
JS获取子节点、父节点和兄弟节点的方法实例总结
2018/07/06 Javascript
关于自定义Egg.js的请求级别日志详解
2018/12/12 Javascript
js+canvas实现刮刮奖功能
2020/09/13 Javascript
下载给定网页上图片的方法
2014/02/18 Python
Python Web服务器Tornado使用小结
2014/05/06 Python
Python实现抓取网页并且解析的实例
2014/09/20 Python
Python的for和break循环结构中使用else语句的技巧
2016/05/24 Python
python如何制作英文字典
2019/06/25 Python
Python中使用双下划线防止类属性被覆盖问题
2019/06/27 Python
python Opencv计算图像相似度过程解析
2019/12/03 Python
Python利用Scrapy框架爬取豆瓣电影示例
2020/01/17 Python
英国地毯卖家:The Rug Seller
2019/07/18 全球购物
计算机专业自荐信
2013/10/14 职场文书
应届毕业生个人自荐信范文
2013/11/30 职场文书
开办化妆品公司创业计划书
2013/12/26 职场文书
岗位竞聘演讲稿
2014/01/10 职场文书
党员个人对照检查材料范文
2014/09/24 职场文书
英语邀请函范文
2015/02/02 职场文书
世界遗产导游词
2015/02/13 职场文书
送给客户微信问候语!
2019/07/04 职场文书
Nginx部署vue项目和配置代理的问题解析
2021/08/04 Servers
Flutter集成高德地图并添加自定义Maker的实践
2022/04/07 Java/Android