From d042d0880796cfe99262bb6fa44225e984c63ace Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Sat, 16 May 2015 15:30:59 -0500 Subject: [PATCH] Remodel exceptions based on Throwable interface Added Throwable interface that exceptions must implement in order to be thrown. BaseException was removed, EngineException renamed to Error, and TypeException and ParseException renamed to TypeError and ParseError. Exception and Error no longer extend a common base class, rather they both implement the Throwable interface. --- Zend/zend.c | 8 +- Zend/zend_exceptions.c | 216 ++++++++++++++++++----------------- Zend/zend_exceptions.h | 9 +- Zend/zend_interfaces.c | 20 +++- Zend/zend_interfaces.h | 1 + Zend/zend_language_scanner.c | 10 +- Zend/zend_language_scanner.l | 10 +- ext/soap/soap.c | 8 +- 8 files changed, 156 insertions(+), 126 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index f262f8b5d75..1c2cfd6c770 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -882,7 +882,7 @@ void zenderror(const char *error) /* {{{ */ return; } - zend_throw_exception(zend_get_parse_exception(), error, E_PARSE); + zend_throw_exception(zend_get_parse_error(), error, E_PARSE); } /* }}} */ @@ -1059,7 +1059,7 @@ static void zend_error_va_list(int type, const char *format, va_list args) va_start(args, format); #endif zend_vspprintf(&message, 0, format, args); - zend_throw_exception(zend_get_engine_exception(), message, type); + zend_throw_exception(zend_get_error(), message, type); efree(message); #if !defined(HAVE_NORETURN) || defined(HAVE_NORETURN_ALIAS) va_end(args); @@ -1318,7 +1318,7 @@ ZEND_API void zend_type_error(const char *format, ...) /* {{{ */ va_start(va, format); zend_vspprintf(&message, 0, format, va); - zend_throw_exception(zend_get_type_exception(), message, E_ERROR); + zend_throw_exception(zend_get_type_error(), message, E_ERROR); efree(message); va_end(va); } /* }}} */ @@ -1331,7 +1331,7 @@ ZEND_API void zend_internal_type_error(zend_bool throw_exception, const char *fo va_start(va, format); zend_vspprintf(&message, 0, format, va); if (throw_exception) { - zend_throw_exception(zend_get_type_exception(), message, E_ERROR); + zend_throw_exception(zend_get_type_error(), message, E_ERROR); } else { zend_error(E_WARNING, message); } diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 671c09cd608..484fcdca60c 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -30,33 +30,43 @@ #include "zend_dtrace.h" #include "zend_smart_str.h" -static zend_class_entry *base_exception_ce; static zend_class_entry *default_exception_ce; static zend_class_entry *error_exception_ce; -static zend_class_entry *engine_exception_ce; -static zend_class_entry *parse_exception_ce; -static zend_class_entry *type_exception_ce; +static zend_class_entry *error_ce; +static zend_class_entry *parse_error_ce; +static zend_class_entry *type_error_ce; static zend_object_handlers default_exception_handlers; ZEND_API void (*zend_throw_exception_hook)(zval *ex); +ZEND_API zend_class_entry *zend_get_exception_base(zval *object) +{ + if (instanceof_function(Z_OBJCE_P(object), error_ce)) { + return error_ce; + } + + return default_exception_ce; +} + void zend_exception_set_previous(zend_object *exception, zend_object *add_previous) { zval tmp, *previous, zv, *pzv, rv; + zend_class_entry *base_ce; if (exception == add_previous || !add_previous || !exception) { return; } ZVAL_OBJ(&tmp, add_previous); - if (!instanceof_function(Z_OBJCE(tmp), base_exception_ce)) { + if (!instanceof_function(Z_OBJCE(tmp), zend_ce_throwable)) { zend_error_noreturn(E_CORE_ERROR, "Cannot set non exception as previous exception"); return; } ZVAL_OBJ(&zv, exception); pzv = &zv; do { - previous = zend_read_property(base_exception_ce, pzv, "previous", sizeof("previous")-1, 1, &rv); + base_ce = zend_get_exception_base(pzv); + previous = zend_read_property(base_ce, pzv, "previous", sizeof("previous")-1, 1, &rv); if (Z_TYPE_P(previous) == IS_NULL) { - zend_update_property(base_exception_ce, pzv, "previous", sizeof("previous")-1, &tmp); + zend_update_property(base_ce, pzv, "previous", sizeof("previous")-1, &tmp); GC_REFCOUNT(add_previous)--; return; } @@ -110,7 +120,7 @@ ZEND_API void zend_throw_exception_internal(zval *exception) /* {{{ */ } } if (!EG(current_execute_data)) { - if (exception && Z_OBJCE_P(exception) == parse_exception_ce) { + if (exception && Z_OBJCE_P(exception) == parse_error_ce) { return; } if(EG(exception)) { @@ -158,6 +168,7 @@ static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type, zval obj; zend_object *object; zval trace; + zend_class_entry *base_ce; Z_OBJ(obj) = object = zend_objects_new(class_type); Z_OBJ_HT(obj) = &default_exception_handlers; @@ -170,15 +181,17 @@ static zend_object *zend_default_exception_new_ex(zend_class_entry *class_type, array_init(&trace); } Z_SET_REFCOUNT(trace, 0); + + base_ce = zend_get_exception_base(&obj); - if (EXPECTED(class_type != parse_exception_ce)) { - zend_update_property_string(base_exception_ce, &obj, "file", sizeof("file")-1, zend_get_executed_filename()); - zend_update_property_long(base_exception_ce, &obj, "line", sizeof("line")-1, zend_get_executed_lineno()); + if (EXPECTED(class_type != parse_error_ce)) { + zend_update_property_string(base_ce, &obj, "file", sizeof("file")-1, zend_get_executed_filename()); + zend_update_property_long(base_ce, &obj, "line", sizeof("line")-1, zend_get_executed_lineno()); } else { - zend_update_property_string(base_exception_ce, &obj, "file", sizeof("file")-1, zend_get_compiled_filename()->val); - zend_update_property_long(base_exception_ce, &obj, "line", sizeof("line")-1, zend_get_compiled_lineno()); + zend_update_property_string(base_ce, &obj, "file", sizeof("file")-1, zend_get_compiled_filename()->val); + zend_update_property_long(base_ce, &obj, "line", sizeof("line")-1, zend_get_compiled_lineno()); } - zend_update_property(base_exception_ce, &obj, "trace", sizeof("trace")-1, &trace); + zend_update_property(base_ce, &obj, "trace", sizeof("trace")-1, &trace); return object; } @@ -196,6 +209,12 @@ static zend_object *zend_error_exception_new(zend_class_entry *class_type) /* {{ } /* }}} */ +static zval *zend_get_exception_property(zval *object, const char *name, size_t name_length, zend_bool silent, zval *rv) /* {{{ */ +{ + return zend_read_property(zend_get_exception_base(object), object, name, name_length, silent, rv); +} +/* }}} */ + /* {{{ proto Exception Exception::__clone() Clone the exception object */ ZEND_METHOD(exception, __clone) @@ -212,25 +231,27 @@ ZEND_METHOD(exception, __construct) zend_string *message = NULL; zend_long code = 0; zval *object, *previous = NULL; + zend_class_entry *base_ce; int argc = ZEND_NUM_ARGS(); - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|SlO!", &message, &code, &previous, base_exception_ce) == FAILURE) { + object = getThis(); + base_ce = zend_get_exception_base(object); + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|SlO!", &message, &code, &previous, base_ce) == FAILURE) { zend_error(E_EXCEPTION | E_ERROR, "Wrong parameters for Exception([string $exception [, long $code [, Exception $previous = NULL]]])"); return; } - object = getThis(); - if (message) { - zend_update_property_str(base_exception_ce, object, "message", sizeof("message")-1, message); + zend_update_property_str(base_ce, object, "message", sizeof("message")-1, message); } if (code) { - zend_update_property_long(base_exception_ce, object, "code", sizeof("code")-1, code); + zend_update_property_long(base_ce, object, "code", sizeof("code")-1, code); } if (previous) { - zend_update_property(base_exception_ce, object, "previous", sizeof("previous")-1, previous); + zend_update_property(base_ce, object, "previous", sizeof("previous")-1, previous); } } /* }}} */ @@ -244,8 +265,8 @@ ZEND_METHOD(error_exception, __construct) zval *object, *previous = NULL; int argc = ZEND_NUM_ARGS(); size_t message_len, filename_len; - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|sllslO!", &message, &message_len, &code, &severity, &filename, &filename_len, &lineno, &previous, base_exception_ce) == FAILURE) { + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, argc, "|sllslO!", &message, &message_len, &code, &severity, &filename, &filename_len, &lineno, &previous, default_exception_ce) == FAILURE) { zend_error(E_EXCEPTION | E_ERROR, "Wrong parameters for ErrorException([string $exception [, long $code, [ long $severity, [ string $filename, [ long $lineno [, Exception $previous = NULL]]]]]])"); return; } @@ -253,25 +274,25 @@ ZEND_METHOD(error_exception, __construct) object = getThis(); if (message) { - zend_update_property_string(base_exception_ce, object, "message", sizeof("message")-1, message); + zend_update_property_string(default_exception_ce, object, "message", sizeof("message")-1, message); } if (code) { - zend_update_property_long(base_exception_ce, object, "code", sizeof("code")-1, code); + zend_update_property_long(default_exception_ce, object, "code", sizeof("code")-1, code); } if (previous) { - zend_update_property(base_exception_ce, object, "previous", sizeof("previous")-1, previous); + zend_update_property(default_exception_ce, object, "previous", sizeof("previous")-1, previous); } - zend_update_property_long(base_exception_ce, object, "severity", sizeof("severity")-1, severity); + zend_update_property_long(error_exception_ce, object, "severity", sizeof("severity")-1, severity); if (argc >= 4) { - zend_update_property_string(base_exception_ce, object, "file", sizeof("file")-1, filename); + zend_update_property_string(default_exception_ce, object, "file", sizeof("file")-1, filename); if (argc < 5) { lineno = 0; /* invalidate lineno */ } - zend_update_property_long(base_exception_ce, object, "line", sizeof("line")-1, lineno); + zend_update_property_long(default_exception_ce, object, "line", sizeof("line")-1, lineno); } } /* }}} */ @@ -282,9 +303,9 @@ ZEND_METHOD(error_exception, __construct) } #define GET_PROPERTY(object, name) \ - zend_read_property(base_exception_ce, (object), name, sizeof(name) - 1, 0, &rv) + zend_get_exception_property((object), name, sizeof(name) - 1, 0, &rv) #define GET_PROPERTY_SILENT(object, name) \ - zend_read_property(base_exception_ce, (object), name, sizeof(name) - 1, 1, &rv) + zend_get_exception_property((object), name, sizeof(name) - 1, 1, &rv) /* {{{ proto string Exception::getFile() Get the file in which the exception occurred */ @@ -551,12 +572,17 @@ ZEND_METHOD(exception, getTraceAsString) { zval *trace, *frame, rv; zend_ulong index; + zval *object; + zend_class_entry *base_ce; smart_str str = {0}; uint32_t num = 0; DEFAULT_0_PARAMS; + + object = getThis(); + base_ce = zend_get_exception_base(object); - trace = zend_read_property(base_exception_ce, getThis(), "trace", sizeof("trace")-1, 1, &rv); + trace = zend_read_property(base_ce, getThis(), "trace", sizeof("trace")-1, 1, &rv); if (Z_TYPE_P(trace) != IS_ARRAY) { RETURN_FALSE; } @@ -618,6 +644,7 @@ zend_string *zend_strpprintf(size_t max_len, const char *format, ...) /* {{{ */ ZEND_METHOD(exception, __toString) { zval trace, *exception; + zend_class_entry *base_ce; zend_string *str; zend_fcall_info fci; zval fname, rv; @@ -675,9 +702,12 @@ ZEND_METHOD(exception, __toString) } zval_dtor(&fname); + exception = getThis(); + base_ce = zend_get_exception_base(exception); + /* We store the result in the private property string so we can access * the result in uncaught exception handlers without memleaks. */ - zend_update_property_str(base_exception_ce, getThis(), "string", sizeof("string")-1, str); + zend_update_property_str(base_ce, exception, "string", sizeof("string")-1, str); RETURN_STR(str); } @@ -734,66 +764,48 @@ void zend_register_default_exception(void) /* {{{ */ zend_class_entry ce; zend_property_info *prop; - INIT_CLASS_ENTRY(ce, "BaseException", default_exception_functions); - base_exception_ce = zend_register_internal_class(&ce); - base_exception_ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; - base_exception_ce->create_object = NULL; + INIT_CLASS_ENTRY(ce, "Exception", default_exception_functions); + default_exception_ce = zend_register_internal_class_ex(&ce, NULL); + default_exception_ce->create_object = zend_default_exception_new; memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); default_exception_handlers.clone_obj = NULL; - - zend_declare_property_string(base_exception_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED); - zend_declare_property_string(base_exception_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE); - zend_declare_property_long(base_exception_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED); - zend_declare_property_null(base_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED); - zend_declare_property_null(base_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED); - zend_declare_property_null(base_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE); - zend_declare_property_null(base_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE); - - INIT_CLASS_ENTRY(ce, "Exception", NULL); - default_exception_ce = zend_register_internal_class_ex(&ce, base_exception_ce); - default_exception_ce->create_object = zend_default_exception_new; - - /* A trick, to make visible private properties of BaseException */ - ZEND_HASH_FOREACH_PTR(&default_exception_ce->properties_info, prop) { - if (prop->flags & ZEND_ACC_SHADOW) { - if (prop->name->len == sizeof("\0BaseException\0string")-1) { - prop->flags &= ~ZEND_ACC_SHADOW; - prop->flags |= ZEND_ACC_PRIVATE; - prop->ce = default_exception_ce; - } else if (prop->name->len == sizeof("\0BaseException\0trace")-1) { - prop->flags &= ~ZEND_ACC_SHADOW; - prop->flags |= ZEND_ACC_PRIVATE; - prop->ce = default_exception_ce; - } else if (prop->name->len == sizeof("\0BaseException\0previous")-1) { - prop->flags &= ~ZEND_ACC_SHADOW; - prop->flags |= ZEND_ACC_PRIVATE; - prop->ce = default_exception_ce; - } - } - } ZEND_HASH_FOREACH_END(); + zend_class_implements(default_exception_ce, 1, zend_ce_throwable); + + zend_declare_property_string(default_exception_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED); + zend_declare_property_string(default_exception_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE); + zend_declare_property_long(default_exception_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED); + zend_declare_property_null(default_exception_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED); + zend_declare_property_null(default_exception_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED); + zend_declare_property_null(default_exception_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE); + zend_declare_property_null(default_exception_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE); INIT_CLASS_ENTRY(ce, "ErrorException", error_exception_functions); error_exception_ce = zend_register_internal_class_ex(&ce, default_exception_ce); error_exception_ce->create_object = zend_error_exception_new; zend_declare_property_long(error_exception_ce, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED); - INIT_CLASS_ENTRY(ce, "EngineException", NULL); - engine_exception_ce = zend_register_internal_class_ex(&ce, base_exception_ce); - engine_exception_ce->create_object = zend_default_exception_new; + INIT_CLASS_ENTRY(ce, "Error", default_exception_functions); + error_ce = zend_register_internal_class_ex(&ce, NULL); + error_ce->create_object = zend_default_exception_new; + memcpy(&default_exception_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + default_exception_handlers.clone_obj = NULL; + zend_class_implements(error_ce, 1, zend_ce_throwable); + + zend_declare_property_string(error_ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED); + zend_declare_property_string(error_ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE); + zend_declare_property_long(error_ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED); + zend_declare_property_null(error_ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED); + zend_declare_property_null(error_ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED); + zend_declare_property_null(error_ce, "trace", sizeof("trace")-1, ZEND_ACC_PRIVATE); + zend_declare_property_null(error_ce, "previous", sizeof("previous")-1, ZEND_ACC_PRIVATE); - INIT_CLASS_ENTRY(ce, "ParseException", NULL); - parse_exception_ce = zend_register_internal_class_ex(&ce, base_exception_ce); - parse_exception_ce->create_object = zend_default_exception_new; + INIT_CLASS_ENTRY(ce, "ParseError", NULL); + parse_error_ce = zend_register_internal_class_ex(&ce, error_ce); + parse_error_ce->create_object = zend_default_exception_new; - INIT_CLASS_ENTRY(ce, "TypeException", NULL); - type_exception_ce = zend_register_internal_class_ex(&ce, engine_exception_ce); - type_exception_ce->create_object = zend_default_exception_new; -} -/* }}} */ - -ZEND_API zend_class_entry *zend_exception_get_base(void) /* {{{ */ -{ - return base_exception_ce; + INIT_CLASS_ENTRY(ce, "TypeError", NULL); + type_error_ce = zend_register_internal_class_ex(&ce, error_ce); + type_error_ce->create_object = zend_default_exception_new; } /* }}} */ @@ -809,21 +821,21 @@ ZEND_API zend_class_entry *zend_get_error_exception(void) /* {{{ */ } /* }}} */ -ZEND_API zend_class_entry *zend_get_engine_exception(void) /* {{{ */ +ZEND_API zend_class_entry *zend_get_error(void) /* {{{ */ { - return engine_exception_ce; + return error_ce; } /* }}} */ -ZEND_API zend_class_entry *zend_get_parse_exception(void) /* {{{ */ +ZEND_API zend_class_entry *zend_get_parse_error(void) /* {{{ */ { - return parse_exception_ce; + return parse_error_ce; } /* }}} */ -ZEND_API zend_class_entry *zend_get_type_exception(void) /* {{{ */ +ZEND_API zend_class_entry *zend_get_type_error(void) /* {{{ */ { - return type_exception_ce; + return type_error_ce; } /* }}} */ @@ -833,8 +845,8 @@ ZEND_API zend_object *zend_throw_exception(zend_class_entry *exception_ce, const zval ex; if (exception_ce) { - if (!instanceof_function(exception_ce, base_exception_ce)) { - zend_error(E_NOTICE, "Exceptions must be derived from the Exception base class"); + if (!instanceof_function(exception_ce, zend_ce_throwable)) { + zend_error(E_NOTICE, "Exceptions must implement Throwable"); exception_ce = default_exception_ce; } } else { @@ -844,10 +856,10 @@ ZEND_API zend_object *zend_throw_exception(zend_class_entry *exception_ce, const if (message) { - zend_update_property_string(base_exception_ce, &ex, "message", sizeof("message")-1, message); + zend_update_property_string(exception_ce, &ex, "message", sizeof("message")-1, message); } if (code) { - zend_update_property_long(base_exception_ce, &ex, "code", sizeof("code")-1, code); + zend_update_property_long(exception_ce, &ex, "code", sizeof("code")-1, code); } zend_throw_exception_internal(&ex); @@ -875,7 +887,7 @@ ZEND_API zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, zval ex; zend_object *obj = zend_throw_exception(exception_ce, message, code); ZVAL_OBJ(&ex, obj); - zend_update_property_long(base_exception_ce, &ex, "severity", sizeof("severity")-1, severity); + zend_update_property_long(error_exception_ce, &ex, "severity", sizeof("severity")-1, severity); return obj; } /* }}} */ @@ -908,13 +920,13 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */ ZVAL_OBJ(&exception, ex); ce_exception = Z_OBJCE(exception); EG(exception) = NULL; - if (ce_exception == parse_exception_ce || ce_exception == type_exception_ce) { + if (ce_exception == parse_error_ce || ce_exception == type_error_ce) { zend_string *message = zval_get_string(GET_PROPERTY(&exception, "message")); zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, "file")); zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, "line")); zend_long code = zval_get_long(GET_PROPERTY_SILENT(&exception, "code")); - if (ce_exception == type_exception_ce && strstr(message->val, ", called in ")) { + if (ce_exception == type_error_ce && strstr(message->val, ", called in ")) { zend_error_helper(code, file->val, line, "%s and defined", message->val); } else { zend_error_helper(code, file->val, line, "%s", message->val); @@ -922,7 +934,7 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */ zend_string_release(file); zend_string_release(message); - } else if (instanceof_function(ce_exception, base_exception_ce)) { + } else if (instanceof_function(ce_exception, default_exception_ce) || instanceof_function(ce_exception, error_ce)) { zval tmp, rv; zend_string *str, *file = NULL; zend_long line = 0; @@ -932,7 +944,7 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */ if (Z_TYPE(tmp) != IS_STRING) { zend_error(E_WARNING, "%s::__toString() must return a string", ce_exception->name->val); } else { - zend_update_property_string(base_exception_ce, &exception, "string", sizeof("string")-1, EG(exception) ? ce_exception->name->val : Z_STRVAL(tmp)); + zend_update_property_string(zend_get_exception_base(&exception), &exception, "string", sizeof("string")-1, EG(exception) ? ce_exception->name->val : Z_STRVAL(tmp)); } } zval_ptr_dtor(&tmp); @@ -942,10 +954,8 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */ ZVAL_OBJ(&zv, EG(exception)); /* do the best we can to inform about the inner exception */ - if (instanceof_function(ce_exception, base_exception_ce)) { - file = zval_get_string(GET_PROPERTY_SILENT(&zv, "file")); - line = zval_get_long(GET_PROPERTY_SILENT(&zv, "line")); - } + file = zval_get_string(GET_PROPERTY_SILENT(&zv, "file")); + line = zval_get_long(GET_PROPERTY_SILENT(&zv, "line")); zend_error_va(E_WARNING, (file && file->len > 0) ? file->val : NULL, line, "Uncaught %s in exception handling during call to %s::__tostring()", @@ -983,8 +993,8 @@ ZEND_API void zend_throw_exception_object(zval *exception) /* {{{ */ exception_ce = Z_OBJCE_P(exception); - if (!exception_ce || !instanceof_function(exception_ce, base_exception_ce)) { - zend_error(E_EXCEPTION | E_ERROR, "Exceptions must be valid objects derived from the Exception base class"); + if (!exception_ce || !instanceof_function(exception_ce, zend_ce_throwable)) { + zend_error(E_EXCEPTION | E_ERROR, "Exceptions must be valid objects implementing Throwable"); return; } zend_throw_exception_internal(exception); diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index e2c1f1fac7f..bae4c35a78f 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -34,12 +34,13 @@ ZEND_API void zend_throw_exception_internal(zval *exception); void zend_register_default_exception(void); -ZEND_API zend_class_entry *zend_exception_get_base(void); +ZEND_API zend_class_entry *zend_get_exception_base(zval *object); + ZEND_API zend_class_entry *zend_exception_get_default(void); ZEND_API zend_class_entry *zend_get_error_exception(void); -ZEND_API zend_class_entry *zend_get_engine_exception(void); -ZEND_API zend_class_entry *zend_get_parse_exception(void); -ZEND_API zend_class_entry *zend_get_type_exception(void); +ZEND_API zend_class_entry *zend_get_error(void); +ZEND_API zend_class_entry *zend_get_parse_error(void); +ZEND_API zend_class_entry *zend_get_type_error(void); ZEND_API void zend_register_default_classes(void); /* exception_ce NULL or zend_exception_get_default() or a derived class diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 54f8f8c1172..2b8f8a25bc8 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -28,6 +28,7 @@ ZEND_API zend_class_entry *zend_ce_aggregate; ZEND_API zend_class_entry *zend_ce_iterator; ZEND_API zend_class_entry *zend_ce_arrayaccess; ZEND_API zend_class_entry *zend_ce_serializable; +ZEND_API zend_class_entry *zend_ce_throwable; /* {{{ zend_call_method Only returns the returned zval if retval_ptr != NULL */ @@ -502,6 +503,19 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e } /* }}}*/ +/* {{{ zend_implement_traversable */ +static int zend_implement_throwable(zend_class_entry *interface, zend_class_entry *class_type) +{ + if (instanceof_function(class_type, zend_exception_get_default()) || instanceof_function(class_type, zend_get_error())) { + return SUCCESS; + } + zend_error_noreturn(E_CORE_ERROR, "Class %s cannot implement interface %s, extend Exception instead", + class_type->name->val, + interface->name->val); + return FAILURE; +} +/* }}} */ + /* {{{ function tables */ const zend_function_entry zend_funcs_aggregate[] = { ZEND_ABSTRACT_ME(iterator, getIterator, NULL) @@ -551,6 +565,8 @@ const zend_function_entry zend_funcs_serializable[] = { }; /* }}} */ +const zend_function_entry *zend_funcs_throwable = NULL; + #define REGISTER_ITERATOR_INTERFACE(class_name, class_name_str) \ {\ zend_class_entry ce;\ @@ -575,7 +591,9 @@ ZEND_API void zend_register_interfaces(void) REGISTER_ITERATOR_INTERFACE(arrayaccess, ArrayAccess); - REGISTER_ITERATOR_INTERFACE(serializable, Serializable) + REGISTER_ITERATOR_INTERFACE(serializable, Serializable); + + REGISTER_ITERATOR_INTERFACE(throwable, Throwable); } /* }}} */ diff --git a/Zend/zend_interfaces.h b/Zend/zend_interfaces.h index 8a8e0ce988a..daf0aae5dc1 100644 --- a/Zend/zend_interfaces.h +++ b/Zend/zend_interfaces.h @@ -31,6 +31,7 @@ extern ZEND_API zend_class_entry *zend_ce_aggregate; extern ZEND_API zend_class_entry *zend_ce_iterator; extern ZEND_API zend_class_entry *zend_ce_arrayaccess; extern ZEND_API zend_class_entry *zend_ce_serializable; +extern ZEND_API zend_class_entry *zend_ce_throwable; typedef struct _zend_user_iterator { zend_object_iterator it; diff --git a/Zend/zend_language_scanner.c b/Zend/zend_language_scanner.c index abf93d587cf..e3b2de1a795 100644 --- a/Zend/zend_language_scanner.c +++ b/Zend/zend_language_scanner.c @@ -998,7 +998,7 @@ static int zend_scan_escape_string(zval *zendlval, char *str, int len, char quot } if (!valid) { - zend_throw_exception(zend_get_parse_exception(), + zend_throw_exception(zend_get_parse_error(), "Invalid UTF-8 codepoint escape sequence", E_PARSE); zval_ptr_dtor(zendlval); return FAILURE; @@ -1009,7 +1009,7 @@ static int zend_scan_escape_string(zval *zendlval, char *str, int len, char quot /* per RFC 3629, UTF-8 can only represent 21 bits */ if (codepoint > 0x10FFFF || errno) { - zend_throw_exception(zend_get_parse_exception(), + zend_throw_exception(zend_get_parse_error(), "Invalid UTF-8 codepoint escape sequence: Codepoint too large", E_PARSE); zval_ptr_dtor(zendlval); return FAILURE; @@ -2720,7 +2720,7 @@ yy136: * Because the lexing itself doesn't do that for us */ if (end != yytext + yyleng) { - zend_throw_exception(zend_get_parse_exception(), "Invalid numeric literal", E_PARSE); + zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); return T_ERROR; } } else { @@ -2736,7 +2736,7 @@ yy136: } /* Also not an assert for the same reason */ if (end != yytext + yyleng) { - zend_throw_exception(zend_get_parse_exception(), + zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); return T_ERROR; } @@ -2745,7 +2745,7 @@ yy136: } /* Also not an assert for the same reason */ if (end != yytext + yyleng) { - zend_throw_exception(zend_get_parse_exception(), "Invalid numeric literal", E_PARSE); + zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); return T_ERROR; } } diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index fdba4b9f076..1204287ad93 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -996,7 +996,7 @@ static int zend_scan_escape_string(zval *zendlval, char *str, int len, char quot } if (!valid) { - zend_throw_exception(zend_get_parse_exception(), + zend_throw_exception(zend_get_parse_error(), "Invalid UTF-8 codepoint escape sequence", E_PARSE); zval_ptr_dtor(zendlval); return FAILURE; @@ -1007,7 +1007,7 @@ static int zend_scan_escape_string(zval *zendlval, char *str, int len, char quot /* per RFC 3629, UTF-8 can only represent 21 bits */ if (codepoint > 0x10FFFF || errno) { - zend_throw_exception(zend_get_parse_exception(), + zend_throw_exception(zend_get_parse_error(), "Invalid UTF-8 codepoint escape sequence: Codepoint too large", E_PARSE); zval_ptr_dtor(zendlval); return FAILURE; @@ -1635,7 +1635,7 @@ NEWLINE ("\r"|"\n"|"\r\n") * Because the lexing itself doesn't do that for us */ if (end != yytext + yyleng) { - zend_throw_exception(zend_get_parse_exception(), "Invalid numeric literal", E_PARSE); + zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); return T_ERROR; } } else { @@ -1651,7 +1651,7 @@ NEWLINE ("\r"|"\n"|"\r\n") } /* Also not an assert for the same reason */ if (end != yytext + yyleng) { - zend_throw_exception(zend_get_parse_exception(), + zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); return T_ERROR; } @@ -1660,7 +1660,7 @@ NEWLINE ("\r"|"\n"|"\r\n") } /* Also not an assert for the same reason */ if (end != yytext + yyleng) { - zend_throw_exception(zend_get_parse_exception(), "Invalid numeric literal", E_PARSE); + zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); return T_ERROR; } } diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 120274b2722..e6c7e5f4cd7 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -1496,10 +1496,10 @@ static void _soap_server_exception(soapServicePtr service, sdlFunctionPtr functi ZVAL_OBJ(&exception_object, EG(exception)); if (instanceof_function(Z_OBJCE(exception_object), soap_fault_class_entry)) { soap_server_fault_ex(function, &exception_object, NULL); - } else if (instanceof_function(Z_OBJCE(exception_object), zend_get_engine_exception())) { + } else if (instanceof_function(Z_OBJCE(exception_object), zend_get_error())) { if (service->send_errors) { zval rv; - zend_string *msg = zval_get_string(zend_read_property(zend_exception_get_base(), &exception_object, "message", sizeof("message")-1, 0, &rv)); + zend_string *msg = zval_get_string(zend_read_property(zend_get_exception_base(&exception_object), &exception_object, "message", sizeof("message")-1, 0, &rv)); add_soap_fault_ex(&exception_object, this_ptr, "Server", msg->val, NULL, NULL); zend_string_release(msg); } else { @@ -2596,13 +2596,13 @@ static int do_request(zval *this_ptr, xmlDoc *request, char *location, char *act add_soap_fault(this_ptr, "Client", "SoapClient::__doRequest() failed", NULL, NULL); ret = FALSE; } else if (Z_TYPE_P(response) != IS_STRING) { - if (EG(exception) && instanceof_function(EG(exception)->ce, zend_get_engine_exception())) { + if (EG(exception) && instanceof_function(EG(exception)->ce, zend_get_error())) { zval rv; zend_string *msg; zval exception_object; ZVAL_OBJ(&exception_object, EG(exception)); - msg = zval_get_string(zend_read_property(zend_exception_get_base(), &exception_object, "message", sizeof("message")-1, 0, &rv)); + msg = zval_get_string(zend_read_property(zend_get_exception_base(&exception_object), &exception_object, "message", sizeof("message")-1, 0, &rv)); /* change class */ EG(exception)->ce = soap_fault_class_entry; set_soap_fault(&exception_object, NULL, "Client", msg->val, NULL, NULL, NULL);