深入理解PHP原理之异常机制

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异常流程

注: 图中有一处不严谨, 即在确定是否最后一个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)

(0)

相关推荐

  • PHP原理之异常机制深入分析

    PHP的异常机制的原理是什么? 在PHP每一个可独立执行的op array最后的ZEND_HANDLE_EXCEPTION是用来干什么呢? 让我们从一个问题说起, 上周的时候, blue5tar提了一个问题:"对于下面的代码, onError明明执行了, 但是onException却没有执行, 为什么?". 复制代码 代码如下: <?php function onError($errCode, $errMesg, $errFile, $errLine) { echo "

  • 修改Zend引擎实现PHP源码加密的原理及实践

    一.基本原理 考虑截获PHP读取源文件的接口.一开始,我考虑从Apache和PHP 之间的接口处处理,参见apache的src/modules/php4/mod_php4.c (这个是PHP用static方式编译进apache,make install 后的文件),在send_php()函数中截获文件指针,采用临时文件的方式,解密后替换文件指针.这种方法经过测试实践,证明是可行的.但是,必须使用两次文件操作,效率低下,而且对于DSO方式不可采用. 双缘敬老院 由此,重新考虑截获PHP读取文件并装

  • php源码加密 仿微盾PHP加密专家(PHPCodeLock)

    复制代码 代码如下: function T_rndstr($length=""){//返回随机字符串 $str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; if($length==""){ return str_shuffle($str); }else{ return substr(str_shuffle($str),-$length); } } $T_k1=T_rndstr()

  • 深入理解PHP之源码目录结构与功能说明

    本文讲述了PHP源码目录结构与功能说明.分享给大家供大家参考,具体如下: PHP之所以能在web开发语言中排名靠前,不仅仅是因为语法简单,上手容易.我个人认为更多是因为其语言本身的:模块的易扩展性,可维护性以及内存安全管理等特点.写过PHP的程序员不一定都知道:PHP是如何执行的?其组织结构目录的作用?如果对其有所了解,对PHP的认识会更深入,写出的代码也会更高效,更健壮...... 1. build 和编译有关的目录. 2. ext 扩展库代码,例如 MySQL.zlib.iconv 等我们熟

  • 深入理解PHP之OpCode原理详解

    本文实例讲述了PHP中OpCode的原理.分享给大家供大家参考,具体如下: OpCode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,或者.NET的MSL. 此文主要基于< Understanding OPcode>和 网络,根据个人的理解和修改,特记录下来 : PHP代码: <?php echo "Hello World"; $a = 1 + 1; echo $a; ?> PHP执行这段代码会经过如下4个步骤: 1. Scanning (L

  • PHP源码之 ext/mysql扩展部分

    我写过一个外部模块扩展,现在开始看PHP源码中的mysql扩展,它是可以被集成到PHP内部的,所以应该算是内置的扩展了. 该扩展需要用到mysql数据库提供的一些接口,所以需要安装了mysql,并能够确定mysql.h的位置. 该扩展的位置一般在 PHP-source-code/ext/mysql 下. 在linux下,主要需要注意的文件是: config.m4, php_mysql.c, php_mysql_structs.h. ps:该目录下有tags文件,所以可以利用ctags的各种特性,

  • PHP的运行机制与原理(底层)

    说到php的运行机制还要先给大家介绍php的模块,PHP总共有三个模块:内核.Zend引擎.以及扩展层:PHP内核用来处理请求.文件流.错误处理等相关操作:Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它:扩展层是一组函数.类库和流,PHP使用它们来执行一些特定的操作.比如,我们需要mysql扩展来连接MySQL数据库:当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还: 最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAP

  • 深入理解PHP原理之执行周期分析

    本文讲述了PHP原理之执行周期.分享给大家供大家参考,具体如下: PHP的执行周期,从最初我们编写的PHP脚本->到最后脚本被执行->得到执行结果,这个过程,其实可以分为如下几个阶段: 首先,Zend Engine(ZE),调用词法分析 器(Lex生成的,源文件在 Zend/zend_language_sanner.l), 将我们要执行的PHP源文件,去掉空格 ,注释,分割成一个一个的token. 然后,ZE会将得到的token forward给语法分析 器(yacc生成, 源文件在 Zend

  • 学习php开源项目的源码指南

    一.先把源代码安装起来,结合它的文档和手册,熟悉其功能和它的应用方式. 二.浏览源代码的目录结构,了解各个目录的功能. 三.经过以上两步后相信你对这个开源的产品有了一个初步的了解了,那现在就开始分析它的源码吧.这一步我们开始分析源代码框架.例如入口方式是单入口还是多入口,页面之间的调用规则,能根据规则找出某个功能用到的页面. 四.熟悉源代码的代码写作风格,例如缩进方式,排版格式等. 五.熟悉一下源代码用到的数据库和表,可以参考它的技术支持文档. 六.经过以上几步相信大家已经对这份源代码有了更深刻

  • 深入理解PHP原理之异常机制

    PHP的异常机制的原理是什么? 在PHP每一个可独立执行的op array最后的ZEND_HANDLE_EXCEPTION是用来干什么呢? 让我们从一个问题说起, 上周的时候, blue5tar提了一个问题:"对于下面的代码, onError明明执行了, 但是onException却没有执行, 为什么?". 复制代码 代码如下: <?php function onError($errCode, $errMesg, $errFile, $errLine) { echo "

  • C++中异常机制的实现机制详解

    前言 异常就是运行时出现出现的不正常(没说一样),例如系统运行时耗尽了内存或遇到意外的非法输入.本文详细介绍了关于C++中异常机制实现机制的相关内容,下面话不多说了,来一起看看详细的介绍吧. 1.C函数的调用和返回 要理解C++异常机制实现之前,首先要了解一个函数的调用和返回机制,这里面就要涉及到ESP和EBP寄存器.我们先看一下函数调用和返回的流程. 下面是按调用约定__stdcall 调用函数test(int p1,int p2)的汇编代码 假设执行函数前堆栈指针ESP为NN push p2

  • 理解Django 中Call Stack机制的小Demo

    1.工作流程 request/response模式下,request并不是直接到达view方法,view方法也不是将返回的response直接发送给浏览器的,而是request由外到里的层层通过各种middleware层,这个时候可以对request做一些事情,到最后一层也就是最内层时,得到view方法返回的response,然后再把这个response再由内到外层层传递出来,这时候可以对response做一些事情,如下图: 2.原理 class SimpleMiddleware: def __

  • 全面理解java中的异常处理机制

    一.java异常总结: 异常就是程序运行时出现不正常运行情况 1.异常由来: 通过java的类的形式对现实事物中问题的描述,并封住成了对象 其实就是java对不正常情况描述后的对象体现 2.对于问题的划分有两种:一种是严重的问题,一种是非严重的问题 对于严重的,java通过Error类来描述 对于Error一般不编写针对性的代码对其进行处理 对于非严重的,java通过Exception类来描述 对于Exception可以使用针对性的处理方式进行处理 3.常见的异常有:数组角标越界异常,空指针异常

  • 如何快速理解python的垃圾回收机制

    一.先来说说为什么要有垃圾回收 解释器在执行到定义变量得语法时,会申请内存空间来存放变量得值,但是由于内存空间是有限得,所以这就涉及到了内存回收问题了,当一个变量值没有用了(简称垃圾),这种时候就应该回收掉这个变量值得内存空间. 二.那么什么是垃圾回收机制 垃圾回收机制(简称GC)是Python解释器自带一种机,专门用来回收不可用的变量值所占用的内存空间 三.为什么要用垃圾回收机制呢? 程序运行过程中会申请大量的内存空间,而对于一些无用的内存空间如果不及时清理的话会导致内存使用殆尽(内存溢出),

  • 深入理解Java中的SPI机制

    本文通过探析JDK提供的,在开源项目中比较常用的Java SPI机制,希望给大家在实际开发实践.学习开源项目提供参考. 1 SPI是什么 SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件. 整体机制图如下: Java SPI 实际上是"基于接口的编程+策略模式+配置文件"组合实现的动态加载机制. 系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接

  • 深入理解JavaScript的事件执行机制

    目录 前言 浏览器 JS 异步执行的原理 浏览器中的事件循环 执行栈与任务队列 宏任务和微任务 Async/await的运行顺序 特点 示例 个人分析 前言 熟悉事件循环,了解浏览器运行机制将对我们理解 JavaScript 的执行过程和排查运行问题有很大帮助.以下是总结的一些浏览器事件循环的一些原理和示例. 浏览器 JS 异步执行的原理 JS 是单线程的,也就是同一个时刻只能做一件事情,那么,为什么浏览器可以同时执行异步任务呢? 因为浏览器是多线程的,当 JS 需要执行异步任务时,浏览器会另外

  • 利用Java异常机制实现模拟借书系统

    本文介绍的是利用java语言实现一个控制台版的模拟借书系统,在开始本文的正式内容之前,我们先来了解一下Java异常机制. 什么是异常? 异常,不正常也.Exception是Exception event的缩写,因此异常是一个事件,该事件发生在程序运行时. 异常会影响程序的连续性,使程序中断.在Java中,一切皆对象,所以要定义异常,也需要使用对象.异常对象里 封装了异常类型和程序发生异常时的状态. 我们经常说的抛出异常就是创建异常对象,并提交给运行系统. 异常捕获机制与try-catch 当异常

  • JAVA装饰者模式(从现实生活角度理解代码原理)

    装饰者模式可以动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活. 该模式的适用环境为: (1)在不影响其他对象的情况下,以动态.透明的方式给单个对象添加职责. (2)处理那些可以撤消的职责. (3)当不能采用生成子类的方法进行扩充时.一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长.另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类. 实现该模式的关键步骤: (1)Component(被装饰对象基类

随机推荐