mirror of
https://github.com/php/php-src.git
synced 2025-01-27 06:03:45 +08:00
Adding object aggregation capability along with tests.
This commit is contained in:
parent
444d3a6150
commit
513e598d1e
1
NEWS
1
NEWS
@ -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)
|
||||
|
@ -9,7 +9,7 @@ LTLIBRARY_SOURCES=\
|
||||
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
|
||||
|
||||
|
586
ext/standard/aggregation.c
Normal file
586
ext/standard/aggregation.c
Normal file
@ -0,0 +1,586 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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$ */
|
||||
|
||||
#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
|
||||
*/
|
51
ext/standard/aggregation.h
Normal file
51
ext/standard/aggregation.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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$ */
|
||||
|
||||
#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 */
|
@ -812,6 +812,17 @@ function_entry basic_functions[] = {
|
||||
#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 @@ static void basic_globals_ctor(php_basic_globals *basic_globals_p TSRMLS_DC)
|
||||
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 @@ PHP_RSHUTDOWN_FUNCTION(basic)
|
||||
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 @@ PHP_FUNCTION(register_tick_function)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -180,6 +180,8 @@ typedef struct {
|
||||
void *mmap_file;
|
||||
size_t mmap_len;
|
||||
#endif
|
||||
|
||||
HashTable *aggregation_table;
|
||||
} php_basic_globals;
|
||||
|
||||
#ifdef ZTS
|
||||
|
@ -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
|
||||
|
||||
|
65
ext/standard/tests/aggregation/aggregate.lib.php
Normal file
65
ext/standard/tests/aggregation/aggregate.lib.php
Normal file
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
20
ext/standard/tests/aggregation/aggregate.phpt
Normal file
20
ext/standard/tests/aggregation/aggregate.phpt
Normal file
@ -0,0 +1,20 @@
|
||||
--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!
|
||||
****
|
25
ext/standard/tests/aggregation/aggregate_methods.phpt
Normal file
25
ext/standard/tests/aggregation/aggregate_methods.phpt
Normal file
@ -0,0 +1,25 @@
|
||||
--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
|
@ -0,0 +1,22 @@
|
||||
--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
|
@ -0,0 +1,20 @@
|
||||
--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
|
19
ext/standard/tests/aggregation/aggregate_properties.phpt
Normal file
19
ext/standard/tests/aggregation/aggregate_properties.phpt
Normal file
@ -0,0 +1,19 @@
|
||||
--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
|
@ -0,0 +1,20 @@
|
||||
--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
|
@ -0,0 +1,20 @@
|
||||
--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
|
31
ext/standard/tests/aggregation/aggregation_info.phpt
Normal file
31
ext/standard/tests/aggregation/aggregation_info.phpt
Normal file
@ -0,0 +1,31 @@
|
||||
--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
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
)
|
72
ext/standard/tests/aggregation/deaggregate.phpt
Normal file
72
ext/standard/tests/aggregation/deaggregate.phpt
Normal file
@ -0,0 +1,72 @@
|
||||
--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)
|
Loading…
Reference in New Issue
Block a user