php 捕获异常 原理,深入理解PHP异常机制的原理

PHP的异常机制的原理是什么?在PHP每一个可独立执行的op array最后的ZEND_HANDLE_EXCEPTION是用来干什么呢?

让我们从一个问题说起,上周的时候,blue5tar提了一个问题:”对于下面的代码,onError明明执行了,但是onException却没有执行,为什么?”。

}functiononException($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*exceptionTSRMLS_DC) {1.判断是否已经有异常抛出2.记录exception3.记录下一条要执行的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));

注: 图中有一处不严谨,即在确定是否最后一个catch块的时候,会同时判断(is_a),如果是才进入最后一个catch块执行。

0818b9ca8b590ca3270a3433284dd417.png

而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。

相关阅读

phpwind发布windframework开源开发框架

Web开发必知:PHP 5.4正式版重要新特性

实战:php环境下redis实现IP查找

排名前八的PHP调试工具你认可吗?

2014年为您细数PHP框架排行榜Top 10

不要再假装 PHP是一个好的编程语言?

PHP 4.0缓冲区控制函数使用方法及实例

为 PHP 开发者准备的 12 个调试工具

支撑30000用户 扩展PHP的五个简单技巧

社交帐号登录:

微博

QQ

人人

豆瓣

更多»

0818b9ca8b590ca3270a3433284dd417.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值