php代码中的 throw 被编译完后的opcode 如下
opcode: 108 ZEND_THROW , index:2369 ZEND_THROW_SPEC_VAR_HANDLER
其中 ZEND_THROW_SPEC_VAR_HANDLER 最终会通过
zend_throw_exception_internal() 来抛出异常
ZEND_API ZEND_COLD void zend_throw_exception_internal(zval *exception) /* {{{ */
{
if (!EG(current_execute_data)->func ||
!ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) {
/* no need to rethrow the exception */
return;
}
EG(opline_before_exception) = EG(current_execute_data)->opline;
EG(current_execute_data)->opline = EG(exception_op);
}
这里的关键代码是这句 EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION
ZEND_HANDLE_EXCEPTION 对应的 handler是 ZEND_HANDLE_EXCEPTION_SPEC_HANDLER
执行的是常见的try catch 捕获跳转流程, 参考php源码-try、catch过程-原理
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
uint32_t throw_op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
int i, current_try_catch_offset = -1;
{
const zend_op *exc_opline = EG(opline_before_exception);
if ((exc_opline->opcode == ZEND_FREE || exc_opline->opcode == ZEND_FE_FREE)
&& exc_opline->extended_value & ZEND_FREE_ON_RETURN) {
/* exceptions thrown because of loop var destruction on return/break/...
* are logically thrown at the end of the foreach loop, so adjust the
* throw_op_num.
*/
throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
}
}
/* Find the innermost try/catch/finally the exception was thrown in */
for (i = 0; i < EX(func)->op_array.last_try_catch; i++) {
zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[i];
if (try_catch->try_op > throw_op_num) {
/* further blocks will not be relevant... */
break;
}
if (throw_op_num < try_catch->catch_op || throw_op_num < try_catch->finally_end) {
current_try_catch_offset = i;
}
}
cleanup_unfinished_calls(execute_data, throw_op_num);
ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(current_try_catch_offset, throw_op_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}