]> granicus.if.org Git - php/commitdiff
Fixed bug #33512 (Add missing support for isset()/unset() overloading to complement...
authorDmitry Stogov <dmitry@php.net>
Thu, 7 Jul 2005 16:07:09 +0000 (16:07 +0000)
committerDmitry Stogov <dmitry@php.net>
Thu, 7 Jul 2005 16:07:09 +0000 (16:07 +0000)
NEWS
Zend/zend.h
Zend/zend_API.c
Zend/zend_API.h
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_object_handlers.c
Zend/zend_objects.c
Zend/zend_reflection_api.c
ext/reflection/php_reflection.c

diff --git a/NEWS b/NEWS
index 20ff8f09ac81749de1094abda12d7e275dccd548..ebc98169544b48b3a6f5b79084f828f3d6bacc47 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,8 @@ PHP                                                                        NEWS
 - Fixed bug #33523 (Memory leak in xmlrpc_encode_request()). (Ilia)
 - Fixed bug #33520 (crash if safe_mode is on and session.save_path is changed).
   (Dmitry)
+- Fixed bug #33512 (Add missing support for isset()/unset() overloading to
+  complement the property get/set methods). (Dmitry)
 - Fixed bug #33491 (crash after extending MySQLi internal class). (Tony)
 - Fixed bug #33475 (cURL handle is not closed on curl_close(). (Ilia)
 - Fixed bug #33469 (Compile error undefined reference to ifx_checkAPI). (Jani)
index eb06003029937609bc919b853209660031883efb..13b14ea9c195ec14ccb299810745fea39c45bff6 100644 (file)
@@ -270,6 +270,8 @@ typedef struct _zend_object {
        HashTable *properties;
        unsigned int in_get:1;
        unsigned int in_set:1;
+       unsigned int in_unset:1;
+       unsigned int in_isset:1;
 } zend_object;
 
 typedef unsigned int zend_object_handle;
@@ -338,6 +340,8 @@ struct _zend_class_entry {
        union _zend_function *clone;
        union _zend_function *__get;
        union _zend_function *__set;
+       union _zend_function *__unset;
+       union _zend_function *__isset;
        union _zend_function *__call;
        union _zend_function *serialize_func;
        union _zend_function *unserialize_func;
index eef2254172e2d05109b7dd3a80dd9aff2005adb5..b4338c7f74214f9a49f2ce963944b8628e2d5d5e 100644 (file)
@@ -1408,6 +1408,10 @@ ZEND_API void zend_check_magic_method_implementation(zend_class_entry *ce, zend_
                zend_error(error_type, "Method %s::%s() must take exactly 1 argument", ce->name, ZEND_GET_FUNC_NAME);
        } else if (name_len == sizeof(ZEND_SET_FUNC_NAME) - 1 && !memcmp(lcname, ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME)) && fptr->common.num_args != 2) {
                zend_error(error_type, "Method %s::%s() must take exactly 2 arguments", ce->name, ZEND_SET_FUNC_NAME);
+       } else if (name_len == sizeof(ZEND_UNSET_FUNC_NAME) - 1 && !memcmp(lcname, ZEND_UNSET_FUNC_NAME, sizeof(ZEND_UNSET_FUNC_NAME)) && fptr->common.num_args != 1) {
+               zend_error(error_type, "Method %s::%s() must take exactly 1 argument", ce->name, ZEND_UNSET_FUNC_NAME);
+       } else if (name_len == sizeof(ZEND_ISSET_FUNC_NAME) - 1 && !memcmp(lcname, ZEND_ISSET_FUNC_NAME, sizeof(ZEND_ISSET_FUNC_NAME)) && fptr->common.num_args != 1) {
+               zend_error(error_type, "Method %s::%s() must take exactly 1 argument", ce->name, ZEND_ISSET_FUNC_NAME);
        } else if (name_len == sizeof(ZEND_CALL_FUNC_NAME) - 1 && !memcmp(lcname, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)) && fptr->common.num_args != 2) {
                zend_error(error_type, "Method %s::%s() must take exactly 2 arguments", ce->name, ZEND_CALL_FUNC_NAME);
        }
