]> granicus.if.org Git - php/commitdiff
- Closure/Reflection integration (original idea by Christian Seiler)
authorMarcus Boerger <helly@php.net>
Mon, 11 Aug 2008 12:36:01 +0000 (12:36 +0000)
committerMarcus Boerger <helly@php.net>
Mon, 11 Aug 2008 12:36:01 +0000 (12:36 +0000)
ext/reflection/php_reflection.c
ext/reflection/tests/closures_001.phpt [new file with mode: 0755]
ext/reflection/tests/closures_002.phpt [new file with mode: 0755]
ext/reflection/tests/reflectionParameter_export_error2.phpt

index 2a56bf2ef6f9314b4ba23f7b5d30c7378bdc4a7a..0f1d01ade659c1d37ec2d827af001b537d7ebc4f 100644 (file)
@@ -187,11 +187,18 @@ typedef struct _parameter_reference {
        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;
@@ -221,34 +228,45 @@ static void reflection_register_implement(zend_class_entry *class_entry, zend_cl
 }
 /* }}} */
 
-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 (fptr
+               && fptr->type != ZEND_USER_FUNCTION
+               && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0
+       ) {
+               efree(fptr->internal_function.function_name.v);
+               efree(fptr);
        }
-       if (intern->obj) {
-               zval_ptr_dtor(&intern->obj);
-       }
-       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);
 }
 /* }}} */
 
@@ -258,14 +276,12 @@ static zend_object_value reflection_objects_new(zend_class_entry *class_type TSR
        zend_object_value retval;
        reflection_object *intern;
 
-       intern = emalloc(sizeof(reflection_object));
-       intern->ptr = NULL;
-       intern->obj = NULL;
-       intern->free_ptr = 0;
+       intern = ecalloc(1, sizeof(reflection_object));
+       intern->zo.ce = class_type;
 
        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;
 }
@@ -528,20 +544,34 @@ static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *in
                                        zstr key;
                                        uint key_len;
                                        ulong num_index;
-                                       uint len = UG(unicode)?u_strlen(mptr->common.function_name.u):strlen(mptr->common.function_name.s);
+                                       uint lcname_len, len = UG(unicode)?u_strlen(mptr->common.function_name.u):strlen(mptr->common.function_name.s);
+                                       zstr lcname = zend_u_str_case_fold(ZEND_STR_TYPE, mptr->common.function_name, len, 1, &lcname_len);
 
                                        /* Do not display old-style inherited constructors */
                                        if ((mptr->common.fn_flags & ZEND_ACC_CTOR) == 0 ||
                                            mptr->common.scope == ce ||
                                            zend_hash_get_current_key_ex(&ce->function_table, &key, &key_len, &num_index, 0, &pos) != (UG(unicode)?HASH_KEY_IS_UNICODE:HASH_KEY_IS_STRING) ||
-                                           (UG(unicode) ? 
-                                            (zend_u_binary_strcasecmp(key.u, key_len-1, mptr->common.function_name.u, len) == 0) :
-                                            (zend_binary_strcasecmp(key.s, key_len-1, mptr->common.function_name.s, len) == 0))) {
+                                           (UG(unicode)
+                                            ? zend_u_binary_strcmp(key.u, key_len-1, lcname.u, lcname_len) == 0
+                                            : zend_binary_strcmp(key.s, key_len-1, lcname.s, lcname_len) == 0)
+                                       ) {
+                                               zend_function *closure;
+                                               /* see if this is a closure */
+                                               if (ce == zend_ce_closure && obj && (len == sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+                                                       && ZEND_U_EQUAL(ZEND_STR_TYPE, lcname, len, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+                                                       && (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);
                                        }
+                                       efree(lcname.v);
                                }
                                zend_hash_move_forward_ex(&ce->function_table, &pos);
                        }
@@ -1057,7 +1087,7 @@ PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object TSR
        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_ascii_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL);
 }
@@ -1086,7 +1116,7 @@ static void reflection_extension_factory(zval *object, const char *name_str TSRM
        MAKE_STD_ZVAL(name);
        ZVAL_ASCII_STRINGL(name, module->name, name_len, 1);
        intern->ptr = module;
-       intern->free_ptr = 0;
+       intern->ref_type = REF_TYPE_OTHER;
        intern->ce = NULL;
        zend_ascii_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL);
 }
@@ -1113,7 +1143,7 @@ static void reflection_parameter_factory(zend_function *fptr, struct _zend_arg_i
        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_ascii_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL);
 }
@@ -1131,7 +1161,7 @@ static void reflection_function_factory(zend_function *function, zval *object TS
        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_ascii_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL);
 }
@@ -1151,7 +1181,7 @@ static void reflection_method_factory(zend_class_entry *ce, zend_function *metho
        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_ascii_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL);
        zend_ascii_hash_update(Z_OBJPROP_P(object), "class", sizeof("class"), (void **) &classname, sizeof(zval *), NULL);
