summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@php.net>2005-11-15 13:35:23 +0000
committerDmitry Stogov <dmitry@php.net>2005-11-15 13:35:23 +0000
commit64931b62cc6bed6c7397dcca826df3395258d07f (patch)
treec6add8599ba034b08a6481f28ccb9e4d0af69981
parenta8c6b992b8894763c59276c1142971aa9a314500 (diff)
downloadphp-git-64931b62cc6bed6c7397dcca826df3395258d07f.tar.gz
Allow recursive calls to __get/__set for different properties
-rw-r--r--NEWS1
-rw-r--r--Zend/zend.h13
-rw-r--r--Zend/zend_object_handlers.c94
-rw-r--r--Zend/zend_objects.c9
-rw-r--r--Zend/zend_reflection_api.c10
-rw-r--r--ext/reflection/php_reflection.c10
6 files changed, 84 insertions, 53 deletions
diff --git a/NEWS b/NEWS
index d2d23156b5..565b22fcf6 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? Nov 2005, PHP 5.1
+- Allow recursive calls to __get/__set for different properties. (Dmitry)
- Upgraded PEAR to version 1.4.4. (Greg)
- Fixed bug in mysqli extension with unsigned int(11) being represented as
signed integer in PHP instead of string in 32bit systems. (Andrey)
diff --git a/Zend/zend.h b/Zend/zend.h
index c242be7da1..f39f8456cb 100644
--- a/Zend/zend.h
+++ b/Zend/zend.h
@@ -265,13 +265,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;
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 49b5e1d6cf..d1d413b211 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -237,7 +237,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_STRVAL_P(member);
EG(std_property_info).name_length = Z_STRLEN_P(member);
- EG(std_property_info).h = zend_get_hash_value(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;
@@ -268,6 +268,30 @@ ZEND_API int zend_check_property_access(zend_object *zobj, char *prop_info_name
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)
+{
+ zend_property_info info;
+ zend_guard stub;
+
+ if (!property_info) {
+ property_info = &info;
+ info.name = Z_STRVAL_P(member);
+ info.name_length = Z_STRLEN_P(member);
+ info.h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
+ }
+ if (!zobj->guards) {
+ ALLOC_HASHTABLE(zobj->guards);
+ zend_hash_init(zobj->guards, 0, NULL, NULL, 0);
+ } else if (zend_hash_quick_find(zobj->guards, 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_hash_quick_add(zobj->guards, 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;
@@ -276,11 +300,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_STRING) {
ALLOC_ZVAL(tmp_member);
@@ -296,14 +318,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_hash_quick_find(zobj->properties, 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) == 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;
@@ -333,10 +359,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_STRING) {
ALLOC_ZVAL(tmp_member);
@@ -347,7 +371,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_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &variable_ptr) == SUCCESS) {
if (*variable_ptr == value) {
@@ -368,13 +392,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) == 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;
}
}
@@ -482,10 +510,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_STRING) {
tmp_member = *member;
@@ -498,12 +524,15 @@ static zval **zend_std_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC
fprintf(stderr, "Ptr object #%d property: %s\n", Z_OBJ_HANDLE_P(object), 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_hash_quick_find(zobj->properties, 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) != SUCCESS ||
+ guard->in_get) {
/* we don't have access controls - will just add it */
new_zval = &EG(uninitialized_zval);
@@ -527,10 +556,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_STRING) {
ALLOC_ZVAL(tmp_member);
@@ -541,14 +568,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_hash_del(zobj->properties, property_info->name, property_info->name_length+1) == FAILURE) {
- if (use_unset) {
+ zend_guard *guard;
+
+ if (zobj->ce->__unset &&
+ zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
+ !guard->in_unset) {
/* have unseter - try with it! */
- zobj->in_unset = 1; /* prevent circular unsetting */
+ guard->in_unset = 1; /* prevent circular setting */
zend_std_call_unsetter(object, member TSRMLS_CC);
- zobj->in_unset = 0;
+ guard->in_unset = 0;
}
}
@@ -895,10 +926,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_STRING) {
ALLOC_ZVAL(tmp_member);
@@ -916,19 +945,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_hash_quick_find(zobj->properties, 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) == 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);
@@ -936,6 +971,7 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists
}
}
}
+ guard->in_isset = 0;
}
} else {
switch (has_set_exists) {
diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c
index 9b5b854220..3b98ef8698 100644
--- a/Zend/zend_objects.c
+++ b/Zend/zend_objects.c
@@ -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;
}
diff --git a/Zend/zend_reflection_api.c b/Zend/zend_reflection_api.c
index cd56a3213d..58a4465e50 100644
--- a/Zend/zend_reflection_api.c
+++ b/Zend/zend_reflection_api.c
@@ -203,10 +203,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;
@@ -224,10 +221,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;
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index cd56a3213d..58a4465e50 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -203,10 +203,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;
@@ -224,10 +221,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;