@@ -1422,7 +1426,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, zend_function_entr
        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, *__call = NULL;
+       zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL;
        char *lowercase_name;
        int fname_len;
        char *lc_class_name;
@@ -1529,6 +1533,10 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, zend_function_entr
                                __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))) {
                                __set = reg_function;
+                       } else if ((fname_len == sizeof(ZEND_UNSET_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_UNSET_FUNC_NAME, sizeof(ZEND_UNSET_FUNC_NAME))) {
+                               __unset = reg_function;
+                       } else if ((fname_len == sizeof(ZEND_ISSET_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_ISSET_FUNC_NAME, sizeof(ZEND_ISSET_FUNC_NAME))) {
+                               __isset = reg_function;
                        } else {
                                reg_function = NULL;
                        }
@@ -1560,6 +1568,8 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, zend_function_entr
                scope->__call = __call;
                scope->__get = __get;
                scope->__set = __set;
+               scope->__unset = __unset;
+               scope->__isset = __isset;
                if (ctor) {
                        ctor->common.fn_flags |= ZEND_ACC_CTOR;
                        if (ctor->common.fn_flags & ZEND_ACC_STATIC) {
@@ -1599,6 +1609,18 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, zend_function_entr
                        }
                        __set->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC;
                }
+               if (__unset) {
+                       if (__unset->common.fn_flags & ZEND_ACC_STATIC) {
+                               zend_error(error_type, "Method %s::%s() cannot be static", scope->name, __unset->common.function_name);
+                       }
+                       __unset->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC;
+               }
+               if (__isset) {
+                       if (__isset->common.fn_flags & ZEND_ACC_STATIC) {
+                               zend_error(error_type, "Method %s::%s() cannot be static", scope->name, __isset->common.function_name);
+                       }
+                       __isset->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC;
+               }
                efree(lc_class_name);
        }
        return SUCCESS;
index 33f8eeb870032ba0562f574eda963711aa0a36b4..d8833a237cc267f8ad9f68270a9986cb21892bd3 100644 (file)
@@ -118,7 +118,7 @@ typedef struct _zend_function_entry {
 
 #define INIT_CLASS_ENTRY(class_container, class_name, functions) INIT_OVERLOADED_CLASS_ENTRY(class_container, class_name, functions, NULL, NULL, NULL)
 
-#define INIT_OVERLOADED_CLASS_ENTRY(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset) \
+#define INIT_OVERLOADED_CLASS_ENTRY_EX(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset, handle_propunset, handle_propisset) \
        {                                                                                                                       \
                class_container.name = strdup(class_name);                              \
                class_container.name_length = sizeof(class_name) - 1;   \
@@ -131,6 +131,8 @@ typedef struct _zend_function_entry {
                class_container.__call = handle_fcall;  \
                class_container.__get = handle_propget; \
                class_container.__set = handle_propset; \
+               class_container.__unset = handle_propunset;     \
+               class_container.__isset = handle_propisset;     \
                class_container.serialize = NULL;       \
                class_container.unserialize = NULL;     \
                class_container.parent = NULL;          \
@@ -141,6 +143,9 @@ typedef struct _zend_function_entry {
                class_container.module = NULL;          \
        }
 
+#define INIT_OVERLOADED_CLASS_ENTRY(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset) \
+       INIT_OVERLOADED_CLASS_ENTRY_EX(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset, NULL, NULL)
+
 int zend_next_free_module(void);
 
 BEGIN_EXTERN_C()
index 94442bee98c50b943878cc6ae4b78ae92b7e2568..f50d9d01a6689e5ed33ae719ea896376d1507e12 100644 (file)
@@ -1119,6 +1119,10 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n
                                CG(active_class_entry)->__get = (zend_function *) CG(active_op_array);
                        } else if ((name_len == sizeof(ZEND_SET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME)))) {
                                CG(active_class_entry)->__set = (zend_function *) CG(active_op_array);
+                       } else if ((name_len == sizeof(ZEND_UNSET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_UNSET_FUNC_NAME, sizeof(ZEND_UNSET_FUNC_NAME)))) {
+                               CG(active_class_entry)->__unset = (zend_function *) CG(active_op_array);
+                       } else if ((name_len == sizeof(ZEND_ISSET_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_ISSET_FUNC_NAME, sizeof(ZEND_ISSET_FUNC_NAME)))) {
+                               CG(active_class_entry)->__isset = (zend_function *) CG(active_op_array);
                        } else if (!(fn_flags & ZEND_ACC_STATIC)) {
                                CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC;
                        }
@@ -1762,6 +1766,12 @@ static void do_inherit_parent_constructor(zend_class_entry *ce)
        if (!ce->__set) {
                ce->__set = ce->parent->__set;
        }
+       if (!ce->__unset) {
+               ce->__unset = ce->parent->__unset;
+       }
+       if (!ce->__isset) {
+               ce->__isset = ce->parent->__isset;
+       }
        if (!ce->__call) {
                ce->__call = ce->parent->__call;
        }
@@ -3932,6 +3942,8 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
                ce->clone = NULL;
                ce->__get = NULL;
                ce->__set = NULL;
+               ce->__unset = NULL;
+               ce->__isset = NULL;
                ce->__call = NULL;
                ce->create_object = NULL;
                ce->get_iterator = NULL;
index 97352fdb28fec6c049861920f0987e75910523a0..4ac174037472bc4db696064a06506bcb233b8ce7 100644 (file)
@@ -680,6 +680,8 @@ END_EXTERN_C()
 #define ZEND_DESTRUCTOR_FUNC_NAME      "__destruct"
 #define ZEND_GET_FUNC_NAME          "__get"
 #define ZEND_SET_FUNC_NAME          "__set"
+#define ZEND_UNSET_FUNC_NAME          "__unset"
+#define ZEND_ISSET_FUNC_NAME          "__isset"
 #define ZEND_CALL_FUNC_NAME         "__call"
 #define ZEND_AUTOLOAD_FUNC_NAME     "__autoload"
 
index f8e01920e022b94eb6397988765e139fb14fc263..c0dbe82a0d95c7726cb1f87f53415443204dc282 100644 (file)
@@ -109,6 +109,40 @@ static int zend_std_call_setter(zval *object, zval *member, zval *value TSRMLS_D
        }
 }
 
+static void zend_std_call_unsetter(zval *object, zval *member TSRMLS_DC)
+{
+       zend_class_entry *ce = Z_OBJCE_P(object);
+       
+       /* __unset handler is called with one argument:
+             property name
+       */
+       
+       SEPARATE_ARG_IF_REF(member);
+
+       zend_call_method_with_1_params(&object, ce, &ce->__unset, ZEND_UNSET_FUNC_NAME, NULL, member);
+
+       zval_ptr_dtor(&member);
+}
+
+static zval *zend_std_call_issetter(zval *object, zval *member TSRMLS_DC)
+{
+       zval *retval = NULL;
+       zend_class_entry *ce = Z_OBJCE_P(object);
+       
+       /* __isset handler is called with one argument:
+             property name
+
+          it should return whether the property is set or not
+       */
+       
+       SEPARATE_ARG_IF_REF(member);
+
+       zend_call_method_with_1_params(&object, ce, &ce->__isset, ZEND_ISSET_FUNC_NAME, &retval, member);
+
+       zval_ptr_dtor(&member);
+
+       return retval;
+}
 
 static int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC)
 {
@@ -420,18 +454,25 @@ static int zend_std_has_dimension(zval *object, zval *offset, int check_empty TS
        if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
                SEPARATE_ARG_IF_REF(offset);
                zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
-               zval_ptr_dtor(&offset);
                if (retval) {
                        result = i_zend_is_true(retval);
                        zval_ptr_dtor(&retval);
-                       return result;
+                       if (check_empty && result && !EG(exception)) {
+                               zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
+                               if (retval) {
+                                       result = i_zend_is_true(retval);
+                                       zval_ptr_dtor(&retval);
+                               }
+                       }
                } else {
-                       return 0;
+                       result = 0;
                }
+               zval_ptr_dtor(&offset);
        } else {
                zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
                return 0;
        }
+       return result;
 }
 
 