@@ -1200,7 +1230,7 @@ static void reflection_property_factory(zend_class_entry *ce, zend_property_info
        reference->prop = *prop;
        reference->ignore_visibility = 0;
        intern->ptr = reference;
-       intern->free_ptr = 1;
+       intern->ref_type = REF_TYPE_PROPERTY;
        intern->ce = ce;
        zend_ascii_hash_update(Z_OBJPROP_P(object), "name", sizeof("name"), (void **) &name, sizeof(zval *), NULL);
        zend_ascii_hash_update(Z_OBJPROP_P(object), "class", sizeof("class"), (void **) &classname, sizeof(zval *), NULL);
@@ -1435,7 +1465,7 @@ ZEND_METHOD(reflection_function, __construct)
        ZVAL_TEXT(name, fptr->common.function_name, 1);
        zend_ascii_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;
 }
 /* }}} */
@@ -1916,7 +1946,13 @@ ZEND_METHOD(reflection_parameter, __construct)
                                
                                convert_to_text_ex(method);
                                lcname = zend_u_str_case_fold(Z_TYPE_PP(method), Z_UNIVAL_PP(method), Z_UNILEN_PP(method), 1, &lcname_len);
-                               if (zend_u_hash_find(&ce->function_table, Z_TYPE_PP(method), 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
+                                       && ZEND_U_EQUAL(ZEND_STR_TYPE, lcname, lcname_len, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+                                       && (fptr = zend_get_closure_invoke_method(*classref TSRMLS_CC)) != NULL
+                               ) {
+                                       /* do nothing, fptr is already set */
+                               } else if (zend_u_hash_find(&ce->function_table, Z_TYPE_PP(method), lcname, lcname_len + 1, (void **) &fptr) == FAILURE) {
                                        efree(lcname.v);
                                        zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, 
                                                "Method %R::%R() does not exist", Z_TYPE_PP(classref), Z_UNIVAL_PP(classref), Z_TYPE_PP(method), Z_UNIVAL_PP(method));
@@ -1925,9 +1961,22 @@ ZEND_METHOD(reflection_parameter, __construct)
                                efree(lcname.v);
                        }
                        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_u_hash_find(&ce->function_table, IS_STRING, ZSTR(ZEND_INVOKE_FUNC_NAME), sizeof(ZEND_INVOKE_FUNC_NAME), (void **)&fptr) == FAILURE) {
+                                       zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC,
+                                               "Method %v::%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 */
        }
        
@@ -1973,7 +2022,7 @@ ZEND_METHOD(reflection_parameter, __construct)
        ref->required = fptr->common.required_num_args;
        ref->fptr = fptr;
        intern->ptr = ref;
-       intern->free_ptr = 1;
+       intern->ref_type = REF_TYPE_PARAMETER;
        intern->ce = ce;
 }
 /* }}} */
@@ -2237,7 +2286,7 @@ ZEND_METHOD(reflection_method, export)
 ZEND_METHOD(reflection_method, __construct)
 {
        zval *name, *classname;
-       zval *object;
+       zval *object, *orig_obj;
        reflection_object *intern;
        unsigned int lcname_len;
        zstr lcname;
@@ -2246,11 +2295,22 @@ ZEND_METHOD(reflection_method, __construct)
        zend_function *mptr;
        zstr name_str;
        zstr tmp;
-       int name_len, tmp_len;
+       int name_len, tmp_len, free_name_str = 0;
        zval ztmp;
        zend_uchar type;
 
-       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "zt", &classname, &name_str, &name_len, &type) == FAILURE) {
+       if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "o", &classname) == SUCCESS) {
+               if (UG(unicode)) {
+                       name_str.u = USTR_MAKE(ZEND_INVOKE_FUNC_NAME);
+                       free_name_str = 1;
+                       type = IS_UNICODE;
+               } else {
+                       name_str.s = ZEND_INVOKE_FUNC_NAME;
+                       type = IS_STRING;
+               }
+               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, "zt", &classname, &name_str, &name_len, &type) == FAILURE) {
                if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &name_str, &name_len, &type) == FAILURE) {
                        return;
                }
@@ -2272,11 +2332,19 @@ ZEND_METHOD(reflection_method, __construct)
                } else {
                        name_str.s = tmp.s + 2;
                }
-       }
+               orig_obj = NULL;
+       } else if (Z_TYPE_P(classname) == IS_OBJECT) {
+               orig_obj = classname;
+       } else {
+               orig_obj = NULL;
+       }
 
        object = getThis();
        intern = (reflection_object *) zend_object_store_get_object(object TSRMLS_CC);
        if (intern == NULL) {
+               if (free_name_str) {
+                       efree(name_str.v);
+               }
                return;
        }
        
@@ -2290,6 +2358,9 @@ ZEND_METHOD(reflection_method, __construct)
                                if (classname == &ztmp) {
                                        zval_dtor(&ztmp);
                                }
+                               if (free_name_str) {
+                                       efree(name_str.v);
+                               }
                                return;
                        }
                        ce = *pce;
