- 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)
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;
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;
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);
}
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;
__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;
}
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) {
}
__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;
#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; \
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; \
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()
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;
}
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;
}
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;
#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"
}
}
+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)
{
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;
}
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);
}
}
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);
result = 1;
break;
}
- } else {
- result = 0;
}
- if (member == &tmp_member) {
- zval_dtor(member);
+ if (tmp_member) {
+ zval_ptr_dtor(&tmp_member);
}
return result;
}
retval.handlers = &std_object_handlers;
(*object)->in_get = 0;
(*object)->in_set = 0;
+ (*object)->in_unset = 0;
+ (*object)->in_isset = 0;
return retval;
}
(*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;
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;
(*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;
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;