@@ -482,23 +523,35 @@ static zval **zend_std_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC
 static void zend_std_unset_property(zval *object, zval *member TSRMLS_DC)
 {
        zend_object *zobj;
-       zval tmp_member;
+       zval *tmp_member = NULL;
        zend_property_info *property_info;
+       zend_bool use_unset;
        
        zobj = Z_OBJ_P(object);
+       use_unset = (zobj->ce->__unset && !zobj->in_unset);
 
        if (member->type != IS_STRING) {
-               tmp_member = *member;
-               zval_copy_ctor(&tmp_member);
-               convert_to_string(&tmp_member);
-               member = &tmp_member;
+               ALLOC_ZVAL(tmp_member);
+               *tmp_member = *member;
+               INIT_PZVAL(tmp_member);
+               zval_copy_ctor(tmp_member);
+               convert_to_string(tmp_member);
+               member = tmp_member;
        }
 
        property_info = zend_get_property_info(zobj->ce, member, 0 TSRMLS_CC);
        
-       zend_hash_del(zobj->properties, property_info->name, property_info->name_length+1);
-       if (member == &tmp_member) {
-               zval_dtor(member);
+       if (!property_info || zend_hash_del(zobj->properties, property_info->name, property_info->name_length+1) == FAILURE) {
+               if (use_unset) {
+                       /* have unseter - try with it! */
+                       zobj->in_unset = 1; /* prevent circular unsetting */
+                       zend_std_call_unsetter(object, member TSRMLS_CC);
+                       zobj->in_unset = 0;
+               }
+       }
+
+       if (tmp_member) {
+               zval_ptr_dtor(&tmp_member);
        }
 }
 
@@ -837,27 +890,51 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists
        zend_object *zobj;
        int result;
        zval **value;
-       zval tmp_member;
+       zval *tmp_member = NULL;
        zend_property_info *property_info;
+       zend_bool use_isset;
        
        zobj = Z_OBJ_P(object);
+       use_isset = (zobj->ce->__isset && !zobj->in_isset);
 
        if (member->type != IS_STRING) {
-               tmp_member = *member;
-               zval_copy_ctor(&tmp_member);
-               convert_to_string(&tmp_member);
-               member = &tmp_member;
+               ALLOC_ZVAL(tmp_member);
+               *tmp_member = *member;
+               INIT_PZVAL(tmp_member);
+               zval_copy_ctor(tmp_member);
+               convert_to_string(tmp_member);
+               member = tmp_member;
        }
 
 #if DEBUG_OBJECT_HANDLERS
        fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
 #endif                 
 
-       if (!(property_info = zend_get_property_info(zobj->ce, member, 1 TSRMLS_CC))) {
-               return 0;
-       }
+       property_info = zend_get_property_info(zobj->ce, member, 1 TSRMLS_CC);
+
+       if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &value) == FAILURE) {
+               result = 0;
+               if (use_isset && (has_set_exists != 2)) {
+                       zval *rv;
 
-       if (zend_hash_find(zobj->properties, property_info->name, property_info->name_length+1, (void **) &value) == SUCCESS) {
+                       /* have issetter - try with it! */
+                       zobj->in_isset = 1; /* prevent circular getting */
+                       rv = zend_std_call_issetter(object, member TSRMLS_CC);
+                       zobj->in_isset = 0;
+                       if (rv) {
+                               result = zend_is_true(rv);
+                               zval_ptr_dtor(&rv);
+                               if (has_set_exists && result && !EG(exception) && zobj->ce->__get && !zobj->in_get) {
+                                       rv = zend_std_call_getter(object, member TSRMLS_CC);
+                                       if (rv) {
+                                               rv->refcount++;
+                                               result = i_zend_is_true(rv);
+                                               zval_ptr_dtor(&rv);
+                                       }
+                               }
+                       }
+               }
+       } else {
                switch (has_set_exists) {
                case 0:
                        result = (Z_TYPE_PP(value) != IS_NULL);
@@ -869,12 +946,10 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists
                        result = 1;
                        break;
                }
-       } else {
-               result = 0;
        }
 
-       if (member == &tmp_member) {
-               zval_dtor(member);
+       if (tmp_member) {
+               zval_ptr_dtor(&tmp_member);
        }
        return result;
 }
index 41ee18f9c0568117f53fd20c76a4e8ab41319c09..e9b68b13afc4f6dc3e3bd8c9d7be2c7331516926 100644 (file)
@@ -103,6 +103,8 @@ ZEND_API zend_object_value zend_objects_new(zend_object **object, zend_class_ent
        retval.handlers = &std_object_handlers;
        (*object)->in_get = 0;
        (*object)->in_set = 0;
+       (*object)->in_unset = 0;
+       (*object)->in_isset = 0;
        return retval;
 }
 
index 970ab38d98470bd11136689fa7ef652ebdf649f8..19d32fa3516ec37bc0cceae86d03e0222ad6caf5 100644 (file)
@@ -205,6 +205,8 @@ static void reflection_objects_clone(void *object, void **object_clone TSRMLS_DC
        (*intern_clone)->zo.ce = intern->zo.ce;
        (*intern_clone)->zo.in_get = 0;
        (*intern_clone)->zo.in_set = 0;
+       (*intern_clone)->zo.in_unset = 0;
+       (*intern_clone)->zo.in_isset = 0;
        ALLOC_HASHTABLE((*intern_clone)->zo.properties);
        (*intern_clone)->ptr = intern->ptr;
        (*intern_clone)->free_ptr = intern->free_ptr;
@@ -224,6 +226,8 @@ static zend_object_value reflection_objects_new(zend_class_entry *class_type TSR
        intern->zo.ce = class_type;
        intern->zo.in_get = 0;
        intern->zo.in_set = 0;
+       intern->zo.in_unset = 0;
+       intern->zo.in_isset = 0;
        intern->ptr = NULL;
        intern->obj = NULL;
        intern->free_ptr = 0;
index 970ab38d98470bd11136689fa7ef652ebdf649f8..19d32fa3516ec37bc0cceae86d03e0222ad6caf5 100644 (file)
@@ -205,6 +205,8 @@ static void reflection_objects_clone(void *object, void **object_clone TSRMLS_DC
        (*intern_clone)->zo.ce = intern->zo.ce;
        (*intern_clone)->zo.in_get = 0;
        (*intern_clone)->zo.in_set = 0;
+       (*intern_clone)->zo.in_unset = 0;
+       (*intern_clone)->zo.in_isset = 0;
        ALLOC_HASHTABLE((*intern_clone)->zo.properties);
        (*intern_clone)->ptr = intern->ptr;
        (*intern_clone)->free_ptr = intern->free_ptr;
@@ -224,6 +226,8 @@ static zend_object_value reflection_objects_new(zend_class_entry *class_type TSR
        intern->zo.ce = class_type;
        intern->zo.in_get = 0;
        intern->zo.in_set = 0;
+       intern->zo.in_unset = 0;
+       intern->zo.in_isset = 0;
        intern->ptr = NULL;
        intern->obj = NULL;
        intern->free_ptr = 0;