这篇博客很简单,是写给自己的,每天写一篇博客的习惯要继续。今天的博文写的主要是php中异常条转的分析。先写一段PHP的代码吧:
<?php
try {
echo 'step exception one';//flag1
try {
echo 'step exception two';//flag2
try {
echo 'step exception three';//flag3
throw new Exception("exception");
}catch(Exception $e) {
throw $e;
}
} catch (Exception $e) {
throw $e;
}
} catch(Exception $e) {
echo "catch the exception now";
}
?>
上面我们使用了3个try语句,异常一层层往上抛,PHP如何去捕获这些异常,PHP内部如何做到抛出异常。我们知道在C语言中,jmp类族函数可以实现跨函数跳转,因此我们写个简单的例子:
#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
void step2()
{
puts("now we are in step2");
longjmp(buf, 2);
}
void step1()
{
puts("now we are in step1");
if(setjmp(buf) == 0) {
puts("set jump in step1");
step2();
} else {
puts("catch the jump");
}
}
int main(int argc, char **argv)
{
/**
*the results:
*now we are in step1
*set jump in step1
*now we are in step2
*catch the jump
*/
step1();
return 0;
}
现在我们在加一层跳转,来模拟上述PHP的三个try catch,这个时候我们需要记录下上一步的
#include <setjmp.h>
jmp_buf *buf;
void step3()
{
longjmp(*buf, 3);
}
void step2()
{
puts("now we are in step2");
jmp_buf *old_buf = buf;
jmp_buf now_buf;
if(setjmp(now_buf) == 0) {
buf = &now_buf;
step3();
} else {
//我们可以处理异常,也可以继续往上抛 类似于 throw
longjmp(*old_buf, 2);
}
}
void step1()
{
puts("now we are in step1");
jmp_buf *old_buf;
jmp_buf now_buf;
if(setjmp(now_buf) == 0) {
puts("set jump in step1");
buf = &now_buf;
step2();
} else {
puts("catch the jump");
}
}
//这样我可以一层层往上跳转,buf 记录上一步的jmp_buf的地址。PHP就是这样实现异常的往上逐层抛。
PHP的内部封装:
//在zend_globals.h中的
struct _zend_executor_globals {
//...
JMP_BUF *bailout;//类似我们上边的buf,称之为中继指针
//...
};
//zend_portability.h定义了
#ifdef HAVE_SIGSETJMP
# define SETJMP(a) sigsetjmp(a, 0)
# define LONGJMP(a,b) siglongjmp(a, b)
# define JMP_BUF sigjmp_buf
#else
# define SETJMP(a) setjmp(a)
# define LONGJMP(a,b) longjmp(a, b)
# define JMP_BUF jmp_buf
#endif
//在zend.h中有定义 try之类的定义
#define zend_try \
{ \
JMP_BUF *__orig_bailout = EG(bailout); \
JMP_BUF __bailout; \
\
EG(bailout) = &__bailout; \
if (SETJMP(__bailout)==0) {
#define zend_catch \
} else { \
EG(bailout) = __orig_bailout;
#define zend_end_try() \
} \
EG(bailout) = __orig_bailout; \
}
#define zend_first_try EG(bailout)=NULL; zend_try
下一篇博客我们分析一下sigsetjmp siglongjmp这2个函数吧。