cvs: php4 / NEWS /ext/standard Makefile.in aggregation.c aggregation.h basic_functions.c basic_functions.h php_standard.h
/ext/standard/tests/aggregation .cvsignore aggregate.lib.php aggregate.phpt aggregate_methods.phpt aggregate_methods_by_list.phpt
aggregate_methods_by_regexp.phpt aggregate_properties.phpt aggregate_properties_by_list.phpt aggregate_properties_by_regexp.phpt aggregation_info.phpt
deaggregate.phpt
| From: | Andrei Zmievski | Date: | Thu, 21 Feb 2002 03:32:44 +0000 |
| Subject: | cvs: php4 / NEWS /ext/standard Makefile.in aggregation.c aggregation.h basic_functions.c basic_functions.h php_standard.h /ext/standard/tests/aggregation .cvsignore aggregate.lib.php aggregate.phpt aggregate_methods.phpt aggregate_methods_by_list.phpt aggregate_methods_by_regexp.phpt aggregate_properties.phpt aggregate_properties_by_list.phpt aggregate_properties_by_regexp.phpt aggregation_info.phpt deaggregate.phpt |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-9413@lists.php.net to get a copy of this message | ||
andrei Wed Feb 20 22:32:44 2002 EDT
Added files:
/php4/ext/standard aggregation.c aggregation.h
/php4/ext/standard/tests/aggregation .cvsignore aggregate.lib.php
aggregate.phpt
aggregate_methods.phpt
aggregate_methods_by_list.phpt
aggregate_methods_by_regexp.phpt
aggregate_properties.phpt
aggregate_properties_by_list.phpt
aggregate_properties_by_regexp.phpt
aggregation_info.phpt
deaggregate.phpt
Modified files:
/php4 NEWS
/php4/ext/standard Makefile.in basic_functions.c basic_functions.h
php_standard.h
Log:
Adding object aggregation capability along with tests.
Index: php4/NEWS diff -u php4/NEWS:1.869 php4/NEWS:1.870 --- php4/NEWS:1.869 Fri Feb 15 12:39:25 2002 +++ php4/NEWS Wed Feb 20 22:32:38 2002 @@ -1,6 +1,7 @@ PHP 4 NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 200?, Version 4.2.0-dev +- Added object aggregation capability, see aggregation_*() functions. (Andrei) - Added debug_zval_dump(), which works similar to var_dump, yet displays extra internal information such as refcounts and true type names. (Jason) - Added Andrei's tokenizer extension. (Stig) Index: php4/ext/standard/Makefile.in diff -u php4/ext/standard/Makefile.in:1.25 php4/ext/standard/Makefile.in:1.26 --- php4/ext/standard/Makefile.in:1.25 Thu Nov 15 04:53:40 2001 +++ php4/ext/standard/Makefile.in Wed Feb 20 22:32:40 2002 @@ -9,7 +9,7 @@ syslog.c type.c uniqid.c url.c url_scanner.c var.c versioning.c assert.c \ strnatcmp.c levenshtein.c incomplete_class.c url_scanner_ex.c \ ftp_fopen_wrapper.c http_fopen_wrapper.c php_fopen_wrapper.c credits.c \ - var_unserializer.c ftok.c + var_unserializer.c ftok.c aggregation.c include $(top_srcdir)/build/dynlib.mk Index: php4/ext/standard/basic_functions.c diff -u php4/ext/standard/basic_functions.c:1.442 php4/ext/standard/basic_functions.c:1.443 --- php4/ext/standard/basic_functions.c:1.442 Sun Feb 10 12:38:15 2002 +++ php4/ext/standard/basic_functions.c Wed Feb 20 22:32:41 2002 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: basic_functions.c,v 1.442 2002/02/10 17:38:15 jason Exp $ */ +/* $Id: basic_functions.c,v 1.443 2002/02/21 03:32:41 andrei Exp $ */ #include "php.h" #include "php_main.h" @@ -812,6 +812,17 @@ #endif PHP_FE(str_rot13, NULL) + + /* functions from aggregate.c */ + PHP_FE(aggregate, first_arg_force_ref) + PHP_FE(aggregate_methods, first_arg_force_ref) + PHP_FE(aggregate_methods_by_list, first_arg_force_ref) + PHP_FE(aggregate_methods_by_regexp, first_arg_force_ref) + PHP_FE(aggregate_properties, first_arg_force_ref) + PHP_FE(aggregate_properties_by_list, first_arg_force_ref) + PHP_FE(aggregate_properties_by_regexp, first_arg_force_ref) + PHP_FE(deaggregate, first_arg_force_ref) + PHP_FE(aggregation_info, first_arg_force_ref) {NULL, NULL, NULL} }; @@ -895,6 +906,7 @@ BG(next) = NULL; BG(left) = -1; BG(user_tick_functions) = NULL; + BG(aggregation_table) = NULL; zend_hash_init(&BG(sm_protected_env_vars), 5, NULL, NULL, 1); BG(sm_allowed_env_vars) = NULL; @@ -1100,6 +1112,13 @@ efree(BG(user_tick_functions)); BG(user_tick_functions) = NULL; } + + if (BG(aggregation_table)) { + zend_hash_destroy(BG(aggregation_table)); + efree(BG(aggregation_table)); + BG(aggregation_table) = NULL; + } + #ifdef HAVE_MMAP if (BG(mmap_file)) { munmap(BG(mmap_file), BG(mmap_len)); @@ -2243,7 +2262,7 @@ BG(user_tick_functions) = (zend_llist *) emalloc(sizeof(zend_llist)); zend_llist_init(BG(user_tick_functions), sizeof(user_tick_function_entry), - (void (*)(void *)) user_tick_function_dtor, 0); + (llist_dtor_func_t) user_tick_function_dtor, 0); php_add_tick_function(run_user_tick_functions); } Index: php4/ext/standard/basic_functions.h diff -u php4/ext/standard/basic_functions.h:1.102 php4/ext/standard/basic_functions.h:1.103 --- php4/ext/standard/basic_functions.h:1.102 Wed Jan 9 18:47:41 2002 +++ php4/ext/standard/basic_functions.h Wed Feb 20 22:32:42 2002 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: basic_functions.h,v 1.102 2002/01/09 23:47:41 jimw Exp $ */ +/* $Id: basic_functions.h,v 1.103 2002/02/21 03:32:42 andrei Exp $ */ #ifndef BASIC_FUNCTIONS_H #define BASIC_FUNCTIONS_H @@ -180,6 +180,8 @@ void *mmap_file; size_t mmap_len; #endif + + HashTable *aggregation_table; } php_basic_globals; #ifdef ZTS Index: php4/ext/standard/php_standard.h diff -u php4/ext/standard/php_standard.h:1.13 php4/ext/standard/php_standard.h:1.14 --- php4/ext/standard/php_standard.h:1.13 Wed Jan 9 18:47:41 2002 +++ php4/ext/standard/php_standard.h Wed Feb 20 22:32:42 2002 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_standard.h,v 1.13 2002/01/09 23:47:41 jimw Exp $ */ +/* $Id: php_standard.h,v 1.14 2002/02/21 03:32:42 andrei Exp $ */ #include "basic_functions.h" #include "php_math.h" @@ -58,6 +58,7 @@ #include "php_versioning.h" #include "php_ftok.h" #include "php_type.h" +#include "aggregation.h" #define phpext_standard_ptr basic_functions_module_ptr Index: php4/ext/standard/aggregation.c +++ php4/ext/standard/aggregation.c /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2002 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ /* $Id: aggregation.c,v 1.1 2002/02/21 03:32:41 andrei Exp $ */ #include "php.h" #include "basic_functions.h" #include "aggregation.h" #if HAVE_PCRE || HAVE_BUNDLED_PCRE #include "ext/pcre/php_pcre.h" #endif static void aggregation_info_dtor(aggregation_info *info) { destroy_zend_class(info->new_ce); efree(info->new_ce); zval_ptr_dtor(&info->aggr_members); } /* {{{ static zval* array_to_hash */ static zval *array_to_hash(zval *array) { zval *hash, **entry; char *name_lc; /* * Well, this just transposes the array, popularly known as flipping it, or * giving it the finger. */ MAKE_STD_ZVAL(hash); array_init(hash); for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(array)); zend_hash_get_current_data(Z_ARRVAL_P(array), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(array))) { if (Z_TYPE_PP(entry) == IS_STRING) { /* * I hate case-insensitivity. Die, die, die. */ name_lc = estrndup(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry)); zend_str_tolower(name_lc, Z_STRLEN_PP(entry)); add_assoc_bool_ex(hash, name_lc, Z_STRLEN_PP(entry)+1, 1); efree(name_lc); } } return hash; } /* }}} */ /* {{{ static void aggregate_methods() */ static void aggregate_methods(zend_class_entry *ce, zend_class_entry *from_ce, int aggr_type, zval *aggr_filter, zend_bool exclude, zval *aggr_methods TSRMLS_DC) { HashPosition pos; zend_function *function; char *func_name; uint func_name_len; ulong num_key; zval *list_hash = NULL; pcre *re = NULL; pcre_extra *re_extra = NULL; int re_options = 0; /* * Flip the array for easy lookup, or compile the regexp. */ if (aggr_type == AGGREGATE_BY_LIST) { list_hash = array_to_hash(aggr_filter); } else if (aggr_type == AGGREGATE_BY_REGEXP) { if ((re = pcre_get_compiled_regex(Z_STRVAL_P(aggr_filter), &re_extra, &re_options)) == NULL) { return; } } /* * "Just because it's not nice doesn't mean it's not miraculous." * -- _Interesting Times_, Terry Pratchett */ /* * Aggregating by list without exclusion can be done more efficiently if we * iterate through the list and check against function table instead of the * other way around. */ if (aggr_type != AGGREGATE_BY_LIST || exclude) { zend_hash_internal_pointer_reset_ex(&from_ce->function_table, &pos); while (zend_hash_get_current_data_ex(&from_ce->function_table, (void**)&function, &pos) == SUCCESS) { zend_hash_get_current_key_ex(&from_ce->function_table, &func_name, &func_name_len, &num_key, 0, &pos); /* We do not aggregate: * 1. constructors */ if (!strncmp(func_name, from_ce->name, MIN(func_name_len-1, from_ce->name_length)) || /* 2. private methods (heh, like we really have them) */ func_name[0] == '_' || /* 3. explicitly excluded methods */ (aggr_type == AGGREGATE_BY_LIST && zend_hash_exists(Z_ARRVAL_P(list_hash), func_name, func_name_len)) || /* 4. methods matching regexp as modified by the exclusion flag */ (aggr_type == AGGREGATE_BY_REGEXP && (pcre_exec(re, re_extra, func_name, func_name_len-1, 0, 0, NULL, 0) < 0) ^ exclude) == 1) { zend_hash_move_forward_ex(&from_ce->function_table, &pos); continue; } /* * This is where the magic happens. */ if (zend_hash_add(&ce->function_table, func_name, func_name_len, (void*)function, sizeof(zend_function), NULL) == SUCCESS) { add_next_index_stringl(aggr_methods, func_name, func_name_len-1, 1); } zend_hash_move_forward_ex(&from_ce->function_table, &pos); } } else { /* * This is just like above except the other way around. */ zend_hash_internal_pointer_reset(Z_ARRVAL_P(list_hash)); while (zend_hash_get_current_key_ex(Z_ARRVAL_P(list_hash), &func_name, &func_name_len, &num_key, 0, NULL) == HASH_KEY_IS_STRING) { if (!strncmp(func_name, from_ce->name, MIN(func_name_len-1, from_ce->name_length)) || func_name[0] == '_' || zend_hash_find(&from_ce->function_table, func_name, func_name_len, (void**)&function) == FAILURE) { zend_hash_move_forward(Z_ARRVAL_P(list_hash)); continue; } if (zend_hash_add(&ce->function_table, func_name, func_name_len, (void*)function, sizeof(zend_function), NULL) == SUCCESS) { add_next_index_stringl(aggr_methods, func_name, func_name_len-1, 1); } zend_hash_move_forward(Z_ARRVAL_P(list_hash)); } } if (list_hash) { zval_ptr_dtor(&list_hash); } } /* }}} */ /* {{{ static void aggregate_properties() */ static void aggregate_properties(zval *obj, zend_class_entry *from_ce, int aggr_type, zval *aggr_filter, zend_bool exclude, zval *aggr_props TSRMLS_DC) { HashPosition pos; zval **prop; char *prop_name; uint prop_name_len; ulong num_key; zval *list_hash = NULL; pcre *re = NULL; pcre_extra *re_extra = NULL; int re_options = 0; if (!from_ce->constants_updated) { zend_hash_apply_with_argument(&from_ce->default_properties, (apply_func_arg_t) zval_update_constant, (void *) 1 TSRMLS_CC); from_ce->constants_updated = 1; } /* * Flip the array for easy lookup, or compile the regexp. */ if (aggr_type == AGGREGATE_BY_LIST) { list_hash = array_to_hash(aggr_filter); } else if (aggr_type == AGGREGATE_BY_REGEXP) { if ((re = pcre_get_compiled_regex(Z_STRVAL_P(aggr_filter), &re_extra, &re_options)) == NULL) { return; } } /* * "Just because it's not nice doesn't mean it's not miraculous." * -- _Interesting Times_, Terry Pratchett */ /* * Aggregating by list without exclusion can be done more efficiently if we * iterate through the list and check against default properties table * instead of the other way around. */ if (aggr_type != AGGREGATE_BY_LIST || exclude) { zend_hash_internal_pointer_reset_ex(&from_ce->default_properties, &pos); while (zend_hash_get_current_data_ex(&from_ce->default_properties, (void**)&prop, &pos) == SUCCESS) { zend_hash_get_current_key_ex(&from_ce->default_properties, &prop_name, &prop_name_len, &num_key, 0, &pos); /* We do not aggregate: * 1. private properties (heh, like we really have them) */ if (prop_name[0] == '_' || /* 2. explicitly excluded properties */ (aggr_type == AGGREGATE_BY_LIST && zend_hash_exists(Z_ARRVAL_P(list_hash), prop_name, prop_name_len)) || /* 3. properties matching regexp as modified by the exclusion flag */ (aggr_type == AGGREGATE_BY_REGEXP && (pcre_exec(re, re_extra, prop_name, prop_name_len-1, 0, 0, NULL, 0) < 0) ^ exclude) == 1) { zend_hash_move_forward_ex(&from_ce->default_properties, &pos); continue; } /* * This is where the magic happens. */ if (zend_hash_add(Z_OBJPROP_P(obj), prop_name, prop_name_len, (void*)prop, sizeof(zval *), NULL) == SUCCESS) { zval_add_ref(prop); add_next_index_stringl(aggr_props, prop_name, prop_name_len-1, 1); } zend_hash_move_forward_ex(&from_ce->default_properties, &pos); } } else { /* * This is just like above except the other way around. */ zend_hash_internal_pointer_reset(Z_ARRVAL_P(list_hash)); while (zend_hash_get_current_key_ex(Z_ARRVAL_P(list_hash), &prop_name, &prop_name_len, &num_key, 0, NULL) == HASH_KEY_IS_STRING) { if (prop_name[0] == '_' || zend_hash_find(&from_ce->default_properties, prop_name, prop_name_len, (void**)&prop) == FAILURE) { zend_hash_move_forward(Z_ARRVAL_P(list_hash)); continue; } if (zend_hash_add(Z_OBJPROP_P(obj), prop_name, prop_name_len, (void*)prop, sizeof(zval *), NULL) == SUCCESS) { zval_add_ref(prop); add_next_index_stringl(aggr_props, prop_name, prop_name_len-1, 1); } zend_hash_move_forward(Z_ARRVAL_P(list_hash)); } } if (list_hash) { zval_ptr_dtor(&list_hash); } } /* }}} */ /* {{{ static void aggregate() */ static void aggregate(INTERNAL_FUNCTION_PARAMETERS, int aggr_what, int aggr_type) { /* Incoming parameters. */ zval *obj, *aggr_list = NULL; char *class_name, *class_name_lc, *aggr_regexp; int class_name_len, aggr_regexp_len; zend_bool exclude = 0; /* Other variables. */ zval **aggr_members, z_aggr_regexp; zend_class_entry *ce, *new_ce; zend_function tmp_zend_function; aggregation_info aggr_info_new, *aggr_info = &aggr_info_new; zval *aggr_methods_new, **aggr_methods = &aggr_methods_new; zval *aggr_props_new, **aggr_props = &aggr_props_new; zval *aggr_filter = NULL; int zpp_result = FAILURE; /* * Ah, the beauty of the new parameter parsing API. Almost as good as Heidi Klum. */ switch (aggr_type) { case AGGREGATE_ALL: zpp_result = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "os", &obj, &class_name, &class_name_len); break; case AGGREGATE_BY_LIST: zpp_result = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "osa|b", &obj, &class_name, &class_name_len, &aggr_list, &exclude); break; case AGGREGATE_BY_REGEXP: zpp_result = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "oss|b", &obj, &class_name, &class_name_len, &aggr_regexp, &aggr_regexp_len, &exclude); ZVAL_STRINGL(&z_aggr_regexp, aggr_regexp, aggr_regexp_len, 0); break; } if (zpp_result == FAILURE) { return; } /* * Case-insensitivity is like that last freaking mutant from horror movies: * irradiated, blown in half, its eyes melting in their sockets, yet still * dragging itself closer and closer to you until it's pulverized into * microscopic pieces via some last-minute contrivance. And even then you * are not sure that it's finally dead. But that's just how I feel. */ class_name_lc = estrndup(class_name, class_name_len); zend_str_tolower(class_name_lc, class_name_len); if (zend_hash_find(EG(class_table), class_name_lc, class_name_len+1, (void **)&ce) == FAILURE) { php_error(E_WARNING, "%s() expects parameter 2 to be a valid class name, '%s' given\n", get_active_function_name(TSRMLS_C), class_name); efree(class_name_lc); return; } /* * And God said, Let there be light; and there was light. But only once. */ if (!BG(aggregation_table)) { BG(aggregation_table) = (HashTable *) emalloc(sizeof(HashTable)); zend_hash_init(BG(aggregation_table), 5, NULL, (dtor_func_t) aggregation_info_dtor, 0); } /* * Digging deep in the rabbit hole with a long object.. and coming up * more empty than the imagination of whoever made "Battlefield Earth". */ if (zend_hash_index_find(BG(aggregation_table), (long)obj, (void**)&aggr_info) == FAILURE) { zval *tmp; /* * You are not expected to understand this. */ new_ce = emalloc(sizeof(zend_class_entry)); new_ce->type = ZEND_USER_CLASS; new_ce->name = estrndup(Z_OBJCE_P(obj)->name, Z_OBJCE_P(obj)->name_length); new_ce->name_length = Z_OBJCE_P(obj)->name_length; new_ce->parent = Z_OBJCE_P(obj)->parent; new_ce->refcount = (int *) emalloc(sizeof(int)); *new_ce->refcount = 1; new_ce->constants_updated = Z_OBJCE_P(obj)->constants_updated; zend_hash_init(&new_ce->function_table, 10, NULL, ZEND_FUNCTION_DTOR, 0); zend_hash_init(&new_ce->default_properties, 10, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(&new_ce->function_table, &Z_OBJCE_P(obj)->function_table, (copy_ctor_func_t) function_add_ref, &tmp_zend_function, sizeof(zend_function)); zend_hash_copy(&new_ce->default_properties, &Z_OBJCE_P(obj)->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); new_ce->builtin_functions = Z_OBJCE_P(obj)->builtin_functions; new_ce->handle_function_call = Z_OBJCE_P(obj)->handle_function_call; new_ce->handle_property_get = Z_OBJCE_P(obj)->handle_property_get; new_ce->handle_property_set = Z_OBJCE_P(obj)->handle_property_set; /* * Okay, that was kind of exhausting. Let's invoke programmer virtue #1 * and stuff this where it belongs so we don't have to work so hard next * time. */ Z_OBJCE_P(obj) = new_ce; aggr_info_new.new_ce = new_ce; MAKE_STD_ZVAL(aggr_info_new.aggr_members); array_init(aggr_info_new.aggr_members); zend_hash_index_update(BG(aggregation_table), (long)obj, (void *)&aggr_info_new, sizeof(aggregation_info), NULL); } else { /* * Seek and ye shall find. */ new_ce = aggr_info->new_ce; } /* * This should be easy to understand. If not, ask Rasmus about it at his * next tutorial. */ if (zend_hash_find(Z_ARRVAL_P(aggr_info->aggr_members), class_name_lc, class_name_len+1, (void **)&aggr_members) == FAILURE) { zval *tmp; MAKE_STD_ZVAL(tmp); array_init(tmp); MAKE_STD_ZVAL(aggr_methods_new); array_init(aggr_methods_new); MAKE_STD_ZVAL(aggr_props_new); array_init(aggr_props_new); add_assoc_zval_ex(tmp, "methods", sizeof("methods"), aggr_methods_new); add_assoc_zval_ex(tmp, "properties", sizeof("properties"), aggr_props_new); zend_hash_add(Z_ARRVAL_P(aggr_info->aggr_members), class_name_lc, class_name_len+1, &tmp, sizeof(zval *), NULL); } else { zend_hash_find(Z_ARRVAL_PP(aggr_members), "methods", sizeof("methods"), (void**)&aggr_methods); zend_hash_find(Z_ARRVAL_PP(aggr_members), "properties", sizeof("properties"), (void**)&aggr_props); } if (aggr_type == AGGREGATE_BY_LIST) { aggr_filter = aggr_list; } else if (aggr_type == AGGREGATE_BY_REGEXP) { aggr_filter = &z_aggr_regexp; } if (aggr_what == AGGREGATE_METHODS || aggr_what == AGGREGATE_ALL) { aggregate_methods(new_ce, ce, aggr_type, aggr_filter, exclude, *aggr_methods TSRMLS_CC); } if (aggr_what == AGGREGATE_PROPERTIES || aggr_what == AGGREGATE_ALL) { aggregate_properties(obj, ce, aggr_type, aggr_filter, exclude, *aggr_props TSRMLS_CC); } /* * Yes, we have to clean up after monsters. Tsk-tsk. */ efree(class_name_lc); } /* }}} */ /* {{{ proto void aggregate(object obj, string class) */ PHP_FUNCTION(aggregate) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_ALL, AGGREGATE_ALL); } /* }}} */ /* {{{ proto void aggregate_methods(object obj, string class) */ PHP_FUNCTION(aggregate_methods) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_METHODS, AGGREGATE_ALL); } /* }}} */ /* {{{ proto void aggregate_methods_by_list(object obj, string class, array method_list [, bool exclude]) */ PHP_FUNCTION(aggregate_methods_by_list) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_METHODS, AGGREGATE_BY_LIST); } /* }}} */ /* {{{ proto void aggregate_methods_by_regexp(object obj, string class, string regexp [, bool exclude]) */ PHP_FUNCTION(aggregate_methods_by_regexp) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_METHODS, AGGREGATE_BY_REGEXP); } /* }}} */ /* {{{ proto void aggregate_properties(object obj, string class) */ PHP_FUNCTION(aggregate_properties) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_PROPERTIES, AGGREGATE_ALL); } /* }}} */ /* {{{ proto void aggregate_properites_by_list(object obj, string class, array props_list [, bool exclude]) */ PHP_FUNCTION(aggregate_properties_by_list) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_PROPERTIES, AGGREGATE_BY_LIST); } /* }}} */ /* {{{ proto void aggregate_properties_by_regexp(object obj, string class, string regexp [, bool exclude]) */ PHP_FUNCTION(aggregate_properties_by_regexp) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_PROPERTIES, AGGREGATE_BY_REGEXP); } /* }}} */ /* {{{ proto array aggregation_info(object obj) */ PHP_FUNCTION(aggregation_info) { zval *obj; aggregation_info *aggr_info; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) { return; } if (!BG(aggregation_table) || zend_hash_index_find(BG(aggregation_table), (long)obj, (void**)&aggr_info) == FAILURE) { RETURN_FALSE; } *return_value = *aggr_info->aggr_members; zval_copy_ctor(return_value); } /* }}} */ /* {{{ proto void deaggregate(object obj [, string class]) */ PHP_FUNCTION(deaggregate) { zval *obj; char *class_name = NULL, *class_name_lc; int class_name_len; aggregation_info *aggr_info; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|s", &obj, &class_name, &class_name_len) == FAILURE) { return; } if (!BG(aggregation_table) || zend_hash_index_find(BG(aggregation_table), (long)obj, (void**)&aggr_info) == FAILURE) { return; } if (class_name) { zval **aggr_members, **aggr_methods, **aggr_props, **method, **prop; class_name_lc = estrndup(class_name, class_name_len); zend_str_tolower(class_name_lc, class_name_len); if (zend_hash_find(Z_ARRVAL_P(aggr_info->aggr_members), class_name_lc, class_name_len+1, (void **)&aggr_members) == FAILURE) { efree(class_name_lc); return; } zend_hash_find(Z_ARRVAL_PP(aggr_members), "methods", sizeof("methods"), (void**)&aggr_methods); for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(aggr_methods)); zend_hash_get_current_data(Z_ARRVAL_PP(aggr_methods), (void**)&method) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_PP(aggr_methods))) { zend_hash_del(&Z_OBJCE_P(obj)->function_table, Z_STRVAL_PP(method), Z_STRLEN_PP(method)+1); } zend_hash_find(Z_ARRVAL_PP(aggr_members), "properties", sizeof("properties"), (void**)&aggr_props); for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(aggr_props)); zend_hash_get_current_data(Z_ARRVAL_PP(aggr_props), (void**)&prop) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_PP(aggr_props))) { zend_hash_del(Z_OBJPROP_P(obj), Z_STRVAL_PP(prop), Z_STRLEN_PP(prop)+1); } zend_hash_del(Z_ARRVAL_P(aggr_info->aggr_members), class_name_lc, class_name_len+1); efree(class_name_lc); } else { zend_class_entry *orig_ce; zval **aggr_members; zval **aggr_props, **prop; if (zend_hash_find(EG(class_table), Z_OBJCE_P(obj)->name, Z_OBJCE_P(obj)->name_length+1, (void **)&orig_ce) == FAILURE) { php_error(E_WARNING, "internal deaggregation error"); return; } for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(aggr_info->aggr_members)); zend_hash_get_current_data(Z_ARRVAL_P(aggr_info->aggr_members), (void **)&aggr_members) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(aggr_info->aggr_members))) { zend_hash_find(Z_ARRVAL_PP(aggr_members), "properties", sizeof("properties"), (void**)&aggr_props); for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(aggr_props)); zend_hash_get_current_data(Z_ARRVAL_PP(aggr_props), (void**)&prop) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_PP(aggr_props))) { zend_hash_del(Z_OBJPROP_P(obj), Z_STRVAL_PP(prop), Z_STRLEN_PP(prop)+1); } } Z_OBJCE_P(obj) = orig_ce; zend_hash_index_del(BG(aggregation_table), (long)obj); } } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ Index: php4/ext/standard/aggregation.h +++ php4/ext/standard/aggregation.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2002 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ /* $Id: aggregation.h,v 1.1 2002/02/21 03:32:41 andrei Exp $ */ #ifndef AGGREGATION_H #define AGGREGATION_H #define AGGREGATE_ALL 0 #define AGGREGATE_METHODS 1 #define AGGREGATE_PROPERTIES 2 #define AGGREGATE_BY_LIST 1 #define AGGREGATE_BY_REGEXP 2 /* * Data structure that is stored in aggregation_table hashtable for each object. */ typedef struct { zend_class_entry *new_ce; zval *aggr_members; } aggregation_info; PHP_FUNCTION(aggregate); PHP_FUNCTION(aggregate_methods); PHP_FUNCTION(aggregate_methods_by_list); PHP_FUNCTION(aggregate_methods_by_regexp); PHP_FUNCTION(aggregate_properties); PHP_FUNCTION(aggregate_properties_by_list); PHP_FUNCTION(aggregate_properties_by_regexp); PHP_FUNCTION(aggregate); PHP_FUNCTION(deaggregate); PHP_FUNCTION(aggregation_info); #endif /* AGGREGATION_H */ Index: php4/ext/standard/tests/aggregation/.cvsignore +++ php4/ext/standard/tests/aggregation/.cvsignore *.exp *.out *.php phpt.* Index: php4/ext/standard/tests/aggregation/aggregate.lib.php +++ php4/ext/standard/tests/aggregation/aggregate.lib.php <?php class simple { var $simple_prop = 100; function simple() { print "I'm alive!\n"; } } class helper { var $my_prop = 5; var $your_prop = array('init' => PHP_VERSION); var $our_prop = '****'; var $_priv_prop = null; function helper() { print "just trying to help\n"; } function do_this() { print "I'm helping!\n"; } function do_that() { print "I'm aggregating!\n"; } function just_another_method() { print "yep, that's me\n"; } function _private() { print "Don't touch me!\n"; } function __wakeup() { } } class mixin { var $simple_prop = true; var $mix = true; function mix_it() { print "mixing\n"; } } class moby { function mix_it() { print "I'm redundant!\n"; } } ?> Index: php4/ext/standard/tests/aggregation/aggregate.phpt +++ php4/ext/standard/tests/aggregation/aggregate.phpt --TEST-- aggregating everything --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate($obj, 'helper'); $obj->do_this(); $obj->do_that(); print $obj->our_prop; ?> --EXPECT-- I'm alive! I'm helping! I'm aggregating! **** Index: php4/ext/standard/tests/aggregation/aggregate_methods.phpt +++ php4/ext/standard/tests/aggregation/aggregate_methods.phpt --TEST-- aggregating all methods --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_methods($obj, 'mixin'); $obj->mix_it(); print $obj->simple_prop."\n"; print implode(',', get_class_methods($obj))."\n"; print implode(',', array_keys(get_object_vars($obj)))."\n"; aggregate_methods($obj, 'moby'); $obj->mix_it(); ?> --EXPECT-- I'm alive! mixing 100 simple,mix_it simple_prop mixing Index: php4/ext/standard/tests/aggregation/aggregate_methods_by_list.phpt +++ php4/ext/standard/tests/aggregation/aggregate_methods_by_list.phpt --TEST-- aggregating methods specified in the list --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_methods_by_list($obj, 'helper', array('just_another_method')); print implode(',', get_class_methods($obj))."\n"; $obj2 = new simple(); aggregate_methods_by_list($obj2, 'helper', array('just_another_method'), true); print implode(',', get_class_methods($obj2))."\n"; $obj->just_another_method(); ?> --EXPECT-- I'm alive! simple,just_another_method I'm alive! simple,do_this,do_that yep, that's me Index: php4/ext/standard/tests/aggregation/aggregate_methods_by_regexp.phpt +++ php4/ext/standard/tests/aggregation/aggregate_methods_by_regexp.phpt --TEST-- aggregating methods matching regular expression --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_methods_by_regexp($obj, 'helper', '/^do/'); print implode(',', get_class_methods($obj))."\n"; $obj2 = new simple(); aggregate_methods_by_regexp($obj2, 'helper', '/^do/', true); print implode(',', get_class_methods($obj2))."\n"; ?> --EXPECT-- I'm alive! simple,do_this,do_that I'm alive! simple,just_another_method Index: php4/ext/standard/tests/aggregation/aggregate_properties.phpt +++ php4/ext/standard/tests/aggregation/aggregate_properties.phpt --TEST-- aggregating all default properties --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_properties($obj, 'mixin'); print implode(',', array_keys(get_object_vars($obj)))."\n"; print $obj->simple_prop."\n"; print implode(',', get_class_methods($obj))."\n"; ?> --EXPECT-- I'm alive! simple_prop,mix 100 simple Index: php4/ext/standard/tests/aggregation/aggregate_properties_by_list.phpt +++ php4/ext/standard/tests/aggregation/aggregate_properties_by_list.phpt --TEST-- aggregating default properties specified in the list --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_properties_by_list($obj, 'helper', array('my_prop', 'our_prop')); print implode(',', array_keys(get_object_vars($obj)))."\n"; $obj2 = new simple(); aggregate_properties_by_list($obj2, 'helper', array('my_prop'), true); print implode(',', array_keys(get_object_vars($obj2)))."\n"; ?> --EXPECT-- I'm alive! simple_prop,my_prop,our_prop I'm alive! simple_prop,your_prop,our_prop Index: php4/ext/standard/tests/aggregation/aggregate_properties_by_regexp.phpt +++ php4/ext/standard/tests/aggregation/aggregate_properties_by_regexp.phpt --TEST-- aggregating default properties matching regular expression --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_properties_by_regexp($obj, 'helper', '/^my/'); print implode(',', array_keys(get_object_vars($obj)))."\n"; $obj2 = new simple(); aggregate_properties_by_regexp($obj2, 'helper', '/^my/', true); print implode(',', array_keys(get_object_vars($obj2)))."\n"; ?> --EXPECT-- I'm alive! simple_prop,my_prop I'm alive! simple_prop,your_prop,our_prop Index: php4/ext/standard/tests/aggregation/aggregation_info.phpt +++ php4/ext/standard/tests/aggregation/aggregation_info.phpt --TEST-- retrieving aggregation info --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate($obj, 'mixin'); print_r(aggregation_info($obj)); ?> --EXPECT-- I'm alive! Array ( [mixin] => Array ( [methods] => Array ( [0] => mix_it ) [properties] => Array ( [0] => mix ) ) ) Index: php4/ext/standard/tests/aggregation/deaggregate.phpt +++ php4/ext/standard/tests/aggregation/deaggregate.phpt --TEST-- deaggreating --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate($obj, 'helper'); aggregate($obj, 'mixin'); print_r(aggregation_info($obj)); deaggregate($obj, 'helper'); print_r(aggregation_info($obj)); deaggregate($obj); var_dump(aggregation_info($obj)); ?> --EXPECT-- I'm alive! Array ( [helper] => Array ( [methods] => Array ( [0] => do_this [1] => do_that [2] => just_another_method ) [properties] => Array ( [0] => my_prop [1] => your_prop [2] => our_prop ) ) [mixin] => Array ( [methods] => Array ( [0] => mix_it ) [properties] => Array ( [0] => mix ) ) ) Array ( [mixin] => Array ( [methods] => Array ( [0] => mix_it ) [properties] => Array ( [0] => mix ) ) ) bool(false)
Index: php4/NEWS diff -u php4/NEWS:1.869 php4/NEWS:1.870 --- php4/NEWS:1.869 Fri Feb 15 12:39:25 2002 +++ php4/NEWS Wed Feb 20 22:32:38 2002 @@ -1,6 +1,7 @@ PHP 4 NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 200?, Version 4.2.0-dev +- Added object aggregation capability, see aggregation_*() functions. (Andrei) - Added debug_zval_dump(), which works similar to var_dump, yet displays extra internal information such as refcounts and true type names. (Jason) - Added Andrei's tokenizer extension. (Stig) Index: php4/ext/standard/Makefile.in diff -u php4/ext/standard/Makefile.in:1.25 php4/ext/standard/Makefile.in:1.26 --- php4/ext/standard/Makefile.in:1.25 Thu Nov 15 04:53:40 2001 +++ php4/ext/standard/Makefile.in Wed Feb 20 22:32:40 2002 @@ -9,7 +9,7 @@ syslog.c type.c uniqid.c url.c url_scanner.c var.c versioning.c assert.c \ strnatcmp.c levenshtein.c incomplete_class.c url_scanner_ex.c \ ftp_fopen_wrapper.c http_fopen_wrapper.c php_fopen_wrapper.c credits.c \ - var_unserializer.c ftok.c + var_unserializer.c ftok.c aggregation.c include $(top_srcdir)/build/dynlib.mk Index: php4/ext/standard/basic_functions.c diff -u php4/ext/standard/basic_functions.c:1.442 php4/ext/standard/basic_functions.c:1.443 --- php4/ext/standard/basic_functions.c:1.442 Sun Feb 10 12:38:15 2002 +++ php4/ext/standard/basic_functions.c Wed Feb 20 22:32:41 2002 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: basic_functions.c,v 1.442 2002/02/10 17:38:15 jason Exp $ */ +/* $Id: basic_functions.c,v 1.443 2002/02/21 03:32:41 andrei Exp $ */ #include "php.h" #include "php_main.h" @@ -812,6 +812,17 @@ #endif PHP_FE(str_rot13, NULL) + + /* functions from aggregate.c */ + PHP_FE(aggregate, first_arg_force_ref) + PHP_FE(aggregate_methods, first_arg_force_ref) + PHP_FE(aggregate_methods_by_list, first_arg_force_ref) + PHP_FE(aggregate_methods_by_regexp, first_arg_force_ref) + PHP_FE(aggregate_properties, first_arg_force_ref) + PHP_FE(aggregate_properties_by_list, first_arg_force_ref) + PHP_FE(aggregate_properties_by_regexp, first_arg_force_ref) + PHP_FE(deaggregate, first_arg_force_ref) + PHP_FE(aggregation_info, first_arg_force_ref) {NULL, NULL, NULL} }; @@ -895,6 +906,7 @@ BG(next) = NULL; BG(left) = -1; BG(user_tick_functions) = NULL; + BG(aggregation_table) = NULL; zend_hash_init(&BG(sm_protected_env_vars), 5, NULL, NULL, 1); BG(sm_allowed_env_vars) = NULL; @@ -1100,6 +1112,13 @@ efree(BG(user_tick_functions)); BG(user_tick_functions) = NULL; } + + if (BG(aggregation_table)) { + zend_hash_destroy(BG(aggregation_table)); + efree(BG(aggregation_table)); + BG(aggregation_table) = NULL; + } + #ifdef HAVE_MMAP if (BG(mmap_file)) { munmap(BG(mmap_file), BG(mmap_len)); @@ -2243,7 +2262,7 @@ BG(user_tick_functions) = (zend_llist *) emalloc(sizeof(zend_llist)); zend_llist_init(BG(user_tick_functions), sizeof(user_tick_function_entry), - (void (*)(void *)) user_tick_function_dtor, 0); + (llist_dtor_func_t) user_tick_function_dtor, 0); php_add_tick_function(run_user_tick_functions); } Index: php4/ext/standard/basic_functions.h diff -u php4/ext/standard/basic_functions.h:1.102 php4/ext/standard/basic_functions.h:1.103 --- php4/ext/standard/basic_functions.h:1.102 Wed Jan 9 18:47:41 2002 +++ php4/ext/standard/basic_functions.h Wed Feb 20 22:32:42 2002 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: basic_functions.h,v 1.102 2002/01/09 23:47:41 jimw Exp $ */ +/* $Id: basic_functions.h,v 1.103 2002/02/21 03:32:42 andrei Exp $ */ #ifndef BASIC_FUNCTIONS_H #define BASIC_FUNCTIONS_H @@ -180,6 +180,8 @@ void *mmap_file; size_t mmap_len; #endif + + HashTable *aggregation_table; } php_basic_globals; #ifdef ZTS Index: php4/ext/standard/php_standard.h diff -u php4/ext/standard/php_standard.h:1.13 php4/ext/standard/php_standard.h:1.14 --- php4/ext/standard/php_standard.h:1.13 Wed Jan 9 18:47:41 2002 +++ php4/ext/standard/php_standard.h Wed Feb 20 22:32:42 2002 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_standard.h,v 1.13 2002/01/09 23:47:41 jimw Exp $ */ +/* $Id: php_standard.h,v 1.14 2002/02/21 03:32:42 andrei Exp $ */ #include "basic_functions.h" #include "php_math.h" @@ -58,6 +58,7 @@ #include "php_versioning.h" #include "php_ftok.h" #include "php_type.h" +#include "aggregation.h" #define phpext_standard_ptr basic_functions_module_ptr Index: php4/ext/standard/aggregation.c +++ php4/ext/standard/aggregation.c /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2002 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ /* $Id: aggregation.c,v 1.1 2002/02/21 03:32:41 andrei Exp $ */ #include "php.h" #include "basic_functions.h" #include "aggregation.h" #if HAVE_PCRE || HAVE_BUNDLED_PCRE #include "ext/pcre/php_pcre.h" #endif static void aggregation_info_dtor(aggregation_info *info) { destroy_zend_class(info->new_ce); efree(info->new_ce); zval_ptr_dtor(&info->aggr_members); } /* {{{ static zval* array_to_hash */ static zval *array_to_hash(zval *array) { zval *hash, **entry; char *name_lc; /* * Well, this just transposes the array, popularly known as flipping it, or * giving it the finger. */ MAKE_STD_ZVAL(hash); array_init(hash); for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(array)); zend_hash_get_current_data(Z_ARRVAL_P(array), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(array))) { if (Z_TYPE_PP(entry) == IS_STRING) { /* * I hate case-insensitivity. Die, die, die. */ name_lc = estrndup(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry)); zend_str_tolower(name_lc, Z_STRLEN_PP(entry)); add_assoc_bool_ex(hash, name_lc, Z_STRLEN_PP(entry)+1, 1); efree(name_lc); } } return hash; } /* }}} */ /* {{{ static void aggregate_methods() */ static void aggregate_methods(zend_class_entry *ce, zend_class_entry *from_ce, int aggr_type, zval *aggr_filter, zend_bool exclude, zval *aggr_methods TSRMLS_DC) { HashPosition pos; zend_function *function; char *func_name; uint func_name_len; ulong num_key; zval *list_hash = NULL; pcre *re = NULL; pcre_extra *re_extra = NULL; int re_options = 0; /* * Flip the array for easy lookup, or compile the regexp. */ if (aggr_type == AGGREGATE_BY_LIST) { list_hash = array_to_hash(aggr_filter); } else if (aggr_type == AGGREGATE_BY_REGEXP) { if ((re = pcre_get_compiled_regex(Z_STRVAL_P(aggr_filter), &re_extra, &re_options)) == NULL) { return; } } /* * "Just because it's not nice doesn't mean it's not miraculous." * -- _Interesting Times_, Terry Pratchett */ /* * Aggregating by list without exclusion can be done more efficiently if we * iterate through the list and check against function table instead of the * other way around. */ if (aggr_type != AGGREGATE_BY_LIST || exclude) { zend_hash_internal_pointer_reset_ex(&from_ce->function_table, &pos); while (zend_hash_get_current_data_ex(&from_ce->function_table, (void**)&function, &pos) == SUCCESS) { zend_hash_get_current_key_ex(&from_ce->function_table, &func_name, &func_name_len, &num_key, 0, &pos); /* We do not aggregate: * 1. constructors */ if (!strncmp(func_name, from_ce->name, MIN(func_name_len-1, from_ce->name_length)) || /* 2. private methods (heh, like we really have them) */ func_name[0] == '_' || /* 3. explicitly excluded methods */ (aggr_type == AGGREGATE_BY_LIST && zend_hash_exists(Z_ARRVAL_P(list_hash), func_name, func_name_len)) || /* 4. methods matching regexp as modified by the exclusion flag */ (aggr_type == AGGREGATE_BY_REGEXP && (pcre_exec(re, re_extra, func_name, func_name_len-1, 0, 0, NULL, 0) < 0) ^ exclude) == 1) { zend_hash_move_forward_ex(&from_ce->function_table, &pos); continue; } /* * This is where the magic happens. */ if (zend_hash_add(&ce->function_table, func_name, func_name_len, (void*)function, sizeof(zend_function), NULL) == SUCCESS) { add_next_index_stringl(aggr_methods, func_name, func_name_len-1, 1); } zend_hash_move_forward_ex(&from_ce->function_table, &pos); } } else { /* * This is just like above except the other way around. */ zend_hash_internal_pointer_reset(Z_ARRVAL_P(list_hash)); while (zend_hash_get_current_key_ex(Z_ARRVAL_P(list_hash), &func_name, &func_name_len, &num_key, 0, NULL) == HASH_KEY_IS_STRING) { if (!strncmp(func_name, from_ce->name, MIN(func_name_len-1, from_ce->name_length)) || func_name[0] == '_' || zend_hash_find(&from_ce->function_table, func_name, func_name_len, (void**)&function) == FAILURE) { zend_hash_move_forward(Z_ARRVAL_P(list_hash)); continue; } if (zend_hash_add(&ce->function_table, func_name, func_name_len, (void*)function, sizeof(zend_function), NULL) == SUCCESS) { add_next_index_stringl(aggr_methods, func_name, func_name_len-1, 1); } zend_hash_move_forward(Z_ARRVAL_P(list_hash)); } } if (list_hash) { zval_ptr_dtor(&list_hash); } } /* }}} */ /* {{{ static void aggregate_properties() */ static void aggregate_properties(zval *obj, zend_class_entry *from_ce, int aggr_type, zval *aggr_filter, zend_bool exclude, zval *aggr_props TSRMLS_DC) { HashPosition pos; zval **prop; char *prop_name; uint prop_name_len; ulong num_key; zval *list_hash = NULL; pcre *re = NULL; pcre_extra *re_extra = NULL; int re_options = 0; if (!from_ce->constants_updated) { zend_hash_apply_with_argument(&from_ce->default_properties, (apply_func_arg_t) zval_update_constant, (void *) 1 TSRMLS_CC); from_ce->constants_updated = 1; } /* * Flip the array for easy lookup, or compile the regexp. */ if (aggr_type == AGGREGATE_BY_LIST) { list_hash = array_to_hash(aggr_filter); } else if (aggr_type == AGGREGATE_BY_REGEXP) { if ((re = pcre_get_compiled_regex(Z_STRVAL_P(aggr_filter), &re_extra, &re_options)) == NULL) { return; } } /* * "Just because it's not nice doesn't mean it's not miraculous." * -- _Interesting Times_, Terry Pratchett */ /* * Aggregating by list without exclusion can be done more efficiently if we * iterate through the list and check against default properties table * instead of the other way around. */ if (aggr_type != AGGREGATE_BY_LIST || exclude) { zend_hash_internal_pointer_reset_ex(&from_ce->default_properties, &pos); while (zend_hash_get_current_data_ex(&from_ce->default_properties, (void**)&prop, &pos) == SUCCESS) { zend_hash_get_current_key_ex(&from_ce->default_properties, &prop_name, &prop_name_len, &num_key, 0, &pos); /* We do not aggregate: * 1. private properties (heh, like we really have them) */ if (prop_name[0] == '_' || /* 2. explicitly excluded properties */ (aggr_type == AGGREGATE_BY_LIST && zend_hash_exists(Z_ARRVAL_P(list_hash), prop_name, prop_name_len)) || /* 3. properties matching regexp as modified by the exclusion flag */ (aggr_type == AGGREGATE_BY_REGEXP && (pcre_exec(re, re_extra, prop_name, prop_name_len-1, 0, 0, NULL, 0) < 0) ^ exclude) == 1) { zend_hash_move_forward_ex(&from_ce->default_properties, &pos); continue; } /* * This is where the magic happens. */ if (zend_hash_add(Z_OBJPROP_P(obj), prop_name, prop_name_len, (void*)prop, sizeof(zval *), NULL) == SUCCESS) { zval_add_ref(prop); add_next_index_stringl(aggr_props, prop_name, prop_name_len-1, 1); } zend_hash_move_forward_ex(&from_ce->default_properties, &pos); } } else { /* * This is just like above except the other way around. */ zend_hash_internal_pointer_reset(Z_ARRVAL_P(list_hash)); while (zend_hash_get_current_key_ex(Z_ARRVAL_P(list_hash), &prop_name, &prop_name_len, &num_key, 0, NULL) == HASH_KEY_IS_STRING) { if (prop_name[0] == '_' || zend_hash_find(&from_ce->default_properties, prop_name, prop_name_len, (void**)&prop) == FAILURE) { zend_hash_move_forward(Z_ARRVAL_P(list_hash)); continue; } if (zend_hash_add(Z_OBJPROP_P(obj), prop_name, prop_name_len, (void*)prop, sizeof(zval *), NULL) == SUCCESS) { zval_add_ref(prop); add_next_index_stringl(aggr_props, prop_name, prop_name_len-1, 1); } zend_hash_move_forward(Z_ARRVAL_P(list_hash)); } } if (list_hash) { zval_ptr_dtor(&list_hash); } } /* }}} */ /* {{{ static void aggregate() */ static void aggregate(INTERNAL_FUNCTION_PARAMETERS, int aggr_what, int aggr_type) { /* Incoming parameters. */ zval *obj, *aggr_list = NULL; char *class_name, *class_name_lc, *aggr_regexp; int class_name_len, aggr_regexp_len; zend_bool exclude = 0; /* Other variables. */ zval **aggr_members, z_aggr_regexp; zend_class_entry *ce, *new_ce; zend_function tmp_zend_function; aggregation_info aggr_info_new, *aggr_info = &aggr_info_new; zval *aggr_methods_new, **aggr_methods = &aggr_methods_new; zval *aggr_props_new, **aggr_props = &aggr_props_new; zval *aggr_filter = NULL; int zpp_result = FAILURE; /* * Ah, the beauty of the new parameter parsing API. Almost as good as Heidi Klum. */ switch (aggr_type) { case AGGREGATE_ALL: zpp_result = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "os", &obj, &class_name, &class_name_len); break; case AGGREGATE_BY_LIST: zpp_result = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "osa|b", &obj, &class_name, &class_name_len, &aggr_list, &exclude); break; case AGGREGATE_BY_REGEXP: zpp_result = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "oss|b", &obj, &class_name, &class_name_len, &aggr_regexp, &aggr_regexp_len, &exclude); ZVAL_STRINGL(&z_aggr_regexp, aggr_regexp, aggr_regexp_len, 0); break; } if (zpp_result == FAILURE) { return; } /* * Case-insensitivity is like that last freaking mutant from horror movies: * irradiated, blown in half, its eyes melting in their sockets, yet still * dragging itself closer and closer to you until it's pulverized into * microscopic pieces via some last-minute contrivance. And even then you * are not sure that it's finally dead. But that's just how I feel. */ class_name_lc = estrndup(class_name, class_name_len); zend_str_tolower(class_name_lc, class_name_len); if (zend_hash_find(EG(class_table), class_name_lc, class_name_len+1, (void **)&ce) == FAILURE) { php_error(E_WARNING, "%s() expects parameter 2 to be a valid class name, '%s' given\n", get_active_function_name(TSRMLS_C), class_name); efree(class_name_lc); return; } /* * And God said, Let there be light; and there was light. But only once. */ if (!BG(aggregation_table)) { BG(aggregation_table) = (HashTable *) emalloc(sizeof(HashTable)); zend_hash_init(BG(aggregation_table), 5, NULL, (dtor_func_t) aggregation_info_dtor, 0); } /* * Digging deep in the rabbit hole with a long object.. and coming up * more empty than the imagination of whoever made "Battlefield Earth". */ if (zend_hash_index_find(BG(aggregation_table), (long)obj, (void**)&aggr_info) == FAILURE) { zval *tmp; /* * You are not expected to understand this. */ new_ce = emalloc(sizeof(zend_class_entry)); new_ce->type = ZEND_USER_CLASS; new_ce->name = estrndup(Z_OBJCE_P(obj)->name, Z_OBJCE_P(obj)->name_length); new_ce->name_length = Z_OBJCE_P(obj)->name_length; new_ce->parent = Z_OBJCE_P(obj)->parent; new_ce->refcount = (int *) emalloc(sizeof(int)); *new_ce->refcount = 1; new_ce->constants_updated = Z_OBJCE_P(obj)->constants_updated; zend_hash_init(&new_ce->function_table, 10, NULL, ZEND_FUNCTION_DTOR, 0); zend_hash_init(&new_ce->default_properties, 10, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(&new_ce->function_table, &Z_OBJCE_P(obj)->function_table, (copy_ctor_func_t) function_add_ref, &tmp_zend_function, sizeof(zend_function)); zend_hash_copy(&new_ce->default_properties, &Z_OBJCE_P(obj)->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); new_ce->builtin_functions = Z_OBJCE_P(obj)->builtin_functions; new_ce->handle_function_call = Z_OBJCE_P(obj)->handle_function_call; new_ce->handle_property_get = Z_OBJCE_P(obj)->handle_property_get; new_ce->handle_property_set = Z_OBJCE_P(obj)->handle_property_set; /* * Okay, that was kind of exhausting. Let's invoke programmer virtue #1 * and stuff this where it belongs so we don't have to work so hard next * time. */ Z_OBJCE_P(obj) = new_ce; aggr_info_new.new_ce = new_ce; MAKE_STD_ZVAL(aggr_info_new.aggr_members); array_init(aggr_info_new.aggr_members); zend_hash_index_update(BG(aggregation_table), (long)obj, (void *)&aggr_info_new, sizeof(aggregation_info), NULL); } else { /* * Seek and ye shall find. */ new_ce = aggr_info->new_ce; } /* * This should be easy to understand. If not, ask Rasmus about it at his * next tutorial. */ if (zend_hash_find(Z_ARRVAL_P(aggr_info->aggr_members), class_name_lc, class_name_len+1, (void **)&aggr_members) == FAILURE) { zval *tmp; MAKE_STD_ZVAL(tmp); array_init(tmp); MAKE_STD_ZVAL(aggr_methods_new); array_init(aggr_methods_new); MAKE_STD_ZVAL(aggr_props_new); array_init(aggr_props_new); add_assoc_zval_ex(tmp, "methods", sizeof("methods"), aggr_methods_new); add_assoc_zval_ex(tmp, "properties", sizeof("properties"), aggr_props_new); zend_hash_add(Z_ARRVAL_P(aggr_info->aggr_members), class_name_lc, class_name_len+1, &tmp, sizeof(zval *), NULL); } else { zend_hash_find(Z_ARRVAL_PP(aggr_members), "methods", sizeof("methods"), (void**)&aggr_methods); zend_hash_find(Z_ARRVAL_PP(aggr_members), "properties", sizeof("properties"), (void**)&aggr_props); } if (aggr_type == AGGREGATE_BY_LIST) { aggr_filter = aggr_list; } else if (aggr_type == AGGREGATE_BY_REGEXP) { aggr_filter = &z_aggr_regexp; } if (aggr_what == AGGREGATE_METHODS || aggr_what == AGGREGATE_ALL) { aggregate_methods(new_ce, ce, aggr_type, aggr_filter, exclude, *aggr_methods TSRMLS_CC); } if (aggr_what == AGGREGATE_PROPERTIES || aggr_what == AGGREGATE_ALL) { aggregate_properties(obj, ce, aggr_type, aggr_filter, exclude, *aggr_props TSRMLS_CC); } /* * Yes, we have to clean up after monsters. Tsk-tsk. */ efree(class_name_lc); } /* }}} */ /* {{{ proto void aggregate(object obj, string class) */ PHP_FUNCTION(aggregate) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_ALL, AGGREGATE_ALL); } /* }}} */ /* {{{ proto void aggregate_methods(object obj, string class) */ PHP_FUNCTION(aggregate_methods) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_METHODS, AGGREGATE_ALL); } /* }}} */ /* {{{ proto void aggregate_methods_by_list(object obj, string class, array method_list [, bool exclude]) */ PHP_FUNCTION(aggregate_methods_by_list) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_METHODS, AGGREGATE_BY_LIST); } /* }}} */ /* {{{ proto void aggregate_methods_by_regexp(object obj, string class, string regexp [, bool exclude]) */ PHP_FUNCTION(aggregate_methods_by_regexp) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_METHODS, AGGREGATE_BY_REGEXP); } /* }}} */ /* {{{ proto void aggregate_properties(object obj, string class) */ PHP_FUNCTION(aggregate_properties) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_PROPERTIES, AGGREGATE_ALL); } /* }}} */ /* {{{ proto void aggregate_properites_by_list(object obj, string class, array props_list [, bool exclude]) */ PHP_FUNCTION(aggregate_properties_by_list) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_PROPERTIES, AGGREGATE_BY_LIST); } /* }}} */ /* {{{ proto void aggregate_properties_by_regexp(object obj, string class, string regexp [, bool exclude]) */ PHP_FUNCTION(aggregate_properties_by_regexp) { aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_PROPERTIES, AGGREGATE_BY_REGEXP); } /* }}} */ /* {{{ proto array aggregation_info(object obj) */ PHP_FUNCTION(aggregation_info) { zval *obj; aggregation_info *aggr_info; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) { return; } if (!BG(aggregation_table) || zend_hash_index_find(BG(aggregation_table), (long)obj, (void**)&aggr_info) == FAILURE) { RETURN_FALSE; } *return_value = *aggr_info->aggr_members; zval_copy_ctor(return_value); } /* }}} */ /* {{{ proto void deaggregate(object obj [, string class]) */ PHP_FUNCTION(deaggregate) { zval *obj; char *class_name = NULL, *class_name_lc; int class_name_len; aggregation_info *aggr_info; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|s", &obj, &class_name, &class_name_len) == FAILURE) { return; } if (!BG(aggregation_table) || zend_hash_index_find(BG(aggregation_table), (long)obj, (void**)&aggr_info) == FAILURE) { return; } if (class_name) { zval **aggr_members, **aggr_methods, **aggr_props, **method, **prop; class_name_lc = estrndup(class_name, class_name_len); zend_str_tolower(class_name_lc, class_name_len); if (zend_hash_find(Z_ARRVAL_P(aggr_info->aggr_members), class_name_lc, class_name_len+1, (void **)&aggr_members) == FAILURE) { efree(class_name_lc); return; } zend_hash_find(Z_ARRVAL_PP(aggr_members), "methods", sizeof("methods"), (void**)&aggr_methods); for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(aggr_methods)); zend_hash_get_current_data(Z_ARRVAL_PP(aggr_methods), (void**)&method) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_PP(aggr_methods))) { zend_hash_del(&Z_OBJCE_P(obj)->function_table, Z_STRVAL_PP(method), Z_STRLEN_PP(method)+1); } zend_hash_find(Z_ARRVAL_PP(aggr_members), "properties", sizeof("properties"), (void**)&aggr_props); for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(aggr_props)); zend_hash_get_current_data(Z_ARRVAL_PP(aggr_props), (void**)&prop) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_PP(aggr_props))) { zend_hash_del(Z_OBJPROP_P(obj), Z_STRVAL_PP(prop), Z_STRLEN_PP(prop)+1); } zend_hash_del(Z_ARRVAL_P(aggr_info->aggr_members), class_name_lc, class_name_len+1); efree(class_name_lc); } else { zend_class_entry *orig_ce; zval **aggr_members; zval **aggr_props, **prop; if (zend_hash_find(EG(class_table), Z_OBJCE_P(obj)->name, Z_OBJCE_P(obj)->name_length+1, (void **)&orig_ce) == FAILURE) { php_error(E_WARNING, "internal deaggregation error"); return; } for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(aggr_info->aggr_members)); zend_hash_get_current_data(Z_ARRVAL_P(aggr_info->aggr_members), (void **)&aggr_members) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(aggr_info->aggr_members))) { zend_hash_find(Z_ARRVAL_PP(aggr_members), "properties", sizeof("properties"), (void**)&aggr_props); for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(aggr_props)); zend_hash_get_current_data(Z_ARRVAL_PP(aggr_props), (void**)&prop) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_PP(aggr_props))) { zend_hash_del(Z_OBJPROP_P(obj), Z_STRVAL_PP(prop), Z_STRLEN_PP(prop)+1); } } Z_OBJCE_P(obj) = orig_ce; zend_hash_index_del(BG(aggregation_table), (long)obj); } } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ Index: php4/ext/standard/aggregation.h +++ php4/ext/standard/aggregation.h /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2002 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andrei Zmievski <andrei@php.net> | +----------------------------------------------------------------------+ */ /* $Id: aggregation.h,v 1.1 2002/02/21 03:32:41 andrei Exp $ */ #ifndef AGGREGATION_H #define AGGREGATION_H #define AGGREGATE_ALL 0 #define AGGREGATE_METHODS 1 #define AGGREGATE_PROPERTIES 2 #define AGGREGATE_BY_LIST 1 #define AGGREGATE_BY_REGEXP 2 /* * Data structure that is stored in aggregation_table hashtable for each object. */ typedef struct { zend_class_entry *new_ce; zval *aggr_members; } aggregation_info; PHP_FUNCTION(aggregate); PHP_FUNCTION(aggregate_methods); PHP_FUNCTION(aggregate_methods_by_list); PHP_FUNCTION(aggregate_methods_by_regexp); PHP_FUNCTION(aggregate_properties); PHP_FUNCTION(aggregate_properties_by_list); PHP_FUNCTION(aggregate_properties_by_regexp); PHP_FUNCTION(aggregate); PHP_FUNCTION(deaggregate); PHP_FUNCTION(aggregation_info); #endif /* AGGREGATION_H */ Index: php4/ext/standard/tests/aggregation/.cvsignore +++ php4/ext/standard/tests/aggregation/.cvsignore *.exp *.out *.php phpt.* Index: php4/ext/standard/tests/aggregation/aggregate.lib.php +++ php4/ext/standard/tests/aggregation/aggregate.lib.php <?php class simple { var $simple_prop = 100; function simple() { print "I'm alive!\n"; } } class helper { var $my_prop = 5; var $your_prop = array('init' => PHP_VERSION); var $our_prop = '****'; var $_priv_prop = null; function helper() { print "just trying to help\n"; } function do_this() { print "I'm helping!\n"; } function do_that() { print "I'm aggregating!\n"; } function just_another_method() { print "yep, that's me\n"; } function _private() { print "Don't touch me!\n"; } function __wakeup() { } } class mixin { var $simple_prop = true; var $mix = true; function mix_it() { print "mixing\n"; } } class moby { function mix_it() { print "I'm redundant!\n"; } } ?> Index: php4/ext/standard/tests/aggregation/aggregate.phpt +++ php4/ext/standard/tests/aggregation/aggregate.phpt --TEST-- aggregating everything --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate($obj, 'helper'); $obj->do_this(); $obj->do_that(); print $obj->our_prop; ?> --EXPECT-- I'm alive! I'm helping! I'm aggregating! **** Index: php4/ext/standard/tests/aggregation/aggregate_methods.phpt +++ php4/ext/standard/tests/aggregation/aggregate_methods.phpt --TEST-- aggregating all methods --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_methods($obj, 'mixin'); $obj->mix_it(); print $obj->simple_prop."\n"; print implode(',', get_class_methods($obj))."\n"; print implode(',', array_keys(get_object_vars($obj)))."\n"; aggregate_methods($obj, 'moby'); $obj->mix_it(); ?> --EXPECT-- I'm alive! mixing 100 simple,mix_it simple_prop mixing Index: php4/ext/standard/tests/aggregation/aggregate_methods_by_list.phpt +++ php4/ext/standard/tests/aggregation/aggregate_methods_by_list.phpt --TEST-- aggregating methods specified in the list --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_methods_by_list($obj, 'helper', array('just_another_method')); print implode(',', get_class_methods($obj))."\n"; $obj2 = new simple(); aggregate_methods_by_list($obj2, 'helper', array('just_another_method'), true); print implode(',', get_class_methods($obj2))."\n"; $obj->just_another_method(); ?> --EXPECT-- I'm alive! simple,just_another_method I'm alive! simple,do_this,do_that yep, that's me Index: php4/ext/standard/tests/aggregation/aggregate_methods_by_regexp.phpt +++ php4/ext/standard/tests/aggregation/aggregate_methods_by_regexp.phpt --TEST-- aggregating methods matching regular expression --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_methods_by_regexp($obj, 'helper', '/^do/'); print implode(',', get_class_methods($obj))."\n"; $obj2 = new simple(); aggregate_methods_by_regexp($obj2, 'helper', '/^do/', true); print implode(',', get_class_methods($obj2))."\n"; ?> --EXPECT-- I'm alive! simple,do_this,do_that I'm alive! simple,just_another_method Index: php4/ext/standard/tests/aggregation/aggregate_properties.phpt +++ php4/ext/standard/tests/aggregation/aggregate_properties.phpt --TEST-- aggregating all default properties --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_properties($obj, 'mixin'); print implode(',', array_keys(get_object_vars($obj)))."\n"; print $obj->simple_prop."\n"; print implode(',', get_class_methods($obj))."\n"; ?> --EXPECT-- I'm alive! simple_prop,mix 100 simple Index: php4/ext/standard/tests/aggregation/aggregate_properties_by_list.phpt +++ php4/ext/standard/tests/aggregation/aggregate_properties_by_list.phpt --TEST-- aggregating default properties specified in the list --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_properties_by_list($obj, 'helper', array('my_prop', 'our_prop')); print implode(',', array_keys(get_object_vars($obj)))."\n"; $obj2 = new simple(); aggregate_properties_by_list($obj2, 'helper', array('my_prop'), true); print implode(',', array_keys(get_object_vars($obj2)))."\n"; ?> --EXPECT-- I'm alive! simple_prop,my_prop,our_prop I'm alive! simple_prop,your_prop,our_prop Index: php4/ext/standard/tests/aggregation/aggregate_properties_by_regexp.phpt +++ php4/ext/standard/tests/aggregation/aggregate_properties_by_regexp.phpt --TEST-- aggregating default properties matching regular expression --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate_properties_by_regexp($obj, 'helper', '/^my/'); print implode(',', array_keys(get_object_vars($obj)))."\n"; $obj2 = new simple(); aggregate_properties_by_regexp($obj2, 'helper', '/^my/', true); print implode(',', array_keys(get_object_vars($obj2)))."\n"; ?> --EXPECT-- I'm alive! simple_prop,my_prop I'm alive! simple_prop,your_prop,our_prop Index: php4/ext/standard/tests/aggregation/aggregation_info.phpt +++ php4/ext/standard/tests/aggregation/aggregation_info.phpt --TEST-- retrieving aggregation info --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate($obj, 'mixin'); print_r(aggregation_info($obj)); ?> --EXPECT-- I'm alive! Array ( [mixin] => Array ( [methods] => Array ( [0] => mix_it ) [properties] => Array ( [0] => mix ) ) ) Index: php4/ext/standard/tests/aggregation/deaggregate.phpt +++ php4/ext/standard/tests/aggregation/deaggregate.phpt --TEST-- deaggreating --POST-- --GET-- --FILE-- <?php include "./aggregate.lib.php"; $obj = new simple(); aggregate($obj, 'helper'); aggregate($obj, 'mixin'); print_r(aggregation_info($obj)); deaggregate($obj, 'helper'); print_r(aggregation_info($obj)); deaggregate($obj); var_dump(aggregation_info($obj)); ?> --EXPECT-- I'm alive! Array ( [helper] => Array ( [methods] => Array ( [0] => do_this [1] => do_that [2] => just_another_method ) [properties] => Array ( [0] => my_prop [1] => your_prop [2] => our_prop ) ) [mixin] => Array ( [methods] => Array ( [0] => mix_it ) [properties] => Array ( [0] => mix ) ) ) Array ( [mixin] => Array ( [methods] => Array ( [0] => mix_it ) [properties] => Array ( [0] => mix ) ) ) bool(false)