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 相关文章推荐
sqlyog 中文乱码问题的设置方法
Oct 19 PHP
PHPMailer 中文使用说明小结
Jan 22 PHP
php中拷贝构造函数、赋值运算符重载
Jul 25 PHP
ThinkPHP的MVC开发机制实例解析
Aug 23 PHP
php metaphone()函数及php localeconv() 函数实例解析
May 15 PHP
PHP开发制作一个简单的活动日程表Calendar
Jun 20 PHP
PHP使用PHPExcel删除Excel单元格指定列的方法
Jul 06 PHP
phpMyAdmin无法登陆的解决方法
Apr 27 PHP
PHP PDOStatement::closeCursor讲解
Jan 30 PHP
PHP 并发场景的几种解决方案
Jun 14 PHP
Yii框架连表查询操作示例
Sep 06 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
Nov 14 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
德劲1104的电路分析与改良
2021/03/01 无线电
在CentOS上搭建LAMP+vsftpd环境的简单指南
2015/08/01 PHP
php is_executable判断给定文件名是否可执行实例
2016/09/26 PHP
Yii2中hasOne、hasMany及多对多关联查询的用法详解
2017/02/15 PHP
thinkphp5框架扩展redis类方法示例
2019/05/06 PHP
JavaScript实用技巧(一)
2010/08/16 Javascript
仅IE6/7/8中innerHTML返回值忽略英文空格的问题
2011/04/07 Javascript
js/html光标定位的实现代码
2013/09/23 Javascript
javascripit实现密码强度检测代码分享
2013/12/12 Javascript
动态载入js提高网页打开速度的方法
2014/07/04 Javascript
JavaScript获取数组最小值和最大值的方法
2015/06/09 Javascript
Javascript实现快速排序(Quicksort)的算法详解
2015/09/06 Javascript
js行号显示的文本框实现效果(兼容多种浏览器 )
2015/10/23 Javascript
详解jQuery向动态生成的内容添加事件响应jQuery live()方法
2015/11/02 Javascript
Bootstrap自定义文件上传下载样式
2016/05/26 Javascript
javascript之Array 数组对象详解
2016/06/07 Javascript
浅谈javascript中的 “ &amp;&amp; ” 和 “ || ”
2017/02/02 Javascript
手机注册发送验证码倒计时的简单实例
2017/11/15 Javascript
轻松搞定jQuery+JSONP跨域请求的解决方案
2018/03/06 jQuery
JavaScript实现飞舞的泡泡效果
2020/02/07 Javascript
JS图片预加载三种实现方法解析
2020/05/08 Javascript
python让图片按照exif信息里的创建时间进行排序的方法
2015/03/16 Python
python登录pop3邮件服务器接收邮件的方法
2015/04/30 Python
Python学习小技巧之列表项的排序
2017/05/20 Python
计算机二级python学习教程(2) python语言基本语法元素
2019/05/16 Python
大家都说好用的Python命令行库click的使用
2019/11/07 Python
Python使用pymysql模块操作mysql增删改查实例分析
2019/12/19 Python
flask框架自定义url转换器操作详解
2020/01/25 Python
使用 django orm 写 exists 条件过滤实例
2020/05/20 Python
keras实现VGG16方式(预测一张图片)
2020/07/07 Python
详解python UDP 编程
2020/08/24 Python
Levi’s美国官网:美国著名的牛仔裤品牌
2016/08/19 全球购物
2015年元旦活动总结
2014/05/09 职场文书
2014年实习期工作总结
2014/11/27 职场文书
2015年高一班主任工作总结
2015/05/13 职场文书
Redis之RedisTemplate配置方式(序列和反序列化)
2022/03/13 Redis