diff -rU3 php-5.3.0alpha2-orig/Zend/zend_API.c php-5.3.0alpha2/Zend/zend_API.c --- php-5.3.0alpha2-orig/Zend/zend_API.c 2008-08-22 16:51:30.000000000 +0200 +++ php-5.3.0alpha2/Zend/zend_API.c 2008-10-05 10:24:34.011719825 +0200 @@ -1761,6 +1761,10 @@ !memcmp(lcname, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && fptr->common.num_args != 0 ) { zend_error(error_type, "Method %s::%s() cannot take arguments", ce->name, ZEND_TOSTRING_FUNC_NAME); + } else if (name_len == sizeof(ZEND_CAST_FUNC_NAME) - 1 && + !memcmp(lcname, ZEND_CAST_FUNC_NAME, sizeof(ZEND_CAST_FUNC_NAME)-1) && fptr->common.num_args != 1 + ) { + zend_error(error_type, "Method %s::%s() must take exactly 1 argument", ce->name, ZEND_CAST_FUNC_NAME); } } /* }}} */ @@ -1774,7 +1778,7 @@ int count=0, unload=0; HashTable *target_function_table = function_table; int error_type; - zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL; + zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL, *__cast = NULL; char *lowercase_name; int fname_len; char *lc_class_name = NULL; @@ -1896,6 +1900,8 @@ __callstatic = reg_function; } else if ((fname_len == sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME))) { __tostring = reg_function; + } else if ((fname_len == sizeof(ZEND_CAST_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_CAST_FUNC_NAME, sizeof(ZEND_CAST_FUNC_NAME))) { + __cast = reg_function; } else if ((fname_len == sizeof(ZEND_GET_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_GET_FUNC_NAME, sizeof(ZEND_GET_FUNC_NAME))) { __get = reg_function; } else if ((fname_len == sizeof(ZEND_SET_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME))) { @@ -1939,6 +1945,7 @@ scope->__call = __call; scope->__callstatic = __callstatic; scope->__tostring = __tostring; + scope->__cast = __cast; scope->__get = __get; scope->__set = __set; scope->__unset = __unset; @@ -1982,6 +1989,12 @@ } __tostring->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC; } + if (__cast) { + if (__cast->common.fn_flags & ZEND_ACC_STATIC) { + zend_error(error_type, "Method %s::%s() cannot be static", scope->name, __cast->common.function_name); + } + __cast->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC; + } if (__get) { if (__get->common.fn_flags & ZEND_ACC_STATIC) { zend_error(error_type, "Method %s::%s() cannot be static", scope->name, __get->common.function_name); diff -rU3 php-5.3.0alpha2-orig/Zend/zend_API.h php-5.3.0alpha2/Zend/zend_API.h --- php-5.3.0alpha2-orig/Zend/zend_API.h 2008-08-12 23:45:52.000000000 +0200 +++ php-5.3.0alpha2/Zend/zend_API.h 2008-10-05 10:24:59.940490512 +0200 @@ -180,6 +180,7 @@ class_container.__call = handle_fcall; \ class_container.__callstatic = NULL; \ class_container.__tostring = NULL; \ + class_container.__cast = NULL; \ class_container.__get = handle_propget; \ class_container.__set = handle_propset; \ class_container.__unset = handle_propunset; \ diff -rU3 php-5.3.0alpha2-orig/Zend/zend_compile.c php-5.3.0alpha2/Zend/zend_compile.c --- php-5.3.0alpha2-orig/Zend/zend_compile.c 2008-08-29 12:17:08.000000000 +0200 +++ php-5.3.0alpha2/Zend/zend_compile.c 2008-10-05 10:22:13.990378347 +0200 @@ -1236,6 +1236,10 @@ if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { zend_error(E_WARNING, "The magic method __toString() must have public visibility and can not be static"); } + } else if ((name_len == sizeof(ZEND_CAST_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_CAST_FUNC_NAME, sizeof(ZEND_CAST_FUNC_NAME)-1))) { + if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { + zend_error(E_WARNING, "The magic method __cast() must have public visibility and can not be static"); + } } } else { char *short_class_name; @@ -1303,6 +1307,11 @@ zend_error(E_WARNING, "The magic method __toString() must have public visibility and can not be static"); } CG(active_class_entry)->__tostring = (zend_function *) CG(active_op_array); + } else if ((name_len == sizeof(ZEND_CAST_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_CAST_FUNC_NAME, sizeof(ZEND_CAST_FUNC_NAME)-1))) { + if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { + zend_error(E_WARNING, "The magic method __cast() must have public visibility and can not be static"); + } + CG(active_class_entry)->__cast = (zend_function *) CG(active_op_array); } else if (!(fn_flags & ZEND_ACC_STATIC)) { CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC; } diff -rU3 php-5.3.0alpha2-orig/Zend/zend_compile.h php-5.3.0alpha2/Zend/zend_compile.h --- php-5.3.0alpha2-orig/Zend/zend_compile.h 2008-08-29 20:12:47.000000000 +0200 +++ php-5.3.0alpha2/Zend/zend_compile.h 2008-10-05 10:18:47.140387882 +0200 @@ -742,6 +742,7 @@ #define ZEND_CALL_FUNC_NAME "__call" #define ZEND_CALLSTATIC_FUNC_NAME "__callstatic" #define ZEND_TOSTRING_FUNC_NAME "__tostring" +#define ZEND_CAST_FUNC_NAME "__cast" #define ZEND_AUTOLOAD_FUNC_NAME "__autoload" /* The following constants may be combined in CG(compiler_options) diff -rU3 php-5.3.0alpha2-orig/Zend/zend.h php-5.3.0alpha2/Zend/zend.h --- php-5.3.0alpha2-orig/Zend/zend.h 2008-08-15 21:47:23.000000000 +0200 +++ php-5.3.0alpha2/Zend/zend.h 2008-10-05 10:25:21.791668877 +0200 @@ -437,6 +437,7 @@ union _zend_function *__call; union _zend_function *__callstatic; union _zend_function *__tostring; + union _zend_function *__cast; union _zend_function *serialize_func; union _zend_function *unserialize_func; diff -rU3 php-5.3.0alpha2-orig/Zend/zend_object_handlers.c php-5.3.0alpha2/Zend/zend_object_handlers.c --- php-5.3.0alpha2-orig/Zend/zend_object_handlers.c 2008-08-14 23:36:56.000000000 +0200 +++ php-5.3.0alpha2/Zend/zend_object_handlers.c 2008-10-05 10:45:31.291628410 +0200 @@ -1197,9 +1197,57 @@ zval *retval; zend_class_entry *ce; + ce = Z_OBJCE_P(readobj); + if (ce->__cast) { + // HANDLE __cast($type) + zval type_str; + const char *type_c_str = "unknown"; + + switch(type) { + case IS_STRING: type_c_str = "string"; break; + case IS_BOOL: type_c_str = "bool"; break; + case IS_LONG: type_c_str = "int"; break; + case IS_DOUBLE: type_c_str = "float"; break; + } + + ZVAL_STRINGL(&type_str, type_c_str, strlen(type_c_str), 0); + + if ((zend_call_method_with_1_params(&readobj, ce, &ce->__cast, "__cast", &retval, &type_str)) || EG(exception)) { + if (EG(exception)) { + if (retval) { + zval_ptr_dtor(&retval); + retval = NULL; + } + + zend_error(E_ERROR, "Method %s::__cast() must not throw an exception", ce->name); + return FAILURE; + } + if (Z_TYPE_P(retval) == type) { + INIT_PZVAL(writeobj); + if (readobj == writeobj) { + zval_dtor(readobj); + } + 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); + if (readobj == writeobj) { + zval_dtor(readobj); + } + ZVAL_EMPTY_STRING(writeobj); + zend_error(E_RECOVERABLE_ERROR, "Method %s::__cast() must return the requested type of value", ce->name); + return SUCCESS; + } + } + } + switch (type) { case IS_STRING: - ce = Z_OBJCE_P(readobj); +// ce = Z_OBJCE_P(readobj); if (ce->__tostring && (zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) { if (EG(exception)) {