summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei Zmievski <andrei@php.net>2001-12-11 04:41:15 +0000
committerAndrei Zmievski <andrei@php.net>2001-12-11 04:41:15 +0000
commitb197b8d104cdc0d6bc1b57bed60b3d13caf0d62b (patch)
treee3d704cf7c4bd19dd8646b5295d46edbd2b78f79
parent84eaf5e1c89c80d4bd22ec3f4cbc9c3d8b372b67 (diff)
downloadphp-git-b197b8d104cdc0d6bc1b57bed60b3d13caf0d62b.tar.gz
This commit implements property-specific accessors.
When overload() is called, it not only scans for general handlers, but also for any methods beginning with __get_ and __set_ and saves a list of them. Then when a property is accessed, e.g. $obj->x, the extension will first check whether __get_x() is available, and will call it if it is, otherwise it will invoke the general __get() handler.
-rw-r--r--ext/overload/overload.c205
-rw-r--r--ext/overload/php_overload.h1
2 files changed, 153 insertions, 53 deletions
diff --git a/ext/overload/overload.c b/ext/overload/overload.c
index 8895f7a327..8647f1ebf2 100644
--- a/ext/overload/overload.c
+++ b/ext/overload/overload.c
@@ -62,7 +62,7 @@ zend_module_entry overload_module_entry = {
"overload",
overload_functions,
PHP_MINIT(overload),
- NULL,
+ PHP_MSHUTDOWN(overload),
NULL,
NULL,
PHP_MINFO(overload),
@@ -74,17 +74,35 @@ zend_module_entry overload_module_entry = {
ZEND_GET_MODULE(overload)
#endif
-/* {{{ php_overload_init_globals
- */
-static void php_overload_init_globals(zend_overload_globals *overload_globals)
+typedef struct _oo_class_data {
+ 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);
+ HashTable getters;
+ HashTable setters;
+} oo_class_data;
+
+#define DISABLE_HANDLERS(ce) \
+ (ce).handle_property_get = NULL; \
+ (ce).handle_property_set = NULL; \
+ (ce).handle_function_call = NULL;
+
+static void overloaded_class_dtor(oo_class_data *oo_data)
{
- zend_hash_init_ex(&overload_globals->overloaded_classes, 10, NULL, NULL, 1, 0);
+ zend_hash_destroy(&oo_data->getters);
+ zend_hash_destroy(&oo_data->setters);
+}
+
+/* {{{ php_overload_init_globals */
+static void php_overload_init_globals(zend_overload_globals *overload_globals TSRMLS_DC)
+{
+ zend_hash_init_ex(&overload_globals->overloaded_classes, 10, NULL,
+ (dtor_func_t)overloaded_class_dtor, 1, 0);
}
/* }}} */
-/* {{{ php_overload_destroy_globals
- */
-static void php_overload_destroy_globals(zend_overload_globals *overload_globals)
+/* {{{ php_overload_destroy_globals */
+static void php_overload_destroy_globals(zend_overload_globals *overload_globals TSRMLS_DC)
{
zend_hash_destroy(&overload_globals->overloaded_classes);
}
@@ -103,6 +121,17 @@ PHP_MINIT_FUNCTION(overload)
}
/* }}} */
+PHP_MSHUTDOWN_FUNCTION(overload)
+{
+#ifdef ZTS
+ ts_free_id(overload_globals_id);
+#else
+ php_overload_destroy_globals(&overload_globals TSRMLS_CC);
+#endif
+
+ return SUCCESS;
+}
+
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(overload)
@@ -117,17 +146,6 @@ PHP_MINFO_FUNCTION(overload)
}
/* }}} */
-typedef struct _oo_class_data {
- 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);
-} oo_class_data;
-
-#define DISABLE_HANDLERS(ce) \
- (ce).handle_property_get = NULL; \
- (ce).handle_property_set = NULL; \
- (ce).handle_function_call = NULL;
-
/*
* In all three handlers, we save the original CE of the object, and replace it
* with a temporary one that has all handlers turned off. This is to avoid
@@ -136,7 +154,7 @@ typedef struct _oo_class_data {
* class. After invoking the callback we restore the object's CE.
*/
-/* {{{ int call_get_handler() */
+/* {{{ static int call_get_handler() */
static int call_get_handler(zval *object, zval *prop_name, zval **prop_value TSRMLS_DC)
{
int call_result;
@@ -145,7 +163,13 @@ static int call_get_handler(zval *object, zval *prop_name, zval **prop_value TSR
zval get_handler;
zval **args[2];
zval *retval = NULL;
- oo_class_data oo_data;
+ zval **accessor_name;
+ oo_class_data *oo_data;
+
+ if (zend_hash_index_find(&OOG(overloaded_classes), (long)Z_OBJCE_P(object), (void**)&oo_data) == FAILURE) {
+ php_error(E_WARNING, "internal problem trying to get property");
+ return 0;
+ }
temp_ce = *Z_OBJCE_P(object);
DISABLE_HANDLERS(temp_ce);
@@ -156,6 +180,23 @@ static int call_get_handler(zval *object, zval *prop_name, zval **prop_value TSR
result_ptr->refcount = 1;
ZVAL_NULL(result_ptr);
+ if (zend_hash_find(&oo_data->getters, Z_STRVAL_P(prop_name),
+ Z_STRLEN_P(prop_name)+1, (void **)&accessor_name) == SUCCESS) {
+ args[0] = &result_ptr;
+
+ call_result = call_user_function_ex(NULL,
+ &object,
+ *accessor_name,
+ &retval,
+ 1, args,
+ 0, NULL TSRMLS_CC);
+ Z_OBJCE_P(object) = orig_ce;
+
+ if (call_result == FAILURE || !retval) {
+ php_error(E_WARNING, "unable to call %s::" GET_HANDLER "_%s() handler", Z_OBJCE_P(object)->name, Z_STRVAL_P(prop_name));
+ return 0;
+ }
+ } else {
ZVAL_STRINGL(&get_handler, GET_HANDLER, sizeof(GET_HANDLER)-1, 0);
args[0] = &prop_name;
args[1] = &result_ptr;
@@ -169,9 +210,10 @@ static int call_get_handler(zval *object, zval *prop_name, zval **prop_value TSR
Z_OBJCE_P(object) = orig_ce;
if (call_result == FAILURE || !retval) {
- php_error(E_WARNING, "unable to call %s::" GET_HANDLER "() handler", orig_ce->name);
+ php_error(E_WARNING, "unable to call %s::" GET_HANDLER "() handler", Z_OBJCE_P(object)->name);
return 0;
}
+ }
if (zval_is_true(retval)) {
REPLACE_ZVAL_VALUE(prop_value, result_ptr, 0);
@@ -182,12 +224,7 @@ static int call_get_handler(zval *object, zval *prop_name, zval **prop_value TSR
zval_ptr_dtor(&retval);
zval_dtor(result_ptr);
- if (zend_hash_index_find(&OOG(overloaded_classes), (long)Z_OBJCE_P(object), (void*)&oo_data) == FAILURE) {
- php_error(E_WARNING, "internal problem trying to get property");
- return 0;
- }
-
- if (!oo_data.handle_property_get) {
+ if (!oo_data->handle_property_get) {
return 0;
}
@@ -197,7 +234,7 @@ static int call_get_handler(zval *object, zval *prop_name, zval **prop_value TSR
}
/* }}} */
-/* {{{ int call_set_handler() */
+/* {{{ static int call_set_handler() */
int call_set_handler(zval *object, zval *prop_name, zval *value TSRMLS_DC)
{
int call_result;
@@ -206,7 +243,13 @@ int call_set_handler(zval *object, zval *prop_name, zval *value TSRMLS_DC)
zval *value_copy;
zval **args[2];
zval *retval = NULL;
- oo_class_data oo_data;
+ zval **accessor_name;
+ oo_class_data *oo_data;
+
+ if (zend_hash_index_find(&OOG(overloaded_classes), (long)Z_OBJCE_P(object), (void**)&oo_data) == FAILURE) {
+ php_error(E_WARNING, "internal problem trying to set property");
+ return 0;
+ }
temp_ce = *Z_OBJCE_P(object);
DISABLE_HANDLERS(temp_ce);
@@ -220,20 +263,39 @@ int call_set_handler(zval *object, zval *prop_name, zval *value TSRMLS_DC)
zval_copy_ctor(value_copy);
value = value_copy;
}
- args[0] = &prop_name;
- args[1] = &value;
- call_result = call_user_function_ex(NULL,
- &object,
- &set_handler,
- &retval,
- 2, args,
- 0, NULL TSRMLS_CC);
- Z_OBJCE_P(object) = orig_ce;
+ if (zend_hash_find(&oo_data->setters, Z_STRVAL_P(prop_name),
+ Z_STRLEN_P(prop_name)+1, (void **)&accessor_name) == SUCCESS) {
+ args[0] = &value;
- if (call_result == FAILURE || !retval) {
- php_error(E_WARNING, "unable to call %s::" SET_HANDLER "() handler", orig_ce->name);
- return 0;
+ call_result = call_user_function_ex(NULL,
+ &object,
+ *accessor_name,
+ &retval,
+ 1, args,
+ 0, NULL TSRMLS_CC);
+ Z_OBJCE_P(object) = orig_ce;
+
+ if (call_result == FAILURE || !retval) {
+ php_error(E_WARNING, "unable to call %s::" SET_HANDLER "_%s() handler", Z_OBJCE_P(object)->name, Z_STRVAL_P(prop_name));
+ return 0;
+ }
+ } else {
+ args[0] = &prop_name;
+ args[1] = &value;
+
+ call_result = call_user_function_ex(NULL,
+ &object,
+ &set_handler,
+ &retval,
+ 2, args,
+ 0, NULL TSRMLS_CC);
+ Z_OBJCE_P(object) = orig_ce;
+
+ if (call_result == FAILURE || !retval) {
+ php_error(E_WARNING, "unable to call %s::" SET_HANDLER "() handler", orig_ce->name);
+ return 0;
+ }
}
if (zval_is_true(retval)) {
@@ -243,12 +305,7 @@ int call_set_handler(zval *object, zval *prop_name, zval *value TSRMLS_DC)
zval_ptr_dtor(&retval);
- if (zend_hash_index_find(&OOG(overloaded_classes), (long)Z_OBJCE_P(object), (void*)&oo_data) == FAILURE) {
- php_error(E_WARNING, "internal problem trying to set property");
- return 0;
- }
-
- if (!oo_data.handle_property_set) {
+ if (!oo_data->handle_property_set) {
return 0;
}
@@ -388,14 +445,17 @@ static int overload_set_property(zend_property_reference *property_reference, zv
(void **)&object) == FAILURE) {
if (element == property_reference->elements_list->tail) {
- if (!call_set_handler(*object,
+ if (Z_OBJCE_PP(object)->handle_property_set == overload_set_property &&
+ call_set_handler(*object,
&overloaded_property->element,
value TSRMLS_CC)) {
CLEANUP_OO_CHAIN();
+ return SUCCESS;
+ } else {
+ php_error(E_WARNING, "Unable to set property: %s", Z_STRVAL(overloaded_property->element));
+ CLEANUP_OO_CHAIN();
return FAILURE;
}
- CLEANUP_OO_CHAIN();
- return SUCCESS;
}
if (Z_OBJCE_PP(object)->handle_property_get == overload_get_property &&
@@ -434,6 +494,10 @@ static void overload_call_method(INTERNAL_FUNCTION_PARAMETERS, zend_property_ref
zval call_handler, method_name, *method_name_ptr = &method_name;
zend_overloaded_element *method = (zend_overloaded_element *)property_reference->elements_list->tail->data;
+ /*
+ * We don't use the call handler if the invoked method exists in object's
+ * method table.
+ */
if (zend_hash_exists(&Z_OBJCE_P(object)->function_table,
Z_STRVAL(method->element),
Z_STRLEN(method->element) + 1)) {
@@ -528,6 +592,34 @@ static void overload_call_method(INTERNAL_FUNCTION_PARAMETERS, zend_property_ref
}
/* }}} */
+/* {{{ static int locate_accessors() */
+static int locate_accessors(zend_function *method, oo_class_data *oo_data TSRMLS_DC)
+{
+ zval *accessor_name;
+ char *function_name = method->common.function_name;
+ int function_name_len = strlen(method->common.function_name);
+
+ if (!strncmp(function_name, GET_HANDLER "_", sizeof(GET_HANDLER "_")-1)) {
+ MAKE_STD_ZVAL(accessor_name);
+ ZVAL_STRINGL(accessor_name, function_name, function_name_len, 1);
+ zend_hash_update(&oo_data->getters,
+ function_name + sizeof(GET_HANDLER "_") - 1,
+ function_name_len - sizeof(GET_HANDLER "_") + 2,
+ (void *)&accessor_name, sizeof(zval *), NULL);
+
+ } else if (!strncmp(function_name, SET_HANDLER "_", sizeof(SET_HANDLER "_")-1)) {
+ MAKE_STD_ZVAL(accessor_name);
+ ZVAL_STRINGL(accessor_name, function_name, function_name_len, 1);
+ zend_hash_update(&oo_data->setters,
+ function_name + sizeof(SET_HANDLER "_") - 1,
+ function_name_len - sizeof(SET_HANDLER "_") + 2,
+ (void *)&accessor_name, sizeof(zval *), NULL);
+ }
+
+ return 0;
+}
+/* }}} */
+
/* {{{ proto void overload(string class_entry)
Enables property and method call overloading for a class. */
PHP_FUNCTION(overload)
@@ -552,13 +644,20 @@ PHP_FUNCTION(overload)
RETURN_TRUE;
}
- if (zend_hash_exists(&ce->function_table, GET_HANDLER, sizeof(GET_HANDLER))) {
+ zend_hash_init(&oo_data.getters, 10, NULL, ZVAL_PTR_DTOR, 0);
+ zend_hash_init(&oo_data.setters, 10, NULL, ZVAL_PTR_DTOR, 0);
+
+ zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t)locate_accessors, (void *)&oo_data TSRMLS_CC);
+
+ if (zend_hash_exists(&ce->function_table, GET_HANDLER, sizeof(GET_HANDLER)) ||
+ zend_hash_num_elements(&oo_data.getters)) {
oo_data.handle_property_get = ce->handle_property_get;
ce->handle_property_get = overload_get_property;
} else
oo_data.handle_property_get = NULL;
- if (zend_hash_exists(&ce->function_table, SET_HANDLER, sizeof(SET_HANDLER))) {
+ if (zend_hash_exists(&ce->function_table, SET_HANDLER, sizeof(SET_HANDLER)) ||
+ zend_hash_num_elements(&oo_data.setters)) {
oo_data.handle_property_set = ce->handle_property_set;
ce->handle_property_set = overload_set_property;
} else
diff --git a/ext/overload/php_overload.h b/ext/overload/php_overload.h
index 8b93452667..e80724af5f 100644
--- a/ext/overload/php_overload.h
+++ b/ext/overload/php_overload.h
@@ -36,6 +36,7 @@ extern zend_module_entry overload_module_entry;
#endif
PHP_MINIT_FUNCTION(overload);
+PHP_MSHUTDOWN_FUNCTION(overload);
PHP_MINFO_FUNCTION(overload);
PHP_FUNCTION(overload);