本文基于PHP的commit为:b18b2c8fe587321384c9423470cf97d8040b32e2
在执行PHP
扩展层面的代码的时候,如果遇到了错误,我们可以通过zend_throw_error
这个函数来设置error
异常对象,然后使用宏RETURN_THROWS
来退出扩展函数。例如:
1 2 3 4 5 6 7
| digest = algo->hash(password, options); if (!digest) { if (!EG(exception)) { zend_throw_error(NULL, "Password hashing failed for unknown reason"); } RETURN_THROWS(); }
|
我们来看看zend_throw_error
会做些什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format, ...) { va_list va; char *message = NULL;
if (!exception_ce) { exception_ce = zend_ce_error; }
if (EG(exception) == (void*)(uintptr_t)-1) { return; }
va_start(va, format); zend_vspprintf(&message, 0, format, va);
if (EG(current_execute_data) && !CG(in_compilation)) { zend_throw_exception(exception_ce, message, 0); } else { zend_error(E_ERROR, "%s", message); }
efree(message); va_end(va); }
|
首先是:
1 2 3
| if (!exception_ce) { exception_ce = zend_ce_error; }
|
会先判断是否传递了异常类exception_ce
,如果没有传递,那么使用PHP
默认的zend_ce_error
异常类。
然后,这里核心的地方是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| if (EG(current_execute_data) && !CG(in_compilation)) { zend_throw_exception(exception_ce, message, 0); }
ZEND_API ZEND_COLD zend_object *zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code) { zend_string *msg_str = message ? zend_string_init(message, strlen(message), 0) : NULL; zend_object *ex = zend_throw_exception_zstr(exception_ce, msg_str, code); if (msg_str) { zend_string_release(msg_str); } return ex; }
static zend_object *zend_throw_exception_zstr(zend_class_entry *exception_ce, zend_string *message, zend_long code) { object_init_ex(&ex, exception_ce);
if (message) { ZVAL_STR(&tmp, message); zend_update_property_ex(exception_ce, &ex, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); } if (code) { ZVAL_LONG(&tmp, code); zend_update_property_ex(exception_ce, &ex, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); }
zend_throw_exception_internal(&ex); return Z_OBJ(ex); }
ZEND_API ZEND_COLD void zend_throw_exception_internal(zval *exception) { if (exception != NULL) { zend_object *previous = EG(exception); zend_exception_set_previous(Z_OBJ_P(exception), EG(exception)); EG(exception) = Z_OBJ_P(exception); if (previous) { return; } } }
|
这段代码做了一件事情,把zend_ce_error
异常类实例化,然后设置它的message
等属性,最后设置EG(exception)
为这个实例化的对象。(所以,我们的RETURN_THROWS
断言会成功)。