Permalink
Browse files

Fixed bug #33512 (Add missing support for isset()/unset() overloading…

… to complement the property get/set methods)
  • Loading branch information...
1 parent 307f622 commit 345e0255b5af2d81215c33e107f12df2b3724a92 Dmitry Stogov committed Jul 7, 2005
Showing with 158 additions and 26 deletions.
  1. +2 −0 NEWS
  2. +4 −0 Zend/zend.h
  3. +23 −1 Zend/zend_API.c
  4. +6 −1 Zend/zend_API.h
  5. +12 −0 Zend/zend_compile.c
  6. +2 −0 Zend/zend_compile.h
  7. +99 −24 Zend/zend_object_handlers.c
  8. +2 −0 Zend/zend_objects.c
  9. +4 −0 Zend/zend_reflection_api.c
  10. +4 −0 ext/reflection/php_reflection.c
View
2 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)
View
@@ -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;
View
@@ -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;
View
@@ -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()
View
@@ -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;
View
@@ -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"
View
@@ -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;
}
View
@@ -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;
}
@@ -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;
@@ -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;

0 comments on commit 345e025

Please sign in to comment.