diff options
author | SVN Migration <svn@php.net> | 2003-06-29 18:26:24 +0000 |
---|---|---|
committer | SVN Migration <svn@php.net> | 2003-06-29 18:26:24 +0000 |
commit | 54715b27657f09025ca2d59b6caae0901fcaed3c (patch) | |
tree | a30b6db1ed69a8f48fd70712cff673d2fca9b0a4 /ext/standard | |
parent | 082a1489d20686763496ae09b128c2cdfa9e5637 (diff) | |
download | php-git-php-5.0.0b1.tar.gz |
This commit was manufactured by cvs2svn to create tag 'php_5_0_0b1'.php-5.0.0b1
Diffstat (limited to 'ext/standard')
-rw-r--r-- | ext/standard/aggregation.c | 658 | ||||
-rw-r--r-- | ext/standard/aggregation.h | 51 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregate.lib | 65 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregate.phpt | 19 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregate_methods.phpt | 25 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregate_methods_by_list.phpt | 22 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregate_methods_by_regexp.phpt | 20 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregate_properties.phpt | 19 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregate_properties_by_list.phpt | 20 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregate_properties_by_regexp.phpt | 22 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/aggregation_info.phpt | 31 | ||||
-rw-r--r-- | ext/standard/tests/aggregation/deaggregate.phpt | 72 |
12 files changed, 1024 insertions, 0 deletions
diff --git a/ext/standard/aggregation.c b/ext/standard/aggregation.c new file mode 100644 index 0000000000..8b94334af7 --- /dev/null +++ b/ext/standard/aggregation.c @@ -0,0 +1,658 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Author: Andrei Zmievski <andrei@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "basic_functions.h" +#include "aggregation.h" +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) +#include "ext/pcre/php_pcre.h" +#endif + +static void aggregation_info_dtor(aggregation_info *info) +{ + /* FIXME: This is here to make it compile with Engine 2 but part of this module will need rewriting */ + +#ifndef ZEND_ENGINE_2 + destroy_zend_class(info->new_ce); + efree(info->new_ce); +#else + /* FIXME: In ZE2, there seems to be an issue with refcounts or something between + * this class entry and the original; there are problems when destroying the + * function table. + * Skipping deleting here will prevent a segfault but will leak + * the class name, the static_members hash and the ce itself. + * */ + + /* destroy_zend_class(&info->new_ce); */ +#endif + 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; +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) + pcre *re = NULL; + pcre_extra *re_extra = NULL; + int re_options = 0; +#endif + + /* + * Flip the array for easy lookup, or compile the regexp. + */ + if (aggr_type == AGGREGATE_BY_LIST) { + list_hash = array_to_hash(aggr_filter); + } +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) + else if (aggr_type == AGGREGATE_BY_REGEXP) { + if ((re = pcre_get_compiled_regex(Z_STRVAL_P(aggr_filter), &re_extra, &re_options TSRMLS_CC)) == NULL) { + return; + } + } +#endif + + /* + * "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)) +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) + || + /* 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 +#endif + ) { + 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; +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) + pcre *re = NULL; + pcre_extra *re_extra = NULL; + int re_options = 0; +#endif + + 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); + } +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) + else if (aggr_type == AGGREGATE_BY_REGEXP) { + if ((re = pcre_get_compiled_regex(Z_STRVAL_P(aggr_filter), &re_extra, &re_options TSRMLS_CC)) == NULL) { + return; + } + } +#endif + + /* + * "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)) +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) + || + /* 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 +#endif + ) { + 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_docref(NULL TSRMLS_CC, E_WARNING, "Expects the second parameter to be a valid class name, '%s' given", class_name); + efree(class_name_lc); + return; + } +#ifdef ZEND_ENGINE_2 + ce = *(zend_class_entry**)ce; +#endif + + /* + * 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; +#ifdef ZEND_ENGINE_2 + new_ce->refcount = 1; +#else + new_ce->refcount = (int *) emalloc(sizeof(int)); + *new_ce->refcount = 1; +#endif + 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 *)); + +#ifdef ZEND_ENGINE_2 + ALLOC_HASHTABLE(new_ce->static_members); + zend_hash_init(new_ce->static_members, 10, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(new_ce->static_members, Z_OBJCE_P(obj)->static_members, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + zend_hash_init(&new_ce->constants_table, 10, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(&new_ce->constants_table, &Z_OBJCE_P(obj)->constants_table, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + zend_hash_init(&new_ce->class_table, 10, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(&new_ce->class_table, &Z_OBJCE_P(obj)->class_table, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + + /* + zend_hash_init(&new_ce->private_properties, 10, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(&new_ce->private_properties, &Z_OBJCE_P(obj)->private_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + */ + + new_ce->constructor = Z_OBJCE_P(obj)->constructor; + new_ce->destructor = Z_OBJCE_P(obj)->destructor; + new_ce->clone = Z_OBJCE_P(obj)->clone; +#endif + + new_ce->builtin_functions = Z_OBJCE_P(obj)->builtin_functions; +#ifndef ZEND_ENGINE_2 + 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; +#else + new_ce->__call = Z_OBJCE_P(obj)->__call; + new_ce->__get = Z_OBJCE_P(obj)->__get; + new_ce->__set = Z_OBJCE_P(obj)->__set; +#endif + /* + * 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. + */ + /* OBJECT FIXME!! won't work with non-standard objects */ +#ifndef ZEND_ENGINE_2 + (Z_OBJ_P(obj))->ce = new_ce; +#endif + 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_properties(object obj, string class) + */ +PHP_FUNCTION(aggregate_properties) +{ + aggregate(INTERNAL_FUNCTION_PARAM_PASSTHRU, AGGREGATE_PROPERTIES, AGGREGATE_ALL); +} +/* }}} */ + + +/* {{{ proto void aggregate_properties_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); +} +/* }}} */ + +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) +/* {{{ 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_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); +} +/* }}} */ +#endif + + +/* {{{ proto array aggregate_info(object obj) + */ +PHP_FUNCTION(aggregate_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_docref(NULL TSRMLS_CC, 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); + } + } + + /* OBJECT FIXME!! won't work with non-standard objects */ +#ifndef ZEND_ENGINE_2 + (Z_OBJ_P(obj))->ce = orig_ce; +#endif + 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 + */ diff --git a/ext/standard/aggregation.h b/ext/standard/aggregation.h new file mode 100644 index 0000000000..ab8c4591d6 --- /dev/null +++ b/ext/standard/aggregation.h @@ -0,0 +1,51 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.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. | + +----------------------------------------------------------------------+ + | Author: Andrei Zmievski <andrei@php.net> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#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(aggregate_info); + +#endif /* AGGREGATION_H */ diff --git a/ext/standard/tests/aggregation/aggregate.lib b/ext/standard/tests/aggregation/aggregate.lib new file mode 100644 index 0000000000..3799285f75 --- /dev/null +++ b/ext/standard/tests/aggregation/aggregate.lib @@ -0,0 +1,65 @@ +<?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"; + } +} + +?> diff --git a/ext/standard/tests/aggregation/aggregate.phpt b/ext/standard/tests/aggregation/aggregate.phpt new file mode 100644 index 0000000000..46aa9133b6 --- /dev/null +++ b/ext/standard/tests/aggregation/aggregate.phpt @@ -0,0 +1,19 @@ +--TEST-- +aggregating everything +--FILE-- +<?php + +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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! +**** diff --git a/ext/standard/tests/aggregation/aggregate_methods.phpt b/ext/standard/tests/aggregation/aggregate_methods.phpt new file mode 100644 index 0000000000..b612881c59 --- /dev/null +++ b/ext/standard/tests/aggregation/aggregate_methods.phpt @@ -0,0 +1,25 @@ +--TEST-- +aggregating all methods +--POST-- +--GET-- +--FILE-- +<?php +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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 diff --git a/ext/standard/tests/aggregation/aggregate_methods_by_list.phpt b/ext/standard/tests/aggregation/aggregate_methods_by_list.phpt new file mode 100644 index 0000000000..312a57d1b2 --- /dev/null +++ b/ext/standard/tests/aggregation/aggregate_methods_by_list.phpt @@ -0,0 +1,22 @@ +--TEST-- +aggregating methods specified in the list +--POST-- +--GET-- +--FILE-- +<?php +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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 diff --git a/ext/standard/tests/aggregation/aggregate_methods_by_regexp.phpt b/ext/standard/tests/aggregation/aggregate_methods_by_regexp.phpt new file mode 100644 index 0000000000..6525e50cff --- /dev/null +++ b/ext/standard/tests/aggregation/aggregate_methods_by_regexp.phpt @@ -0,0 +1,20 @@ +--TEST-- +aggregating methods matching regular expression +--POST-- +--GET-- +--FILE-- +<?php +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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 diff --git a/ext/standard/tests/aggregation/aggregate_properties.phpt b/ext/standard/tests/aggregation/aggregate_properties.phpt new file mode 100644 index 0000000000..2a976c71a7 --- /dev/null +++ b/ext/standard/tests/aggregation/aggregate_properties.phpt @@ -0,0 +1,19 @@ +--TEST-- +aggregating all default properties +--POST-- +--GET-- +--FILE-- +<?php +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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 diff --git a/ext/standard/tests/aggregation/aggregate_properties_by_list.phpt b/ext/standard/tests/aggregation/aggregate_properties_by_list.phpt new file mode 100644 index 0000000000..fa12d36bcb --- /dev/null +++ b/ext/standard/tests/aggregation/aggregate_properties_by_list.phpt @@ -0,0 +1,20 @@ +--TEST-- +aggregating default properties specified in the list +--POST-- +--GET-- +--FILE-- +<?php +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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 diff --git a/ext/standard/tests/aggregation/aggregate_properties_by_regexp.phpt b/ext/standard/tests/aggregation/aggregate_properties_by_regexp.phpt new file mode 100644 index 0000000000..9a74f5536d --- /dev/null +++ b/ext/standard/tests/aggregation/aggregate_properties_by_regexp.phpt @@ -0,0 +1,22 @@ +--TEST-- +aggregating default properties matching regular expression +--SKIPIF-- +<?php if (!function_exists('aggregate_properties_by_regexp')) print "skip"; ?> +--POST-- +--GET-- +--FILE-- +<?php +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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 diff --git a/ext/standard/tests/aggregation/aggregation_info.phpt b/ext/standard/tests/aggregation/aggregation_info.phpt new file mode 100644 index 0000000000..8dd943cbcc --- /dev/null +++ b/ext/standard/tests/aggregation/aggregation_info.phpt @@ -0,0 +1,31 @@ +--TEST-- +retrieving aggregation info +--POST-- +--GET-- +--FILE-- +<?php +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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 + ) + + ) + +) diff --git a/ext/standard/tests/aggregation/deaggregate.phpt b/ext/standard/tests/aggregation/deaggregate.phpt new file mode 100644 index 0000000000..5c551d75bd --- /dev/null +++ b/ext/standard/tests/aggregation/deaggregate.phpt @@ -0,0 +1,72 @@ +--TEST-- +deaggreating +--POST-- +--GET-- +--FILE-- +<?php +include "ext/standard/tests/aggregation/aggregate.lib"; + +$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) |