helly Mon Aug 11 12:43:22 2008 UTC Added files: (Branch: PHP_5_3) /php-src/ext/reflection/tests closures_001.phpt closures_002.phpt
Modified files: /php-src/ext/reflection php_reflection.c /php-src/ext/reflection/tests reflectionParameter_export_error2.phpt Log: - MFH Closure/Reflection integration (original idea by Christian Seiler)
http://cvs.php.net/viewvc.cgi/php-src/ext/reflection/php_reflection.c?r1=1.164.2.33.2.45.2.28&r2=1.164.2.33.2.45.2.29&diff_format=u Index: php-src/ext/reflection/php_reflection.c diff -u php-src/ext/reflection/php_reflection.c:1.164.2.33.2.45.2.28 php-src/ext/reflection/php_reflection.c:1.164.2.33.2.45.2.29 --- php-src/ext/reflection/php_reflection.c:1.164.2.33.2.45.2.28 Mon Aug 11 00:47:45 2008 +++ php-src/ext/reflection/php_reflection.c Mon Aug 11 12:43:21 2008 @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_reflection.c,v 1.164.2.33.2.45.2.28 2008/08/11 00:47:45 felipe Exp $ */ +/* $Id: php_reflection.c,v 1.164.2.33.2.45.2.29 2008/08/11 12:43:21 helly Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -192,11 +192,18 @@ zend_function *fptr; } parameter_reference; +typedef enum { + REF_TYPE_OTHER, /* Must be 0 */ + REF_TYPE_FUNCTION, + REF_TYPE_PARAMETER, + REF_TYPE_PROPERTY, +} reflection_type_t; + /* Struct for reflection objects */ typedef struct { zend_object zo; void *ptr; - unsigned int free_ptr:1; + reflection_type_t ref_type; zval *obj; zend_class_entry *ce; } reflection_object; @@ -224,33 +231,45 @@ class_entry->interfaces[num_interfaces - 1] = interface_entry; } -static void reflection_free_objects_storage(void *object TSRMLS_DC) +static void _free_function(zend_function *fptr TSRMLS_DC) /* {{{ */ { - reflection_object *intern = (reflection_object *) object; - - if (intern->free_ptr && intern->ptr) { - efree(intern->ptr); - intern->ptr = NULL; - } - if (intern->obj) { - zval_ptr_dtor(&intern->obj); + if (fptr + && fptr->type == ZEND_INTERNAL_FUNCTION + && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0 + ) { + efree(fptr->internal_function.function_name); + efree(fptr); } - zend_objects_free_object_storage(object TSRMLS_CC); } +/* }}} */ -static void reflection_objects_clone(void *object, void **object_clone TSRMLS_DC) +static void reflection_free_objects_storage(void *object TSRMLS_DC) { reflection_object *intern = (reflection_object *) object; - reflection_object **intern_clone = (reflection_object **) object_clone; + parameter_reference *reference; - *intern_clone = emalloc(sizeof(reflection_object)); - zend_object_std_init(&(*intern_clone)->zo, intern->zo.ce TSRMLS_CC); - (*intern_clone)->ptr = intern->ptr; - (*intern_clone)->free_ptr = intern->free_ptr; - (*intern_clone)->obj = intern->obj; + if (intern->ptr) { + switch (intern->ref_type) { + case REF_TYPE_PARAMETER: + reference = (parameter_reference*)intern->ptr; + _free_function(reference->fptr TSRMLS_CC); + efree(intern->ptr); + break; + case REF_TYPE_FUNCTION: + _free_function(intern->ptr TSRMLS_CC); + break; + case REF_TYPE_PROPERTY: + efree(intern->ptr); + break; + case REF_TYPE_OTHER: + break; + } + } + intern->ptr = NULL; if (intern->obj) { - zval_add_ref(&intern->obj); + zval_ptr_dtor(&intern->obj); } + zend_objects_free_object_storage(object TSRMLS_CC); } static zend_object_value reflection_objects_new(zend_class_entry *class_type TSRMLS_DC) @@ -259,16 +278,12 @@ zend_object_value retval; reflection_object *intern; - intern = emalloc(sizeof(reflection_object)); + intern = ecalloc(1, sizeof(reflection_object)); intern->zo.ce = class_type; - intern->zo.guards = NULL; - intern->ptr = NULL; - intern->obj = NULL; - intern->free_ptr = 0; zend_object_std_init(&intern->zo, class_type TSRMLS_CC); zend_hash_copy(intern->zo.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); - retval.handle = zend_objects_store_put(intern, NULL, reflection_free_objects_storage, reflection_objects_clone TSRMLS_CC); + retval.handle = zend_objects_store_put(intern, NULL, reflection_free_objects_storage, NULL TSRMLS_CC); retval.handlers = &reflection_object_handlers; return retval; } @@ -536,9 +551,20 @@ zend_hash_get_current_key_ex(&ce->function_table, &key, &key_len, &num_index, 0, &pos) != HASH_KEY_IS_STRING || zend_binary_strcasecmp(key, key_len-1, mptr->common.function_name, len) == 0) { + zend_function *closure; + /* see if this is a closure */ + if (ce == zend_ce_closure && obj && (len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && + memcmp(mptr->common.function_name, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0 && + (closure = zend_get_closure_invoke_method(obj TSRMLS_CC)) != NULL + ) { + mptr = closure; + } else { + closure = NULL; + } string_printf(&dyn, "\n"); _function_string(&dyn, mptr, ce, sub_indent.string TSRMLS_CC); count++; + _free_function(closure TSRMLS_CC); } } zend_hash_move_forward_ex(&ce->function_table, &pos); @@ -1039,7 +1065,7 @@ reflection_instantiate(reflection_class_ptr, object TSRMLS_CC); intern = (reflection_object *) zend_object_store_get_object(object TSRMLS_CC); intern->ptr = ce; - intern->free_ptr = 0; + intern->ref_type = REF_TYPE_OTHER; intern->ce = ce; zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); } @@ -1068,7 +1094,7 @@ MAKE_STD_ZVAL(name); ZVAL_STRINGL(name, module->name, name_len, 1); intern->ptr = module; - intern->free_ptr = 0; + intern->ref_type = REF_TYPE_OTHER; intern->ce = NULL; zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); } @@ -1095,7 +1121,7 @@ reference->required = required; reference->fptr = fptr; intern->ptr = reference; - intern->free_ptr = 1; + intern->ref_type = REF_TYPE_PARAMETER; intern->ce = fptr->common.scope; zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); } @@ -1113,7 +1139,7 @@ reflection_instantiate(reflection_function_ptr, object TSRMLS_CC); intern = (reflection_object *) zend_object_store_get_object(object TSRMLS_CC); intern->ptr = function; - intern->free_ptr = 0; + intern->ref_type = REF_TYPE_FUNCTION; intern->ce = NULL; zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); } @@ -1133,7 +1159,7 @@ reflection_instantiate(reflection_method_ptr, object TSRMLS_CC); intern = (reflection_object *) zend_object_store_get_object(object TSRMLS_CC); intern->ptr = method; - intern->free_ptr = 0; + intern->ref_type = REF_TYPE_FUNCTION; intern->ce = ce; zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); zend_hash_update(Z_OBJPROP_P(object), "class", sizeof("class"), (void **) &classname, sizeof(zval *), NULL); @@ -1180,7 +1206,7 @@ reference->prop = *prop; reference->ignore_visibility = 0; intern->ptr = reference; - intern->free_ptr = 1; + intern->ref_type = REF_TYPE_PROPERTY; intern->ce = ce; zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); zend_hash_update(Z_OBJPROP_P(object), "class", sizeof("class"), (void **) &classname, sizeof(zval *), NULL); @@ -1411,7 +1437,7 @@ ZVAL_STRING(name, fptr->common.function_name, 1); zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); intern->ptr = fptr; - intern->free_ptr = 0; + intern->ref_type = REF_TYPE_FUNCTION; intern->ce = NULL; } /* }}} */ @@ -1890,18 +1916,37 @@ convert_to_string_ex(method); lcname_len = Z_STRLEN_PP(method); lcname = zend_str_tolower_dup(Z_STRVAL_PP(method), lcname_len); - if (zend_hash_find(&ce->function_table, lcname, lcname_len + 1, (void **) &fptr) == FAILURE) { + if (ce == zend_ce_closure && Z_TYPE_PP(classref) == IS_OBJECT && + (lcname_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && + memcmp(lcname, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0 && + (fptr = zend_get_closure_invoke_method(*classref TSRMLS_CC)) != NULL + ) { + /* nothign to do */ + } else if (zend_hash_find(&ce->function_table, lcname, lcname_len + 1, (void **) &fptr) == FAILURE) { efree(lcname); zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, - "Method %s::%s() does not exist", Z_STRVAL_PP(classref), Z_TYPE_PP(method), Z_STRVAL_PP(method)); + "Method %s::%s() does not exist", ce->name, Z_STRVAL_PP(method)); return; } efree(lcname); } break; + + case IS_OBJECT: { + ce = Z_OBJCE_P(reference); + + if (instanceof_function(ce, zend_ce_closure TSRMLS_CC)) { + fptr = zend_get_closure_invoke_method(reference TSRMLS_CC); + } else if (zend_hash_find(&ce->function_table, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME), (void **)&fptr) == FAILURE) { + zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, + "Method %s::%s() does not exist", ce->name, ZEND_INVOKE_FUNC_NAME); + return; + } + } + break; default: - _DO_THROW("The parameter class is expected to be either a string or an array(class, method)"); + _DO_THROW("The parameter class is expected to be either a string, an array(class, method) or a callable object"); /* returns out of this function */ } @@ -1943,8 +1988,9 @@ ref->offset = (zend_uint)position; ref->required = fptr->common.required_num_args; ref->fptr = fptr; + // TODO: copy fptr intern->ptr = ref; - intern->free_ptr = 1; + intern->ref_type = REF_TYPE_PARAMETER; intern->ce = ce; } /* }}} */ @@ -2207,7 +2253,7 @@ ZEND_METHOD(reflection_method, __construct) { zval *name, *classname; - zval *object; + zval *object, *orig_obj; reflection_object *intern; char *lcname; zend_class_entry **pce; @@ -2217,7 +2263,11 @@ int name_len, tmp_len; zval ztmp; - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "zs", &classname, &name_str, &name_len) == FAILURE) { + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "o", &classname) == SUCCESS) { + name_str = ZEND_INVOKE_FUNC_NAME; + name_len = sizeof(ZEND_INVOKE_FUNC_NAME)-1; + orig_obj = classname; + } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "zs", &classname, &name_str, &name_len) == FAILURE) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len) == FAILURE) { return; } @@ -2230,6 +2280,11 @@ ZVAL_STRINGL(classname, name_str, tmp_len, 1); name_len = name_len - (tmp_len + 2); name_str = tmp + 2; + orig_obj = NULL; + } else if (Z_TYPE_P(classname) == IS_OBJECT) { + orig_obj = classname; + } else { + orig_obj = NULL; } object = getThis(); @@ -2275,7 +2330,12 @@ lcname = zend_str_tolower_dup(name_str, name_len); - if (zend_hash_find(&ce->function_table, lcname, name_len + 1, (void **) &mptr) == FAILURE) { + if (ce == zend_ce_closure && orig_obj && (name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && + memcmp(lcname, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0 && + (mptr = zend_get_closure_invoke_method(orig_obj TSRMLS_CC)) != NULL + ) { + /* do nothing, mptr already set */ + } else if (zend_hash_find(&ce->function_table, lcname, name_len + 1, (void **) &mptr) == FAILURE) { efree(lcname); zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, "Method %s::%s() does not exist", ce->name, name_str); @@ -2287,7 +2347,7 @@ ZVAL_STRING(name, mptr->common.function_name, 1); zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); intern->ptr = mptr; - intern->free_ptr = 0; + intern->ref_type = REF_TYPE_FUNCTION; intern->ce = ce; } /* }}} */ @@ -2792,7 +2852,7 @@ intern->ptr = *ce; } - intern->free_ptr = 0; + intern->ref_type = REF_TYPE_OTHER; } /* }}} */ @@ -3139,7 +3199,13 @@ GET_REFLECTION_OBJECT_PTR(ce); lc_name = zend_str_tolower_dup(name, name_len); - if (zend_hash_find(&ce->function_table, lc_name, name_len + 1, (void**) &mptr) == SUCCESS) { + if (ce == zend_ce_closure && intern->obj && (name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && + memcmp(lc_name, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0 && + (mptr = zend_get_closure_invoke_method(intern->obj TSRMLS_CC)) != NULL + ) { + reflection_method_factory(ce, mptr, return_value TSRMLS_CC); + efree(lc_name); + } else if (zend_hash_find(&ce->function_table, lc_name, name_len + 1, (void**) &mptr) == SUCCESS) { reflection_method_factory(ce, mptr, return_value TSRMLS_CC); efree(lc_name); } else { @@ -3152,19 +3218,36 @@ /* }}} */ /* {{{ _addmethod */ -static int _addmethod(zend_function *mptr TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +static void _addmethod(zend_function *mptr, zend_class_entry *ce, zval *retval, long filter, zval *obj TSRMLS_DC) { zval *method; - zend_class_entry *ce = *va_arg(args, zend_class_entry**); - zval *retval = va_arg(args, zval*); - long filter = va_arg(args, long); + uint len = strlen(mptr->common.function_name); + zend_function *closure; if (mptr->common.fn_flags & filter) { ALLOC_ZVAL(method); + if (ce == zend_ce_closure && obj && (len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && + memcmp(mptr->common.function_name, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0 && + (closure = zend_get_closure_invoke_method(obj TSRMLS_CC)) != NULL + ) { + mptr = closure; + } reflection_method_factory(ce, mptr, method TSRMLS_CC); add_next_index_zval(retval, method); } - return 0; +} +/* }}} */ + +/* {{{ _addmethod */ +static int _addmethod_va(zend_function *mptr TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +{ + zend_class_entry *ce = *va_arg(args, zend_class_entry**); + zval *retval = va_arg(args, zval*); + long filter = va_arg(args, long); + zval *obj = va_arg(args, zval *); + + _addmethod(mptr, ce, retval, filter, obj TSRMLS_CC); + return ZEND_HASH_APPLY_KEEP; } /* }}} */ @@ -3190,7 +3273,14 @@ GET_REFLECTION_OBJECT_PTR(ce); array_init(return_value); - zend_hash_apply_with_arguments(&ce->function_table TSRMLS_CC, (apply_func_args_t) _addmethod, 3, &ce, return_value, filter); + zend_hash_apply_with_arguments(&ce->function_table TSRMLS_CC, (apply_func_args_t) _addmethod_va, 4, &ce, return_value, filter, intern->obj); + if (intern->obj && instanceof_function(ce, zend_ce_closure)) { + zend_function *closure = zend_get_closure_invoke_method(intern->obj TSRMLS_CC); + if (closure) { + _addmethod(closure, ce, return_value, filter, intern->obj TSRMLS_CC); + _free_function(closure TSRMLS_CC); + } + } } /* }}} */ @@ -4027,7 +4117,7 @@ reference->prop = *property_info; reference->ignore_visibility = 0; intern->ptr = reference; - intern->free_ptr = 1; + intern->ref_type = REF_TYPE_PROPERTY; intern->ce = ce; } /* }}} */ @@ -4347,7 +4437,7 @@ ZVAL_STRING(name, module->name, 1); zend_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL); intern->ptr = module; - intern->free_ptr = 0; + intern->ref_type = REF_TYPE_OTHER; intern->ce = NULL; } /* }}} */ @@ -5117,7 +5207,7 @@ php_info_print_table_start(); php_info_print_table_header(2, "Reflection", "enabled"); - php_info_print_table_row(2, "Version", "$Id: php_reflection.c,v 1.164.2.33.2.45.2.28 2008/08/11 00:47:45 felipe Exp $"); + php_info_print_table_row(2, "Version", "$Id: php_reflection.c,v 1.164.2.33.2.45.2.29 2008/08/11 12:43:21 helly Exp $"); php_info_print_table_end(); } /* }}} */ http://cvs.php.net/viewvc.cgi/php-src/ext/reflection/tests/reflectionParameter_export_error2.phpt?r1=1.1.2.2&r2=1.1.2.3&diff_format=u Index: php-src/ext/reflection/tests/reflectionParameter_export_error2.phpt diff -u php-src/ext/reflection/tests/reflectionParameter_export_error2.phpt:1.1.2.2 php-src/ext/reflection/tests/reflectionParameter_export_error2.phpt:1.1.2.3 --- php-src/ext/reflection/tests/reflectionParameter_export_error2.phpt:1.1.2.2 Thu Jun 12 21:41:21 2008 +++ php-src/ext/reflection/tests/reflectionParameter_export_error2.phpt Mon Aug 11 12:43:21 2008 @@ -9,15 +9,23 @@ } $reflect = new ReflectionFunction('ReflectionParameterTest'); $params = $reflect->getParameters(); -foreach($params as $key => $value) { - ReflectionParameter::export($reflect, $key); +try { + foreach($params as $key => $value) { + ReflectionParameter::export($reflect, $key); + } +} +catch (ReflectionException $e) { + echo $e->getMessage() . "\n"; +} +try { + foreach($params as $key => $value) { + ReflectionParameter::export(42, $key); + } +} +catch (ReflectionException $e) { + echo $e->getMessage() . "\n"; } ?> --EXPECTF-- - -Fatal error: Uncaught exception 'ReflectionException' with message 'The parameter class is expected to be either a string or an array(class, method)' in %s.php:%d -Stack trace: -#0 [internal function]: ReflectionParameter->__construct(Object(ReflectionFunction), 0) -#1 %s.php(%d): ReflectionParameter::export(Object(ReflectionFunction), 0) -#2 {main} - thrown in %s.php on line %d +Method ReflectionFunction::__invoke() does not exist +The parameter class is expected to be either a string, an array(class, method) or a callable object http://cvs.php.net/viewvc.cgi/php-src/ext/reflection/tests/closures_001.phpt?view=markup&rev=1.1 Index: php-src/ext/reflection/tests/closures_001.phpt +++ php-src/ext/reflection/tests/closures_001.phpt --TEST-- Reflection on closures --FILE-- <?php $closure = function($a, $b = 0) { }; $ro = new ReflectionObject($closure); $rm = $ro->getMethod('__invoke'); var_dump($rm->getNumberOfParameters()); var_dump($rm->getNumberOfRequiredParameters()); $rms = $ro->getMethods(); foreach($rms as $rm) { if ($rm->getName() == '__invoke') { var_dump($rm->getNumberOfParameters()); var_dump($rm->getNumberOfRequiredParameters()); } } echo "---\n"; $rm = new ReflectionMethod($closure); var_dump($rm->getName()); var_dump($rm->getNumberOfParameters()); var_dump($rm->getNumberOfRequiredParameters()); echo "---\n"; $rp = new ReflectionParameter(array($closure, '__invoke'), 0); var_dump($rp->isOptional()); $rp = new ReflectionParameter(array($closure, '__invoke'), 1); var_dump($rp->isOptional()); $rp = new ReflectionParameter(array($closure, '__invoke'), 'a'); var_dump($rp->isOptional()); $rp = new ReflectionParameter(array($closure, '__invoke'), 'b'); var_dump($rp->isOptional()); echo "---\n"; $rp = new ReflectionParameter($closure, 0); var_dump($rp->isOptional()); $rp = new ReflectionParameter($closure, 1); var_dump($rp->isOptional()); $rp = new ReflectionParameter($closure, 'a'); var_dump($rp->isOptional()); $rp = new ReflectionParameter($closure, 'b'); var_dump($rp->isOptional()); ?> ===DONE=== --EXPECTF-- int(2) int(1) int(2) int(1) --- unicode(8) "__invoke" int(2) int(1) --- bool(false) bool(true) bool(false) bool(true) --- bool(false) bool(true) bool(false) bool(true) ===DONE=== http://cvs.php.net/viewvc.cgi/php-src/ext/reflection/tests/closures_002.phpt?view=markup&rev=1.1 Index: php-src/ext/reflection/tests/closures_002.phpt +++ php-src/ext/reflection/tests/closures_002.phpt --TEST-- Reflection on invokable objects --FILE-- <?php class Test { function __invoke($a, $b = 0) { } } $rm = new ReflectionMethod(new Test); var_dump($rm->getName()); var_dump($rm->getNumberOfParameters()); var_dump($rm->getNumberOfRequiredParameters()); $rp = new ReflectionParameter(new Test, 0); var_dump($rp->isOptional()); $rp = new ReflectionParameter(new Test, 1); var_dump($rp->isOptional()); ?> ===DONE=== --EXPECTF-- unicode(8) "__invoke" int(2) int(1) bool(false) bool(true) ===DONE===
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php