@@ -2318,19 +2389,30 @@ ZEND_METHOD(reflection_method, __construct)
        
        lcname = zend_u_str_case_fold(type, name_str, name_len, 1, &lcname_len);
 
-       if (zend_u_hash_find(&ce->function_table, type, lcname, lcname_len + 1, (void **) &mptr) == FAILURE) {
+       if (ce == zend_ce_closure && orig_obj && (lcname_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+               && ZEND_U_EQUAL(ZEND_STR_TYPE, lcname, lcname_len, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+               && (mptr = zend_get_closure_invoke_method(orig_obj TSRMLS_CC)) != NULL
+       ) {
+               /* do nothing, mptr already set */
+       } else if (zend_u_hash_find(&ce->function_table, type, lcname, lcname_len + 1, (void **) &mptr) == FAILURE) {
                efree(lcname.v);
                zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, 
                        "Method %v::%R() does not exist", ce->name, type, name_str);
+               if (free_name_str) {
+                       efree(name_str.v);
+               }
                return;
        }
        efree(lcname.v);
+       if (free_name_str) {
+               efree(name_str.v);
+       }
 
        MAKE_STD_ZVAL(name);
        ZVAL_TEXT(name, mptr->common.function_name, 1);
        zend_ascii_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;
 }
 /* }}} */
@@ -2855,7 +2937,7 @@ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_ob
 
                intern->ptr = *ce;
        }
-       intern->free_ptr = 0;
+       intern->ref_type = REF_TYPE_OTHER;
 }
 /* }}} */
 
@@ -3209,7 +3291,13 @@ ZEND_METHOD(reflection_class, getMethod)
 
        GET_REFLECTION_OBJECT_PTR(ce);
        lc_name = zend_u_str_case_fold(type, name, name_len, 1, &lc_name_len);
-       if (zend_u_hash_find(&ce->function_table, type, lc_name, lc_name_len + 1, (void**) &mptr) == SUCCESS) {
+       if (ce == zend_ce_closure && intern->obj && (lc_name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+               && ZEND_U_EQUAL(ZEND_STR_TYPE, lc_name, lc_name_len, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+               && (mptr = zend_get_closure_invoke_method(intern->obj TSRMLS_CC)) != NULL
+       ) {
+               reflection_method_factory(ce, mptr, return_value TSRMLS_CC);
+               efree(lc_name.v);
+       } else if (zend_u_hash_find(&ce->function_table, type, lc_name, lc_name_len + 1, (void**) &mptr) == SUCCESS) {
                reflection_method_factory(ce, mptr, return_value TSRMLS_CC);
                efree(lc_name.v);
        } else {
@@ -3222,19 +3310,39 @@ ZEND_METHOD(reflection_class, getMethod)
 /* }}} */
 
 /* {{{ _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 = UG(unicode)?u_strlen(mptr->common.function_name.u):strlen(mptr->common.function_name.s);
+       zend_function *closure;
 
        if (mptr->common.fn_flags & filter) {
                ALLOC_ZVAL(method);
+               unsigned int lc_name_len;
+               zstr lc_name = zend_u_str_case_fold(ZEND_STR_TYPE, mptr->common.function_name, len, 1, &lc_name_len);
+               if (ce == zend_ce_closure && obj && (lc_name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+                       && ZEND_U_EQUAL(ZEND_STR_TYPE, lc_name, lc_name_len, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1)
+                       && (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);
+               efree(lc_name.v);
        }
-       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;
 }
 /* }}} */
 
@@ -3260,7 +3368,14 @@ ZEND_METHOD(reflection_class, getMethods)
        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);
+               }
+       }
 }
 /* }}} */
 
@@ -4135,7 +4250,7 @@ ZEND_METHOD(reflection_property, __construct)
        reference->prop = *property_info;
        reference->ignore_visibility = 0;
        intern->ptr = reference;
-       intern->free_ptr = 1;
+       intern->ref_type = REF_TYPE_PROPERTY;
        intern->ce = ce;
 }
 /* }}} */
@@ -4490,7 +4605,7 @@ ZEND_METHOD(reflection_extension, __construct)
        ZVAL_ASCII_STRING(name, module->name, 1);
        zend_ascii_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;
 }
 /* }}} */
diff --git a/ext/reflection/tests/closures_001.phpt b/ext/reflection/tests/closures_001.phpt
new file mode 100755 (executable)
index 0000000..7f345e6
--- /dev/null
@@ -0,0 +1,70 @@
+--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===
diff --git a/ext/reflection/tests/closures_002.phpt b/ext/reflection/tests/closures_002.phpt
new file mode 100755 (executable)
index 0000000..fff3962
--- /dev/null
@@ -0,0 +1,29 @@
+--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===
index 50baff5c5967ded2d760775a8d586056e6826dac..8bdbc6ad868444f553402e72de29d9e9bd591ee1 100644 (file)
@@ -9,15 +9,23 @@ function ReflectionParameterTest($test, $test2 = null) {
 }
 $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