diff options
| -rw-r--r-- | Zend/zend.h | 7 | ||||
| -rw-r--r-- | Zend/zend_compile.c | 16 | ||||
| -rw-r--r-- | Zend/zend_compile.h | 7 | ||||
| -rw-r--r-- | Zend/zend_execute.c | 55 | ||||
| -rw-r--r-- | Zend/zend_extensions.h | 2 | ||||
| -rw-r--r-- | Zend/zend_object_handlers.c | 289 | ||||
| -rw-r--r-- | Zend/zend_objects.c | 2 |
7 files changed, 335 insertions, 43 deletions
diff --git a/Zend/zend.h b/Zend/zend.h index d9de24d4f0..2b28eedc4f 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -226,6 +226,8 @@ typedef struct _zend_class_entry zend_class_entry; typedef struct _zend_object { zend_class_entry *ce; HashTable *properties; + int in_get:1; + int in_set:1; } zend_object; typedef unsigned int zend_object_handle; @@ -311,9 +313,14 @@ struct _zend_class_entry { union _zend_function *constructor; union _zend_function *destructor; union _zend_function *clone; + union _zend_function *__get; + union _zend_function *__set; + union _zend_function *__call; /* handlers */ zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC); + + /* old handlers */ void (*handle_function_call)(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference); zval (*handle_property_get)(zend_property_reference *property_reference); int (*handle_property_set)(zend_property_reference *property_reference, zval *value); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index bb7403b0c2..4cf790a87c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -895,10 +895,6 @@ void zend_do_free(znode *op1 TSRMLS_DC) } } -#define ZEND_CLONE_FUNC_NAME "__clone" -#define ZEND_CONSTRUCTOR_FUNC_NAME "__construct" -#define ZEND_DESTRUCTOR_FUNC_NAME "__destruct" - void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference TSRMLS_DC) { zend_op_array op_array; @@ -938,6 +934,12 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n CG(active_class_entry)->destructor = (zend_function *) CG(active_op_array); } else if ((function_name->u.constant.value.str.len == sizeof(ZEND_CLONE_FUNC_NAME)-1) && (!memcmp(function_name->u.constant.value.str.val, ZEND_CLONE_FUNC_NAME, sizeof(ZEND_CLONE_FUNC_NAME)))) { CG(active_class_entry)->clone = (zend_function *) CG(active_op_array); + } else if ((function_name->u.constant.value.str.len == sizeof(ZEND_CALL_FUNC_NAME)-1) && (!memcmp(function_name->u.constant.value.str.val, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)))) { + CG(active_class_entry)->__call = (zend_function *) CG(active_op_array); + } else if ((function_name->u.constant.value.str.len == sizeof(ZEND_GET_FUNC_NAME)-1) && (!memcmp(function_name->u.constant.value.str.val, ZEND_GET_FUNC_NAME, sizeof(ZEND_GET_FUNC_NAME)))) { + CG(active_class_entry)->__get = (zend_function *) CG(active_op_array); + } else if ((function_name->u.constant.value.str.len == sizeof(ZEND_SET_FUNC_NAME)-1) && (!memcmp(function_name->u.constant.value.str.val, ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME)))) { + CG(active_class_entry)->__set = (zend_function *) CG(active_op_array); } } else { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); @@ -1551,6 +1553,9 @@ static void create_class(HashTable *class_table, char *name, int name_length, ze new_class_entry->constructor = NULL; new_class_entry->destructor = NULL; new_class_entry->clone = NULL; + new_class_entry->__get = NULL; + new_class_entry->__set = NULL; + new_class_entry->__call = NULL; new_class_entry->create_object = NULL; new_class_entry->handle_function_call = NULL; @@ -2050,6 +2055,9 @@ void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znod new_class_entry->constructor = NULL; new_class_entry->destructor = NULL; new_class_entry->clone = NULL; + new_class_entry->__get = NULL; + new_class_entry->__set = NULL; + new_class_entry->__call = NULL; new_class_entry->create_object = NULL; new_class_entry->handle_function_call = NULL; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 69f98864d7..94422bf766 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -710,4 +710,11 @@ int zendlex(znode *zendlval TSRMLS_DC); END_EXTERN_C() +#define ZEND_CLONE_FUNC_NAME "__clone" +#define ZEND_CONSTRUCTOR_FUNC_NAME "__construct" +#define ZEND_DESTRUCTOR_FUNC_NAME "__destruct" +#define ZEND_GET_FUNC_NAME "__get" +#define ZEND_SET_FUNC_NAME "__set" +#define ZEND_CALL_FUNC_NAME "__call" + #endif /* ZEND_COMPILE_H */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index d14284b242..fb10e0eda0 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -140,7 +140,7 @@ static inline zval **_get_zval_ptr_ptr(znode *node, temp_variable *Ts TSRMLS_DC) static inline zval **zend_fetch_property_address_inner(zval *object, znode *op2, temp_variable *Ts, int type TSRMLS_DC) { zval *prop_ptr = get_zval_ptr(op2, Ts, &EG(free_op2), BP_VAR_R); - zval **retval; + zval **retval = NULL; zval tmp; @@ -161,7 +161,9 @@ static inline zval **zend_fetch_property_address_inner(zval *object, znode *op2, if(Z_OBJ_HT_P(object)->get_property_ptr != NULL) { retval = Z_OBJ_HT_P(object)->get_property_ptr(object, prop_ptr TSRMLS_CC); - } else { + } + + if(retval == NULL) { zend_error(E_WARNING, "This object doesn't support property references"); retval = &EG(error_zval_ptr); } @@ -321,6 +323,7 @@ static inline void zend_assign_to_object_op(znode *result, znode *op1, znode *op zval *property = get_zval_ptr(op2, Ts, &EG(free_op2), BP_VAR_R); zval tmp; zval **retval = &Ts[result->u.var].var.ptr; + int have_get_ptr = 0; Ts[result->u.var].var.ptr_ptr = NULL; make_real_object(object_ptr TSRMLS_CC); @@ -356,12 +359,17 @@ static inline void zend_assign_to_object_op(znode *result, znode *op1, znode *op if(Z_OBJ_HT_P(object)->get_property_zval_ptr) { zval **zptr = Z_OBJ_HT_P(object)->get_property_zval_ptr(object, property TSRMLS_CC); - SEPARATE_ZVAL_IF_NOT_REF(zptr); + if(zptr != NULL) { /* NULL means no success in getting PTR */ + SEPARATE_ZVAL_IF_NOT_REF(zptr); - binary_op(*zptr, *zptr, value TSRMLS_CC); - *retval = *zptr; - SELECTIVE_PZVAL_LOCK(*retval, result); - } else { + have_get_ptr = 1; + binary_op(*zptr, *zptr, value TSRMLS_CC); + *retval = *zptr; + SELECTIVE_PZVAL_LOCK(*retval, result); + } + } + + if(!have_get_ptr) { zval *z = Z_OBJ_HT_P(object)->read_property(object, property, BP_VAR_RW TSRMLS_CC); SEPARATE_ZVAL_IF_NOT_REF(&z); binary_op(z, z, value TSRMLS_CC); @@ -989,6 +997,7 @@ static void zend_pre_incdec_property(znode *result, znode *op1, znode *op2, temp zval *object; zval *property = get_zval_ptr(op2, Ts, &EG(free_op2), BP_VAR_R); zval **retval = &Ts[result->u.var].var.ptr; + int have_get_ptr = 0; make_real_object(object_ptr TSRMLS_CC); object = *object_ptr; @@ -1006,12 +1015,17 @@ static void zend_pre_incdec_property(znode *result, znode *op1, znode *op2, temp if(Z_OBJ_HT_P(object)->get_property_zval_ptr) { zval **zptr = Z_OBJ_HT_P(object)->get_property_zval_ptr(object, property TSRMLS_CC); - SEPARATE_ZVAL_IF_NOT_REF(zptr); + if(zptr != NULL) { /* NULL means no success in getting PTR */ + SEPARATE_ZVAL_IF_NOT_REF(zptr); - incdec_op(*zptr); - *retval = *zptr; - SELECTIVE_PZVAL_LOCK(*retval, result); - } else { + have_get_ptr = 1; + incdec_op(*zptr); + *retval = *zptr; + SELECTIVE_PZVAL_LOCK(*retval, result); + } + } + + if(!have_get_ptr) { zval *z = Z_OBJ_HT_P(object)->read_property(object, property, BP_VAR_RW TSRMLS_CC); SEPARATE_ZVAL_IF_NOT_REF(&z); incdec_op(z); @@ -1030,6 +1044,7 @@ static void zend_post_incdec_property(znode *result, znode *op1, znode *op2, tem zval *object; zval *property = get_zval_ptr(op2, Ts, &EG(free_op2), BP_VAR_R); zval *retval = &Ts[result->u.var].tmp_var; + int have_get_ptr = 0; make_real_object(object_ptr TSRMLS_CC); object = *object_ptr; @@ -1045,13 +1060,19 @@ static void zend_post_incdec_property(znode *result, znode *op1, znode *op2, tem if(Z_OBJ_HT_P(object)->get_property_zval_ptr) { zval **zptr = Z_OBJ_HT_P(object)->get_property_zval_ptr(object, property TSRMLS_CC); - SEPARATE_ZVAL_IF_NOT_REF(zptr); + if(zptr != NULL) { /* NULL means no success in getting PTR */ + have_get_ptr = 1; + SEPARATE_ZVAL_IF_NOT_REF(zptr); + + *retval = **zptr; + zendi_zval_copy_ctor(*retval); + + incdec_op(*zptr); - *retval = **zptr; - zendi_zval_copy_ctor(*retval); + } + } - incdec_op(*zptr); - } else { + if(!have_get_ptr) { zval *z = Z_OBJ_HT_P(object)->read_property(object, property, BP_VAR_RW TSRMLS_CC); SEPARATE_ZVAL_IF_NOT_REF(&z); *retval = *z; diff --git a/Zend/zend_extensions.h b/Zend/zend_extensions.h index 3f7e0ebfd3..0e540b72e6 100644 --- a/Zend/zend_extensions.h +++ b/Zend/zend_extensions.h @@ -23,7 +23,7 @@ #include "zend_compile.h" -#define ZEND_EXTENSION_API_NO 20010710 +#define ZEND_EXTENSION_API_NO 220020901 typedef struct _zend_extension_version_info { int zend_extension_api_no; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 9c131e2626..1af47b2a27 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -8,6 +8,23 @@ #define DEBUG_OBJECT_HANDLERS 0 +/* + __X accessors explanation: + + if we have __get and property that is not part of the properties array is + requested, we call __get handler. If it fails, we return uninitialized. + + if we have __set and property that is not part of the properties array is + set, we call __set handler. If it fails, we do not change the array. + + for both handlers above, when we are inside __get/__set, no further calls for + __get/__set for these objects will be made, to prevent endless recursion and + enable accessors to change properties array. + + if we have __call and method which is not part of the class function table is + called, we cal __call handler. +*/ + static HashTable *zend_std_get_properties(zval *object TSRMLS_DC) { zend_object *zobj; @@ -15,11 +32,109 @@ static HashTable *zend_std_get_properties(zval *object TSRMLS_DC) return zobj->properties; } +static zval *zend_std_call_getter(zval *object, zval *member TSRMLS_DC) +{ + zval **call_args[1]; + zval *retval = NULL; + zval __get_name; + int call_result; + + /* __get handler is called with two arguments: + property name + return value for the object (by reference) + + it should return whether the call was successfull or not + */ + INIT_PZVAL(&__get_name); + ZVAL_STRINGL(&__get_name, ZEND_GET_FUNC_NAME, sizeof(ZEND_GET_FUNC_NAME)-1, 0); + + call_args[0] = &member; + + /* go call the __get handler */ + call_result = call_user_function_ex(NULL, + &object, + &__get_name, + &retval, + 1, call_args, + 0, NULL TSRMLS_CC); + + /* + call_result is if call_user_function gone OK. + retval returns the value that is received + */ + + + if(call_result == FAILURE) { + zend_error(E_ERROR, "Could not call __get handler for class %s", Z_OBJCE_P(object)->name); + return NULL; + } + + retval->refcount--; + + return retval; +} + +static int zend_std_call_setter(zval *object, zval *member, zval *value TSRMLS_DC) +{ + zval **call_args[2]; + zval *retval = NULL; + zval __set_name; + int call_result; + int ret; + + /* __set handler is called with two arguments: + property name + value to be set + + it should return whether the call was successfull or not + */ + INIT_PZVAL(&__set_name); + ZVAL_STRINGL(&__set_name, ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME)-1, 0); + + call_args[0] = &member; + value->refcount++; + call_args[1] = &value; + + /* go call the __set handler */ + call_result = call_user_function_ex(NULL, + &object, + &__set_name, + &retval, + 2, call_args, + 0, NULL TSRMLS_CC); + + /* + call_result is if call_user_function gone OK. + retval shows if __get method went OK. + */ + + + if(call_result == FAILURE) { + zend_error(E_ERROR, "Could not call __set handler for class %s", Z_OBJCE_P(object)->name); + return FAILURE; + } + + zval_ptr_dtor(&value); + + if (retval && zval_is_true(retval)) { + ret = SUCCESS; + } else { + ret = FAILURE; + } + + if(retval) { + zval_ptr_dtor(&retval); + } + + return ret; +} + zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC) { zend_object *zobj; zval tmp_member; zval **retval; + zval *rv = NULL; zobj = Z_OBJ_P(object); @@ -35,25 +150,38 @@ zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC) #endif if (zend_hash_find(zobj->properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void **) &retval) == FAILURE) { - switch (type) { - case BP_VAR_R: - zend_error(E_NOTICE,"Undefined property: %s", Z_STRVAL_P(member)); - /* break missing intentionally */ - case BP_VAR_IS: - retval = &EG(uninitialized_zval_ptr); - break; - case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined property: %s", Z_STRVAL_P(member)); - /* break missing intentionally */ - case BP_VAR_W: { + if(zobj->ce->__get && !zobj->in_get) { + /* have getter - try with it! */ + zobj->in_get = 1; /* prevent circular getting */ + rv = zend_std_call_getter(object, member TSRMLS_CC); + zobj->in_get = 0; + + if(rv) { + retval = &rv; + } else { + retval = &EG(uninitialized_zval_ptr); + } + } else { + switch (type) { + case BP_VAR_R: + zend_error(E_NOTICE,"Undefined property: %s", Z_STRVAL_P(member)); + /* break missing intentionally */ + case BP_VAR_IS: + retval = &EG(uninitialized_zval_ptr); + break; + case BP_VAR_RW: + zend_error(E_NOTICE,"Undefined property: %s", Z_STRVAL_P(member)); + /* break missing intentionally */ + case BP_VAR_W: { zval *new_zval = &EG(uninitialized_zval); - + new_zval->refcount++; zend_hash_update(zobj->properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, &new_zval, sizeof(zval *), (void **) &retval); - } + } break; EMPTY_SWITCH_DEFAULT_CASE() - + + } } } if (member == &tmp_member) { @@ -67,6 +195,7 @@ static void zend_std_write_property(zval *object, zval *member, zval *value TSRM zend_object *zobj; zval tmp_member; zval **variable_ptr; + int setter_done = 0; zobj = Z_OBJ_P(object); @@ -88,10 +217,21 @@ static void zend_std_write_property(zval *object, zval *member, zval *value TSRM FREE_ZVAL(*variable_ptr); } } + } else { + if(zobj->ce->__set && !zobj->in_set) { + zobj->in_set = 1; /* prevent circular setting */ + if(zend_std_call_setter(object, member, value) != SUCCESS) { +// zend_error(E_NOTICE,"Cannot set undefined property: %s", Z_STRVAL_P(member)); + } + setter_done = 1; + zobj->in_set = 0; + } } - value->refcount++; - zend_hash_update(zobj->properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, &value, sizeof(zval *), NULL); + if(!setter_done) { + value->refcount++; + zend_hash_update(zobj->properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, &value, sizeof(zval *), NULL); + } if (member == &tmp_member) { zval_dtor(member); } @@ -117,11 +257,19 @@ static zval **zend_std_get_property_ptr(zval *object, zval *member TSRMLS_DC) #endif if (zend_hash_find(zobj->properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void **) &retval) == FAILURE) { - zval *new_zval = &EG(uninitialized_zval); + zval *new_zval; - zend_error(E_NOTICE, "Undefined property: %s", Z_STRVAL_P(member)); - new_zval->refcount++; - zend_hash_update(zobj->properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, &new_zval, sizeof(zval *), (void **) &retval); + if(!zobj->ce->__get) { + /* we don't have getter - will just add it */ + new_zval = &EG(uninitialized_zval); + + zend_error(E_NOTICE, "Undefined property: %s", Z_STRVAL_P(member)); + new_zval->refcount++; + zend_hash_update(zobj->properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, &new_zval, sizeof(zval *), (void **) &retval); + } else { + /* we do have getter - fail and let it try again with usual get/set */ + retval = NULL; + } } if (member == &tmp_member) { zval_dtor(member); @@ -148,12 +296,100 @@ static void zend_std_unset_property(zval *object, zval *member TSRMLS_DC) } } +static void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS) +{ + zval ***args; + zend_internal_function *func = (zend_internal_function *)EG(function_state_ptr)->function; + zval method_name, method_args, method_result, __call_name; + zval *method_name_ptr, *method_args_ptr, *method_result_ptr; + zval **call_args[3]; + zval *retval = NULL; + int i, call_result; + + args = (zval ***)emalloc(ZEND_NUM_ARGS() * sizeof(zval **)); + + if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) { + efree(args); + zend_error(E_ERROR, "Cannot get arguments for __call"); + RETURN_FALSE; + } + + method_name_ptr = &method_name; + INIT_PZVAL(method_name_ptr); + ZVAL_STRING(method_name_ptr, func->function_name, 0); /* no dup - it's a copy */ + + method_args_ptr = &method_args; + INIT_PZVAL(method_args_ptr); + array_init(method_args_ptr); + + for(i=0; i<ZEND_NUM_ARGS(); i++) { + zval_add_ref(args[i]); + add_next_index_zval(method_args_ptr, *args[i]); + } + + efree(args); + + method_result_ptr = &method_result; + method_result.is_ref = 1; + method_result.refcount = 1; + ZVAL_NULL(method_result_ptr); + + INIT_PZVAL(&__call_name); + ZVAL_STRINGL(&__call_name, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1, 0); + + /* __call handler is called with three arguments: + method name + array of method parameters + return value for the method (by reference) + + it should return whether the call was successfull or not + */ + call_args[0] = &method_name_ptr; + call_args[1] = &method_args_ptr; + call_args[2] = &method_result_ptr; + + /* go call the __call handler */ + call_result = call_user_function_ex(NULL, + &this_ptr, + &__call_name, + &retval, + 3, call_args, + 0, NULL TSRMLS_CC); + + /* A lot of return values here. Let's sort them out: + call_result is if call_user_function gone OK. + retval shows if __call method went OK. + method_result is the true result of the called method + */ + + + if(call_result == FAILURE) { + zend_error(E_ERROR, "Could not call __call handler for class %s", Z_OBJCE_P(this_ptr)->name); + } + + if (retval && zval_is_true(retval)) { + *return_value = method_result; + INIT_PZVAL(return_value); + } else { + zval_dtor(method_result_ptr); + zend_error(E_ERROR, "Call to undefined method %s::%s()", Z_OBJCE_P(this_ptr)->name, Z_STRVAL(method_name)); + } + zval_ptr_dtor(&retval); + + /* now destruct all auxiliaries */ + zval_dtor(method_args_ptr); + zval_dtor(method_name_ptr); + + /* destruct the function also, then - we have allocated it in get_method */ + efree(func); +} + static union _zend_function *zend_std_get_method(zval *object, char *method_name, int method_len TSRMLS_DC) { zend_object *zobj; zend_function *func_method; char *lc_method_name; - + lc_method_name = do_alloca(method_len+1); /* Create a zend_copy_str_tolower(dest, src, src_length); */ memcpy(lc_method_name, method_name, method_len+1); @@ -161,6 +397,17 @@ static union _zend_function *zend_std_get_method(zval *object, char *method_name zobj = Z_OBJ_P(object); if(zend_hash_find(&zobj->ce->function_table, lc_method_name, method_len+1, (void **)&func_method) == FAILURE) { + if(zobj->ce->__call != NULL) { + zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); + call_user_call->type = ZEND_INTERNAL_FUNCTION; + call_user_call->handler = zend_std_call_user_call; + call_user_call->arg_types = NULL; + call_user_call->scope = NULL; + call_user_call->function_name = estrndup(method_name, method_len); + + free_alloca(lc_method_name); + return (union _zend_function *)call_user_call; + } zend_error(E_ERROR, "Call to undefined function %s()", method_name); } diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 3e253cc406..a6b7579350 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -57,6 +57,8 @@ 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, NULL TSRMLS_CC); retval.handlers = &std_object_handlers; + (*object)->in_get = 0; + (*object)->in_set = 0; return retval; } |
