]> granicus.if.org Git - php/commitdiff
Allow recursive calls to __get/__set for different properties
authorDmitry Stogov <dmitry@php.net>
Tue, 15 Nov 2005 15:21:47 +0000 (15:21 +0000)
committerDmitry Stogov <dmitry@php.net>
Tue, 15 Nov 2005 15:21:47 +0000 (15:21 +0000)
Zend/zend.h
Zend/zend_object_handlers.c
Zend/zend_objects.c
Zend/zend_reflection_api.c
ext/dom/php_dom.c
ext/mysqli/mysqli.c
ext/reflection/php_reflection.c
ext/xmlreader/php_xmlreader.c
ext/xsl/php_xsl.c

index 58311ad07a1adf6def073f3cdf1b8d94697d4dc5..de932d8c5e78f104d8cd908d859a3c59ee199d83 100644 (file)
@@ -277,13 +277,18 @@ void zend_error_noreturn(int type, const char *format, ...) __attribute__ ((nore
 typedef struct _zval_struct zval;
 typedef struct _zend_class_entry zend_class_entry;
 
+typedef struct _zend_guard {
+       zend_bool in_get;
+       zend_bool in_set;
+       zend_bool in_unset;
+       zend_bool in_isset;
+       zend_bool dummy; /* sizeof(zend_guard) must not be equal to sizeof(void*) */
+} zend_guard;
+
 typedef struct _zend_object {
        zend_class_entry *ce;
        HashTable *properties;
-       unsigned int in_get:1;
-       unsigned int in_set:1;
-       unsigned int in_unset:1;
-       unsigned int in_isset:1;
+       HashTable *guards; /* protects from __get/__set ... recursion */
 } zend_object;
 
 typedef unsigned int zend_object_handle;
index 2dd0238182f6a09523776e70a41932349bd77e66..043835416e7c0bbca2676a19fdd9f8218815f1b3 100644 (file)
@@ -238,7 +238,7 @@ ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce
                EG(std_property_info).flags = ZEND_ACC_PUBLIC;
                EG(std_property_info).name = Z_UNIVAL_P(member);
                EG(std_property_info).name_length = Z_UNILEN_P(member);
-               EG(std_property_info).h = zend_u_get_hash_value(Z_TYPE_P(member), EG(std_property_info).name, EG(std_property_info).name_length+1);
+               EG(std_property_info).h = h;
                property_info = &EG(std_property_info);
        }
        return property_info;
@@ -276,6 +276,30 @@ ZEND_API int zend_check_property_access(zend_object *zobj, zend_uchar utype, voi
        return zend_verify_property_access(property_info, zobj->ce TSRMLS_CC) ? SUCCESS : FAILURE;
 }
 
+static int zend_get_property_guard(zend_object *zobj, zend_property_info *property_info, zval *member, zend_guard **pguard TSRMLS_DC)
+{
+       zend_property_info info;
+       zend_guard stub;
+
+       if (!property_info) {
+               property_info = &info;
+               info.name = Z_UNIVAL_P(member);
+               info.name_length = Z_UNILEN_P(member);
+               info.h = zend_u_get_hash_value(Z_TYPE_P(member), Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
+       }
+       if (!zobj->guards) {
+               ALLOC_HASHTABLE(zobj->guards);
+               zend_u_hash_init(zobj->guards, 0, NULL, NULL, 0, UG(unicode));
+       } else if (zend_u_hash_quick_find(zobj->guards, UG(unicode)?IS_UNICODE:IS_STRING, property_info->name, property_info->name_length+1, property_info->h, (void **) pguard) == SUCCESS) {
+               return SUCCESS;
+       }
+       stub.in_get = 0;
+       stub.in_set = 0;
+       stub.in_unset = 0;
+       stub.in_isset = 0;
+       return zend_u_hash_quick_add(zobj->guards, UG(unicode)?IS_UNICODE:IS_STRING, property_info->name, property_info->name_length+1, property_info->h, (void**)&stub, sizeof(stub), (void**) pguard);
+}
+
 zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC)
 {
        zend_object *zobj;
@@ -284,11 +308,9 @@ zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC)
        zval *rv = NULL;
        zend_property_info *property_info;
        int silent;
-       zend_bool use_get;
 
        silent = (type == BP_VAR_IS);
        zobj = Z_OBJ_P(object);
-       use_get = (zobj->ce->__get && !zobj->in_get);
 
        if (member->type != IS_UNICODE && (UG(unicode) || member->type != IS_STRING)) {
                ALLOC_ZVAL(tmp_member);
@@ -304,14 +326,18 @@ zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC)
 #endif                 
 
        /* make zend_get_property_info silent if we have getter - we may want to use it */
-       property_info = zend_get_property_info(zobj->ce, member, use_get TSRMLS_CC);
+       property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC);
 
        if (!property_info || zend_u_hash_quick_find(zobj->properties, Z_TYPE_P(member), property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) {
-               if (use_get) {
+               zend_guard *guard;
+
+               if (zobj->ce->__get &&
+                   zend_get_property_guard(zobj, property_info, member, &guard TSRMLS_CC) == SUCCESS &&
+                   !guard->in_get) {
                        /* have getter - try with it! */
-                       zobj->in_get = 1; /* prevent circular getting */
+                       guard->in_get = 1; /* prevent circular getting */
                        rv = zend_std_call_getter(object, member TSRMLS_CC);
-                       zobj->in_get = 0;
+                       guard->in_get = 0;
 
                        if (rv) {
                                retval = &rv;
@@ -341,10 +367,8 @@ static void zend_std_write_property(zval *object, zval *member, zval *value TSRM
        zval **variable_ptr;
        int setter_done = 0;
        zend_property_info *property_info;
-       zend_bool use_set;
 
        zobj = Z_OBJ_P(object);
-       use_set = (zobj->ce->__set && !zobj->in_set);
 
        if (member->type != IS_UNICODE && (UG(unicode) || member->type != IS_STRING)) {
                ALLOC_ZVAL(tmp_member);
@@ -355,7 +379,7 @@ static void zend_std_write_property(zval *object, zval *member, zval *value TSRM
                member = tmp_member;
        }
 
-       property_info = zend_get_property_info(zobj->ce, member, use_set TSRMLS_CC);
+       property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__set != NULL) TSRMLS_CC);
 
        if (property_info && zend_u_hash_quick_find(zobj->properties, Z_TYPE_P(member), property_info->name, property_info->name_length+1, property_info->h, (void **) &variable_ptr) == SUCCESS) {
                if (*variable_ptr == value) {
@@ -376,13 +400,17 @@ static void zend_std_write_property(zval *object, zval *member, zval *value TSRM
                        }
                }
        } else {
-               if (use_set) {
-                       zobj->in_set = 1; /* prevent circular setting */
+               zend_guard *guard;
+
+               if (zobj->ce->__set &&
+                   zend_get_property_guard(zobj, property_info, member, &guard TSRMLS_CC) == SUCCESS &&
+                   !guard->in_set) {
+                       guard->in_set = 1; /* prevent circular setting */
                        if (zend_std_call_setter(object, member, value TSRMLS_CC) != SUCCESS) {
                                /* for now, just ignore it - __set should take care of warnings, etc. */
                        }
                        setter_done = 1;
-                       zobj->in_set = 0;
+                       guard->in_set = 0;
                }
        }
 
@@ -490,10 +518,8 @@ static zval **zend_std_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC
        zval tmp_member;
        zval **retval;
        zend_property_info *property_info;
-       zend_bool use_get;
        
        zobj = Z_OBJ_P(object);
-       use_get = (zobj->ce->__get && !zobj->in_get);
 
        if (member->type != IS_UNICODE && (UG(unicode) || member->type != IS_STRING)) {
                tmp_member = *member;
@@ -506,12 +532,15 @@ static zval **zend_std_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC
        fprintf(stderr, "Ptr object #%d property: %R\n", Z_OBJ_HANDLE_P(object), Z_TYPE_P(member), Z_STRVAL_P(member));
 #endif                 
 
-       property_info = zend_get_property_info(zobj->ce, member, use_get TSRMLS_CC);
+       property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC);
 
        if (!property_info || zend_u_hash_quick_find(zobj->properties, Z_TYPE_P(member), property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) {
                zval *new_zval;
+               zend_guard *guard;
 
-               if (!use_get) {
+               if (!zobj->ce->__get ||
+                   zend_get_property_guard(zobj, property_info, member, &guard TSRMLS_CC) != SUCCESS ||
+                   guard->in_get) {
                        /* we don't have access controls - will just add it */
                        new_zval = &EG(uninitialized_zval);
 
@@ -535,10 +564,8 @@ static void zend_std_unset_property(zval *object, zval *member TSRMLS_DC)
        zend_object *zobj;
        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_UNICODE && (UG(unicode) || member->type != IS_STRING)) {
                ALLOC_ZVAL(tmp_member);
@@ -549,14 +576,18 @@ static void zend_std_unset_property(zval *object, zval *member TSRMLS_DC)
                member = tmp_member;
        }
 
-       property_info = zend_get_property_info(zobj->ce, member, 0 TSRMLS_CC);
+       property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__unset != NULL) TSRMLS_CC);
        
        if (!property_info || zend_u_hash_del(zobj->properties, Z_TYPE_P(member), property_info->name, property_info->name_length+1)) {
-               if (use_unset) {
+               zend_guard *guard;
+
+               if (zobj->ce->__unset &&
+                   zend_get_property_guard(zobj, property_info, member, &guard TSRMLS_CC) == SUCCESS &&
+                   !guard->in_unset) {
                        /* have unseter - try with it! */
-                       zobj->in_unset = 1; /* prevent circular unsetting */
+                       guard->in_unset = 1; /* prevent circular unsetting */
                        zend_std_call_unsetter(object, member TSRMLS_CC);
-                       zobj->in_unset = 0;
+                       guard->in_unset = 0;
                }
        }
 
@@ -913,10 +944,8 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists
        zval **value;
        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_UNICODE && (UG(unicode) || member->type != IS_STRING)) {
                ALLOC_ZVAL(tmp_member);
@@ -934,19 +963,25 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists
        property_info = zend_get_property_info(zobj->ce, member, 1 TSRMLS_CC);
 
        if (!property_info || zend_u_hash_quick_find(zobj->properties, Z_TYPE_P(member), property_info->name, property_info->name_length+1, property_info->h, (void **) &value) == FAILURE) {
+               zend_guard *guard;
+
                result = 0;
-               if (use_isset && (has_set_exists != 2)) {
+               if ((has_set_exists != 2) &&
+                   zobj->ce->__isset &&
+                   zend_get_property_guard(zobj, property_info, member, &guard TSRMLS_CC) == SUCCESS &&
+                   !guard->in_isset) {
                        zval *rv;
 
                        /* have issetter - try with it! */
-                       zobj->in_isset = 1; /* prevent circular getting */
+                       guard->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) {
+                               if (has_set_exists && result && !EG(exception) && zobj->ce->__get && !guard->in_get) {
+                                       guard->in_get = 1;
                                        rv = zend_std_call_getter(object, member TSRMLS_CC);
+                                       guard->in_get = 0;
                                        if (rv) {
                                                rv->refcount++;
                                                result = i_zend_is_true(rv);
@@ -954,6 +989,7 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists
                                        }
                                }
                        }
+                       guard->in_isset = 0;
                }
        } else {
                switch (has_set_exists) {
index c9299c4efb871214b6cc43a2a0b7223d500a3568..a699442269df883f18467166158ffe955150c90c 100644 (file)
@@ -88,6 +88,10 @@ ZEND_API void zend_objects_destroy_object(zend_object *object, zend_object_handl
 
 ZEND_API void zend_objects_free_object_storage(zend_object *object TSRMLS_DC)
 {
+       if (object->guards) {
+               zend_hash_destroy(object->guards);
+               FREE_HASHTABLE(object->guards);         
+       }
        zend_hash_destroy(object->properties);
        FREE_HASHTABLE(object->properties);
        efree(object);
@@ -101,10 +105,7 @@ ZEND_API zend_object_value zend_objects_new(zend_object **object, zend_class_ent
        (*object)->ce = class_type;
        retval.handle = zend_objects_store_put(*object, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC);
        retval.handlers = &std_object_handlers;
-       (*object)->in_get = 0;
-       (*object)->in_set = 0;
-       (*object)->in_unset = 0;
-       (*object)->in_isset = 0;
+       (*object)->guards = NULL;
        return retval;
 }
 
index 588e44669cd89eb4ee8ef1e3360db090366c6493..1a65664a00faf50185739dff0bf72da4ba7e6bbc 100644 (file)
@@ -204,10 +204,7 @@ static void reflection_objects_clone(void *object, void **object_clone TSRMLS_DC
 
        *intern_clone = emalloc(sizeof(reflection_object));
        (*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;
+       (*intern_clone)->zo.guards = NULL;
        ALLOC_HASHTABLE((*intern_clone)->zo.properties);
        (*intern_clone)->ptr = intern->ptr;
        (*intern_clone)->free_ptr = intern->free_ptr;
@@ -225,10 +222,7 @@ static zend_object_value reflection_objects_new(zend_class_entry *class_type TSR
 
        intern = emalloc(sizeof(reflection_object));
        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->zo.guards = NULL;
        intern->ptr = NULL;
        intern->obj = NULL;
        intern->free_ptr = 0;
index af3ce66665b527ecf00136b16cfa703d6d5fcdd4..d65ad221e1b08bdc0283aed368f31014db2a71de 100644 (file)
@@ -941,8 +941,7 @@ static dom_object* dom_objects_set_class(zend_class_entry *class_type, zend_bool
 
        intern = emalloc(sizeof(dom_object));
        intern->std.ce = class_type;
-       intern->std.in_get = 0;
-       intern->std.in_set = 0;
+       intern->std.guards = NULL;
        intern->ptr = NULL;
        intern->prop_handler = NULL;
        intern->document = NULL;
index 040a1bd4c1121007dd7c4579ef835b68ef9a1784..a05dd7529887b5f7bea84807e98e4f4d15d28a69 100644 (file)
@@ -346,8 +346,7 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry *class_
        intern = emalloc(sizeof(mysqli_object));
        memset(intern, 0, sizeof(mysqli_object));
        intern->zo.ce = class_type;
-       intern->zo.in_get = 0;
-       intern->zo.in_set = 0;
+       intern->zo.guards = NULL;
        intern->ptr = NULL;
        intern->valid = 0;
        intern->prop_handler = NULL;
index 588e44669cd89eb4ee8ef1e3360db090366c6493..1a65664a00faf50185739dff0bf72da4ba7e6bbc 100644 (file)
@@ -204,10 +204,7 @@ static void reflection_objects_clone(void *object, void **object_clone TSRMLS_DC
 
        *intern_clone = emalloc(sizeof(reflection_object));
        (*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;
+       (*intern_clone)->zo.guards = NULL;
        ALLOC_HASHTABLE((*intern_clone)->zo.properties);
        (*intern_clone)->ptr = intern->ptr;
        (*intern_clone)->free_ptr = intern->free_ptr;
@@ -225,10 +222,7 @@ static zend_object_value reflection_objects_new(zend_class_entry *class_type TSR
 
        intern = emalloc(sizeof(reflection_object));
        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->zo.guards = NULL;
        intern->ptr = NULL;
        intern->obj = NULL;
        intern->free_ptr = 0;
index 18b9a6249efccfc015fa7305b7c823fb46c2ca57..ab0b7da7a0803fcea275da6ce619140e7244a573 100644 (file)
@@ -353,8 +353,7 @@ zend_object_value xmlreader_objects_new(zend_class_entry *class_type TSRMLS_DC)
 
        intern = emalloc(sizeof(xmlreader_object));
        intern->std.ce = class_type;
-       intern->std.in_get = 0;
-       intern->std.in_set = 0;
+       intern->std.guards = NULL;
        intern->ptr = NULL;
        intern->schema = NULL;
        intern->prop_handler = &xmlreader_prop_handlers;
index ed29cbb382bf24f8d34558b5262e3d50e3c10ad2..7959e037261604d55163b13c2e83587ff578e91f 100644 (file)
@@ -120,8 +120,7 @@ zend_object_value xsl_objects_new(zend_class_entry *class_type TSRMLS_DC)
 
        intern = emalloc(sizeof(xsl_object));
        intern->std.ce = class_type;
-       intern->std.in_get = 0;
-       intern->std.in_set = 0;
+       intern->std.guards = NULL;
        intern->ptr = NULL;
        intern->prop_handler = NULL;
        intern->parameter = NULL;