From: Marcus Boerger Date: Wed, 5 Oct 2005 19:02:27 +0000 (+0000) Subject: - Have __toString() be called if available in all places an object is used X-Git-Tag: RELEASE_0_9_1~245 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7c083fcc0790105e4d56f91d68401e95e3f8a769;p=php - Have __toString() be called if available in all places an object is used as a string. # # Note that "Object #" is no longer afallback for debugging purpose use # var_dump, which was made for debugging. If you used this to grab the id # of an object you can never rely on this. For object storage look at SPL's # ObjectStorage class. # # Note the signature change in the cast handler: # # int (*cast_t)(zval *readobj, zval *writeobj, int type, int should_free TSRMLS_DC); # int (*cast_t)(zval *readobj, zval *retval, int type TSRMLS_DC); --- diff --git a/Zend/zend.c b/Zend/zend.c index 37108066ac..8cafecfdc5 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -347,29 +347,22 @@ ZEND_API void zend_make_string_zval(zval *expr, zval *expr_copy, int *use_copy) case IS_OBJECT: { TSRMLS_FETCH(); - if (Z_OBJ_HANDLER_P(expr, cast_object)) { - if(Z_OBJ_HANDLER_P(expr, cast_object)(expr, expr_copy, IS_STRING, 0 TSRMLS_CC) == SUCCESS) { - break; - } + + if(Z_OBJ_HT_P(expr)->cast_object && Z_OBJ_HANDLER_P(expr, cast_object)(expr, expr_copy, IS_STRING TSRMLS_CC) == SUCCESS) { + break; } if (Z_OBJ_HANDLER_P(expr, get)) { zval *z = Z_OBJ_HANDLER_P(expr, get)(expr TSRMLS_CC); if(Z_TYPE_P(z) != IS_OBJECT) { - zend_make_printable_zval(z, expr_copy, use_copy); + zend_make_string_zval(z, expr_copy, use_copy); FREE_ZVAL(z); break; } } - if (EG(exception)) { - expr_copy->value.str.len = 0; - expr_copy->value.str.val = STR_EMPTY_ALLOC(); - break; - } - + zend_error(EG(exception) ? E_ERROR : E_RECOVERABLE_ERROR, "Object of class %v could not be converted to string", Z_OBJCE_P(expr)->name); + ZVAL_EMPTY_STRING(expr_copy); + break; } - expr_copy->value.str.val = (char *) emalloc(sizeof("Object id #")-1 + MAX_LENGTH_OF_LONG); - expr_copy->value.str.len = sprintf(expr_copy->value.str.val, "Object id #%ld", (long)expr->value.obj.handle); - break; default: *expr_copy = *expr; zval_copy_ctor(expr_copy); @@ -416,10 +409,8 @@ ZEND_API void zend_make_printable_zval(zval *expr, zval *expr_copy, int *use_cop expr_copy->value.str.val = estrndup("Array", expr_copy->value.str.len); break; case IS_OBJECT: - if (Z_OBJ_HANDLER_P(expr, cast_object)) { - if(Z_OBJ_HANDLER_P(expr, cast_object)(expr, expr_copy, IS_STRING, 0 TSRMLS_CC) == SUCCESS) { - break; - } + if(Z_OBJ_HT_P(expr)->cast_object && Z_OBJ_HANDLER_P(expr, cast_object)(expr, expr_copy, IS_STRING TSRMLS_CC) == SUCCESS) { + break; } if (Z_OBJ_HANDLER_P(expr, get)) { zval *z = Z_OBJ_HANDLER_P(expr, get)(expr TSRMLS_CC); @@ -429,13 +420,8 @@ ZEND_API void zend_make_printable_zval(zval *expr, zval *expr_copy, int *use_cop break; } } - if (EG(exception)) { - expr_copy->value.str.len = 0; - expr_copy->value.str.val = STR_EMPTY_ALLOC(); - break; - } - expr_copy->value.str.val = (char *) emalloc(sizeof("Object id #")-1 + MAX_LENGTH_OF_LONG); - expr_copy->value.str.len = sprintf(expr_copy->value.str.val, "Object id #%ld", (long)expr->value.obj.handle); + zend_error(EG(exception) ? E_ERROR : E_RECOVERABLE_ERROR, "Object of class %v could not be converted to string", Z_OBJCE_P(expr)->name); + ZVAL_EMPTY_STRING(expr_copy); break; case IS_DOUBLE: *expr_copy = *expr; @@ -467,10 +453,8 @@ ZEND_API void zend_make_unicode_zval(zval *expr, zval *expr_copy, int *use_copy) } switch (expr->type) { case IS_OBJECT: - if (Z_OBJ_HANDLER_P(expr, cast_object)) { - if(Z_OBJ_HANDLER_P(expr, cast_object)(expr, expr_copy, IS_UNICODE, 0 TSRMLS_CC) == SUCCESS) { - break; - } + if(Z_OBJ_HT_P(expr)->cast_object && Z_OBJ_HANDLER_P(expr, cast_object)(expr, expr_copy, IS_UNICODE TSRMLS_CC) == SUCCESS) { + break; } if (Z_OBJ_HANDLER_P(expr, get)) { zval *z = Z_OBJ_HANDLER_P(expr, get)(expr TSRMLS_CC); @@ -480,13 +464,8 @@ ZEND_API void zend_make_unicode_zval(zval *expr, zval *expr_copy, int *use_copy) break; } } - if (EG(exception)) { - expr_copy->value.ustr.len = 0; - expr_copy->value.ustr.val = USTR_MAKE(""); - break; - } - expr_copy->value.ustr.val = eumalloc(sizeof("Object id #")-1 + MAX_LENGTH_OF_LONG + 1); - expr_copy->value.ustr.len = u_sprintf(expr_copy->value.ustr.val, "Object id #%ld", (long)expr->value.obj.handle); + zend_error(EG(exception) ? E_ERROR : E_RECOVERABLE_ERROR, "Object of class %v could not be converted to string", Z_OBJCE_P(expr)->name); + ZVAL_EMPTY_STRING(expr_copy); break; default: *expr_copy = *expr; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 3cf8481dbd..6a34911fce 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -432,7 +432,7 @@ static char *zend_parse_arg_impl(zval **arg, va_list *va, char **spec, char T_ar case IS_OBJECT: { if (Z_OBJ_HANDLER_PP(arg, cast_object)) { SEPARATE_ZVAL_IF_NOT_REF(arg); - if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_STRING, 0 TSRMLS_CC) == SUCCESS) { + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_STRING TSRMLS_CC) == SUCCESS) { *pl = Z_STRLEN_PP(arg); *p = Z_STRVAL_PP(arg); break; @@ -481,7 +481,7 @@ static char *zend_parse_arg_impl(zval **arg, va_list *va, char **spec, char T_ar case IS_OBJECT: { if (Z_OBJ_HANDLER_PP(arg, cast_object)) { SEPARATE_ZVAL_IF_NOT_REF(arg); - if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_BINARY, 0 TSRMLS_CC) == SUCCESS) { + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_BINARY TSRMLS_CC) == SUCCESS) { *pl = Z_BINLEN_PP(arg); *p = Z_BINVAL_PP(arg); break; @@ -525,7 +525,7 @@ static char *zend_parse_arg_impl(zval **arg, va_list *va, char **spec, char T_ar case IS_OBJECT: { if (Z_OBJ_HANDLER_PP(arg, cast_object)) { SEPARATE_ZVAL_IF_NOT_REF(arg); - if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_UNICODE, 0 TSRMLS_CC) == SUCCESS) { + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_UNICODE TSRMLS_CC) == SUCCESS) { *pl = Z_USTRLEN_PP(arg); *p = Z_USTRVAL_PP(arg); break; @@ -588,7 +588,7 @@ static char *zend_parse_arg_impl(zval **arg, va_list *va, char **spec, char T_ar case IS_OBJECT: { if (Z_OBJ_HANDLER_PP(arg, cast_object)) { SEPARATE_ZVAL_IF_NOT_REF(arg); - if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, T_arg_type, 0 TSRMLS_CC) == SUCCESS) { + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, T_arg_type TSRMLS_CC) == SUCCESS) { *(char**)p = Z_UNIVAL_PP(arg); *pl = Z_UNILEN_PP(arg); *type = Z_TYPE_PP(arg); @@ -651,12 +651,12 @@ static char *zend_parse_arg_impl(zval **arg, va_list *va, char **spec, char T_ar if (Z_OBJ_HANDLER_PP(arg, cast_object)) { SEPARATE_ZVAL_IF_NOT_REF(arg); if (UG(unicode)) { - if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_UNICODE, 0 TSRMLS_CC) == SUCCESS) { + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_UNICODE TSRMLS_CC) == SUCCESS) { RETURN_AS_UNICODE(arg, p, pl, type); break; } } else { - if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_STRING, 0 TSRMLS_CC) == SUCCESS) { + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_STRING TSRMLS_CC) == SUCCESS) { RETURN_AS_STRING(arg, p, pl, type); break; } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 7095ac1b15..3096a2a38e 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -114,7 +114,7 @@ static inline int i_zend_is_true(zval *op) if (Z_OBJ_HT_P(op)->cast_object) { zval tmp; - if (Z_OBJ_HT_P(op)->cast_object(op, &tmp, IS_BOOL, 0 TSRMLS_CC) == SUCCESS) { + if (Z_OBJ_HT_P(op)->cast_object(op, &tmp, IS_BOOL TSRMLS_CC) == SUCCESS) { result = Z_LVAL(tmp); break; } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index de0d27213d..52cad9c7ef 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1006,29 +1006,35 @@ int zend_std_object_get_class_name(zval *object, char **class_name, zend_uint *c return SUCCESS; } -ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type, int should_free TSRMLS_DC) +ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) { zval *retval; zend_class_entry *ce; - + switch (type) { case IS_STRING: case IS_UNICODE: ce = Z_OBJCE_P(readobj); if (ce->__tostring && zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval)) { + if (EG(exception)) { + zval_ptr_dtor(&retval); + zend_error(E_ERROR, "Method %v::__toString() must not throw an exception", ce->name); + return FAILURE; + } if (Z_TYPE_P(retval) == (UG(unicode)?IS_UNICODE:IS_STRING)) { - *writeobj = *retval; - zval_copy_ctor(writeobj); - zval_ptr_dtor(&retval); INIT_PZVAL(writeobj); + ZVAL_ZVAL(writeobj, retval, 1, 1); if (Z_TYPE_P(writeobj) != type) { convert_to_explicit_type(writeobj, type); } return SUCCESS; } else { zval_ptr_dtor(&retval); + INIT_PZVAL(writeobj); + ZVAL_EMPTY_STRING(writeobj); zend_error(E_RECOVERABLE_ERROR, "Method %v::__toString() must return a string value", ce->name); + return SUCCESS; } } return FAILURE; diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 3ed13cb733..017874ba40 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -97,7 +97,10 @@ typedef zend_object_value (*zend_object_clone_obj_t)(zval *object TSRMLS_DC); typedef zend_class_entry *(*zend_object_get_class_entry_t)(zval *object TSRMLS_DC); typedef int (*zend_object_get_class_name_t)(zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC); typedef int (*zend_object_compare_t)(zval *object1, zval *object2 TSRMLS_DC); -typedef int (*zend_object_cast_t)(zval *readobj, zval *writeobj, int type, int should_free TSRMLS_DC); + +/* Cast an object to some other type + */ +typedef int (*zend_object_cast_t)(zval *readobj, zval *retval, int type TSRMLS_DC); /* updates *count to hold the number of elements present and returns SUCCESS. * Returns FAILURE if the object does not have any sense of overloaded dimensions */ @@ -138,7 +141,7 @@ ZEND_API zval **zend_std_get_static_property(zend_class_entry *ce, zend_uchar ty ZEND_API zend_bool zend_std_unset_static_property(zend_class_entry *ce, zend_uchar type, void *property_name, int property_name_len TSRMLS_DC); ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce, zval *member, int silent TSRMLS_DC); -ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type, int should_free TSRMLS_DC); +ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC); #define IS_ZEND_STD_OBJECT(z) ((z).type == IS_OBJECT && (Z_OBJ_HT((z))->get_class_entry != NULL)) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index fda1e3ccaa..78f7c0d971 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -317,11 +317,17 @@ ZEND_API void convert_scalar_to_number(zval *op TSRMLS_DC) } -#define convert_object_to_type(op, ctype, conv_func) \ +#define convert_object_to_type(op, ctype, conv_func) \ if (Z_OBJ_HT_P(op)->cast_object) { \ - if (Z_OBJ_HT_P(op)->cast_object(op, op, ctype, 1 TSRMLS_CC) == SUCCESS) { \ - op->type = ctype; \ + zval *org; \ + ALLOC_ZVAL(org); \ + *org = *op; \ + if (Z_OBJ_HT_P(op)->cast_object(org, op, ctype TSRMLS_CC) == FAILURE) { \ + zend_error(E_RECOVERABLE_ERROR, \ + "Object of class %v could not be converted to " # ctype, Z_OBJCE_P(org)->name); \ + INIT_ZVAL(*op); \ } \ + zval_dtor(org); \ } else { \ if(Z_OBJ_HT_P(op)->get) { \ zval *newop = Z_OBJ_HT_P(op)->get(op TSRMLS_CC); \ @@ -496,10 +502,16 @@ ZEND_API void convert_to_null(zval *op) { if (op->type == IS_OBJECT) { if (Z_OBJ_HT_P(op)->cast_object) { + zval *org; TSRMLS_FETCH(); - if (Z_OBJ_HT_P(op)->cast_object(op, op, IS_NULL, 1 TSRMLS_CC) == SUCCESS) { + + ALLOC_ZVAL(org); + *org = *op; + if (Z_OBJ_HT_P(op)->cast_object(org, op, IS_NULL TSRMLS_CC) == SUCCESS) { + zval_dtor(org); return; } + *op = *org; } }