]> granicus.if.org Git - php/commitdiff
Reflection support for type hints and return types
authorMatteo Beccati <mbeccati@php.net>
Mon, 8 Jun 2015 06:32:18 +0000 (08:32 +0200)
committerMatteo Beccati <mbeccati@php.net>
Mon, 8 Jun 2015 06:46:30 +0000 (08:46 +0200)
NEWS
UPGRADING
ext/reflection/php_reflection.c
ext/reflection/php_reflection.h
ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
ext/reflection/tests/ReflectionType_001.phpt [new file with mode: 0644]
ext/reflection/tests/ReflectionType_002.phpt [new file with mode: 0644]
ext/reflection/tests/parameters_typehint.phpt [deleted file]

diff --git a/NEWS b/NEWS
index 8f86cfe565693eca5c8821e24301b376e35732c6..21a67ddd46228d411d2e1b408a5ca4d23fa0e5d2 100644 (file)
--- a/NEWS
+++ b/NEWS
 - Reflection
   . Fixed inheritance chain of Reflector interface. (Tjerk)
   . Added ReflectionGenerator class. (Bob)
+  . Added reflection support for return types and type declarations. (Sara,
+    Matteo)
 
 - Session:
   . Fixed bug #67694 (Regression in session_regenerate_id()). (Tjerk)
index 6c652e379c6cd25b289844294ac80ec4a73acdb7..76894fe4cbc3d6bea9c00c3b3b83e07bf2fac8ee 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -540,7 +540,12 @@ Other
     accessible through stream_get_meta_data() output.
 
 - Reflection
-  . Added a ReflectionGenerator class (yield from Traces, current file/line etc.)
+  . Added a ReflectionGenerator class (yield from Traces, current file/line,
+    etc.)
+  . Added a ReflectionType class to better support the new return type and
+    scalar type declarations features. The new ReflectionParameter::getType()
+    and ReflectionFunctionAbstract::getReturnType() methods both return an
+    instance of ReflectionType.
 
 ========================================
 3. Changes in SAPI modules
index 7cc0d9c2b5f45fe71385b8734ac86387246c3ce0..d2ccfe4442e841c8cc2a238185b26033e6b1a630 100644 (file)
@@ -59,7 +59,7 @@ PHPAPI zend_class_entry *reflection_function_abstract_ptr;
 PHPAPI zend_class_entry *reflection_function_ptr;
 PHPAPI zend_class_entry *reflection_generator_ptr;
 PHPAPI zend_class_entry *reflection_parameter_ptr;
-PHPAPI zend_class_entry *reflection_typeannotation_ptr;
+PHPAPI zend_class_entry *reflection_type_ptr;
 PHPAPI zend_class_entry *reflection_class_ptr;
 PHPAPI zend_class_entry *reflection_object_ptr;
 PHPAPI zend_class_entry *reflection_method_ptr;
@@ -202,17 +202,18 @@ typedef struct _parameter_reference {
        zend_function *fptr;
 } parameter_reference;
 
-/* Struct for type annotations */
-typedef struct _typeannotation_reference {
+/* Struct for type hints */
+typedef struct _type_reference {
        struct _zend_arg_info *arg_info;
-} typeannotation_reference;
+       zend_function *fptr;
+} type_reference;
 
 typedef enum {
        REF_TYPE_OTHER,      /* Must be 0 */
        REF_TYPE_FUNCTION,
        REF_TYPE_GENERATOR,
        REF_TYPE_PARAMETER,
-       REF_TYPE_ANNOTATION,
+       REF_TYPE_TYPE,
        REF_TYPE_PROPERTY,
        REF_TYPE_DYNAMIC_PROPERTY
 } reflection_type_t;
@@ -306,14 +307,18 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */
        reflection_object *intern = reflection_object_from_obj(object);
        parameter_reference *reference;
        property_reference *prop_reference;
+       type_reference *typ_reference;
 
        if (intern->ptr) {
                switch (intern->ref_type) {
                case REF_TYPE_PARAMETER:
                        reference = (parameter_reference*)intern->ptr;
                        _free_function(reference->fptr);
-                       /* fallthrough */
-               case REF_TYPE_ANNOTATION:
+                       efree(intern->ptr);
+                       break;
+               case REF_TYPE_TYPE:
+                       typ_reference = (type_reference*)intern->ptr;
+                       _free_function(typ_reference->fptr);
                        efree(intern->ptr);
                        break;
                case REF_TYPE_FUNCTION:
@@ -1245,18 +1250,19 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje
 }
 /* }}} */
 
-/* {{{ reflection_typeannotation_factory */
-static void reflection_typeannotation_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, zval *object)
+/* {{{ reflection_type_factory */
+static void reflection_type_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, zval *object)
 {
        reflection_object *intern;
-       typeannotation_reference *reference;
+       type_reference *reference;
 
-       reflection_instantiate(reflection_typeannotation_ptr, object);
+       reflection_instantiate(reflection_type_ptr, object);
        intern = Z_REFLECTION_P(object);
-       reference = (typeannotation_reference*) emalloc(sizeof(typeannotation_reference));
+       reference = (type_reference*) emalloc(sizeof(type_reference));
        reference->arg_info = arg_info;
+       reference->fptr = fptr;
        intern->ptr = reference;
-       intern->ref_type = REF_TYPE_ANNOTATION;
+       intern->ref_type = REF_TYPE_TYPE;
        intern->ce = fptr->common.scope;
        if (closure_object) {
                Z_ADDREF_P(closure_object);
@@ -2533,6 +2539,7 @@ ZEND_METHOD(reflection_parameter, __toString)
        _parameter_string(&str, param->fptr, param->arg_info, param->offset, param->required, "");
        RETURN_NEW_STR(str.buf);
 }
+
 /* }}} */
 
 /* {{{ proto public string ReflectionParameter::getName()
@@ -2567,7 +2574,7 @@ ZEND_METHOD(reflection_parameter, getDeclaringFunction)
 /* }}} */
 
 /* {{{ proto public ReflectionClass|NULL ReflectionParameter::getDeclaringClass()
-   Returns in which class this parameter is defined (not the typehint of the parameter) */
+   Returns in which class this parameter is defined (not the type of the parameter) */
 ZEND_METHOD(reflection_parameter, getDeclaringClass)
 {
        reflection_object *intern;
@@ -2659,9 +2666,9 @@ ZEND_METHOD(reflection_parameter, getClass)
 }
 /* }}} */
 
-/* {{{ proto public bool ReflectionParameter::hasTypeAnnotation()
-   Rethern whether parameter has a type hint */
-ZEND_METHOD(reflection_parameter, hasTypeAnnotation)
+/* {{{ proto public bool ReflectionParameter::hasType()
+   Returns whether parameter has a type */
+ZEND_METHOD(reflection_parameter, hasType)
 {
        reflection_object *intern;
        parameter_reference *param;
@@ -2675,9 +2682,9 @@ ZEND_METHOD(reflection_parameter, hasTypeAnnotation)
 }
 /* }}} */
 
-/* {{{ proto public string ReflectionParameter::getTypeAnnotation()
-   Returns the typehint associated with the parameter */
-ZEND_METHOD(reflection_parameter, getTypeAnnotation)
+/* {{{ proto public ReflectionType ReflectionParameter::getType()
+   Returns the type associated with the parameter */
+ZEND_METHOD(reflection_parameter, getType)
 {
        reflection_object *intern;
        parameter_reference *param;
@@ -2687,10 +2694,13 @@ ZEND_METHOD(reflection_parameter, getTypeAnnotation)
        }
        GET_REFLECTION_OBJECT_PTR(param);
 
-       if (!param->arg_info->type_hint) {
+       if ((param->fptr->type == ZEND_INTERNAL_FUNCTION ?
+               ((zend_internal_arg_info*)param->arg_info)->type_hint :
+               param->arg_info->type_hint) == 0)
+       {
                RETURN_NULL();
        }
-       reflection_typeannotation_factory(param->fptr, Z_ISUNDEF(intern->obj)? NULL : &intern->obj, param->arg_info, return_value);
+       reflection_type_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, param->arg_info, return_value);
 }
 /* }}} */
 
@@ -2931,60 +2941,44 @@ ZEND_METHOD(reflection_parameter, isVariadic)
 }
 /* }}} */
 
-/* {{{ proto public bool ReflectionTypeAnnotation::isArray()
-   Returns whether parameter MUST be an array */
-ZEND_METHOD(reflection_typeannotation, isArray)
-{
-       reflection_object *intern;
-       typeannotation_reference *param;
-
-       if (zend_parse_parameters_none() == FAILURE) {
-               return;
-       }
-       GET_REFLECTION_OBJECT_PTR(param);
-
-       RETVAL_BOOL(param->arg_info->type_hint == IS_ARRAY);
-}
-/* }}} */
-
-/* {{{ proto public bool ReflectionTypeAnnotation::isCallable()
-   Returns whether parameter MUST be callable */
-ZEND_METHOD(reflection_typeannotation, isCallable)
+/* {{{ proto public bool ReflectionType::allowsNull()
+  Returns whether parameter MAY be null */
+ZEND_METHOD(reflection_type, allowsNull)
 {
        reflection_object *intern;
-       typeannotation_reference *param;
+       type_reference *param;
 
        if (zend_parse_parameters_none() == FAILURE) {
                return;
        }
        GET_REFLECTION_OBJECT_PTR(param);
 
-       RETVAL_BOOL(param->arg_info->type_hint == IS_CALLABLE);
+       RETVAL_BOOL(param->arg_info->allow_null);
 }
 /* }}} */
 
-/* {{{ proto public bool ReflectionTypeAnnotation::isNullable()
-  Returns whether parameter MAY be null */
-ZEND_METHOD(reflection_typeannotation, isNullable)
+/* {{{ proto public bool ReflectionType::isBuiltin()
+  Returns whether parameter is a builtin type */
+ZEND_METHOD(reflection_type, isBuiltin)
 {
        reflection_object *intern;
-       typeannotation_reference *param;
+       type_reference *param;
 
        if (zend_parse_parameters_none() == FAILURE) {
                return;
        }
        GET_REFLECTION_OBJECT_PTR(param);
 
-       RETVAL_BOOL(param->arg_info->allow_null);
+       RETVAL_BOOL(param->arg_info->type_hint != IS_OBJECT);
 }
 /* }}} */
 
-/* {{{ proto public string ReflectionTypeAnnotation::__toString()
-   Return the text of the type annotation */
-ZEND_METHOD(reflection_typeannotation, __toString)
+/* {{{ proto public string ReflectionType::__toString()
+   Return the text of the type hint */
+ZEND_METHOD(reflection_type, __toString)
 {
        reflection_object *intern;
-       typeannotation_reference *param;
+       type_reference *param;
 
        if (zend_parse_parameters_none() == FAILURE) {
                return;
@@ -2994,10 +2988,18 @@ ZEND_METHOD(reflection_typeannotation, __toString)
        switch (param->arg_info->type_hint) {
                case IS_ARRAY:    RETURN_STRINGL("array", sizeof("array") - 1);
                case IS_CALLABLE: RETURN_STRINGL("callable", sizeof("callable") - 1);
-               case IS_OBJECT:   RETURN_STR(zend_string_copy(param->arg_info->class_name));
-               default:
-                       php_error_docref(NULL, E_ERROR, "Unknown type annotation: %d", (int)param->arg_info->type_hint);
-                       RETURN_EMPTY_STRING();
+               case IS_OBJECT:
+                       if (param->fptr->type == ZEND_INTERNAL_FUNCTION) {
+                               if (!(param->fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
+                                       RETURN_STRING(((zend_internal_arg_info*)param->arg_info)->class_name);
+                               }
+                       }
+                       RETURN_STR_COPY(param->arg_info->class_name);
+               case IS_STRING:   RETURN_STRINGL("string", sizeof("string") - 1);
+               case _IS_BOOL:    RETURN_STRINGL("bool", sizeof("bool") - 1);
+               case IS_LONG:     RETURN_STRINGL("int", sizeof("int") - 1);
+               case IS_DOUBLE:   RETURN_STRINGL("float", sizeof("float") - 1);
+               EMPTY_SWITCH_DEFAULT_CASE()
        }
 }
 /* }}} */
@@ -3514,6 +3516,44 @@ ZEND_METHOD(reflection_function, getShortName)
 }
 /* }}} */
 
+/* {{{ proto public bool ReflectionFunctionAbstract:hasReturnType()
+   Return whether the function has a return type */
+ZEND_METHOD(reflection_function, hasReturnType)
+{
+       reflection_object *intern;
+       zend_function *fptr;
+
+       if (zend_parse_parameters_none() == FAILURE) {
+               return;
+       }
+
+       GET_REFLECTION_OBJECT_PTR(fptr);
+
+       RETVAL_BOOL(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionFunctionAbstract::getReturnType()
+   Returns the return type associated with the function */
+ZEND_METHOD(reflection_function, getReturnType)
+{
+       reflection_object *intern;
+       zend_function *fptr;
+
+       if (zend_parse_parameters_none() == FAILURE) {
+               return;
+       }
+
+       GET_REFLECTION_OBJECT_PTR(fptr);
+
+       if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+               RETURN_NULL();
+       }
+
+       reflection_type_factory(_copy_function(fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, &fptr->common.arg_info[-1], return_value);
+}
+/* }}} */
+
 /* {{{ proto public bool ReflectionMethod::isConstructor()
    Returns whether this method is the constructor */
 ZEND_METHOD(reflection_method, isConstructor)
@@ -6119,6 +6159,8 @@ static const zend_function_entry reflection_function_abstract_functions[] = {
        ZEND_ME(reflection_function, getStartLine, arginfo_reflection__void, 0)
        ZEND_ME(reflection_function, getStaticVariables, arginfo_reflection__void, 0)
        ZEND_ME(reflection_function, returnsReference, arginfo_reflection__void, 0)
+       ZEND_ME(reflection_function, hasReturnType, arginfo_reflection__void, 0)
+       ZEND_ME(reflection_function, getReturnType, arginfo_reflection__void, 0)
        PHP_FE_END
 };
 
@@ -6199,7 +6241,7 @@ static const zend_function_entry reflection_method_functions[] = {
        ZEND_ME(reflection_method, invokeArgs, arginfo_reflection_method_invokeArgs, 0)
        ZEND_ME(reflection_method, getDeclaringClass, arginfo_reflection__void, 0)
        ZEND_ME(reflection_method, getPrototype, arginfo_reflection__void, 0)
-       ZEND_ME(reflection_property, setAccessible, arginfo_reflection_method_setAccessible, 0)
+       ZEND_ME(reflection_method, setAccessible, arginfo_reflection_method_setAccessible, 0)
        PHP_FE_END
 };
 
@@ -6416,8 +6458,8 @@ static const zend_function_entry reflection_parameter_functions[] = {
        ZEND_ME(reflection_parameter, getDeclaringFunction, arginfo_reflection__void, 0)
        ZEND_ME(reflection_parameter, getDeclaringClass, arginfo_reflection__void, 0)
        ZEND_ME(reflection_parameter, getClass, arginfo_reflection__void, 0)
-       ZEND_ME(reflection_parameter, hasTypeAnnotation, arginfo_reflection__void, 0)
-       ZEND_ME(reflection_parameter, getTypeAnnotation, arginfo_reflection__void, 0)
+       ZEND_ME(reflection_parameter, hasType, arginfo_reflection__void, 0)
+       ZEND_ME(reflection_parameter, getType, arginfo_reflection__void, 0)
        ZEND_ME(reflection_parameter, isArray, arginfo_reflection__void, 0)
        ZEND_ME(reflection_parameter, isCallable, arginfo_reflection__void, 0)
        ZEND_ME(reflection_parameter, allowsNull, arginfo_reflection__void, 0)
@@ -6431,12 +6473,11 @@ static const zend_function_entry reflection_parameter_functions[] = {
        PHP_FE_END
 };
 
-static const zend_function_entry reflection_typeannotation_functions[] = {
+static const zend_function_entry reflection_type_functions[] = {
        ZEND_ME(reflection, __clone, arginfo_reflection__void, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL)
-       ZEND_ME(reflection_typeannotation, isArray, arginfo_reflection__void, 0)
-       ZEND_ME(reflection_typeannotation, isCallable, arginfo_reflection__void, 0)
-       ZEND_ME(reflection_typeannotation, isNullable, arginfo_reflection__void, 0)
-       ZEND_ME(reflection_typeannotation, __toString, arginfo_reflection__void, 0)
+       ZEND_ME(reflection_type, allowsNull, arginfo_reflection__void, 0)
+       ZEND_ME(reflection_type, isBuiltin, arginfo_reflection__void, 0)
+       ZEND_ME(reflection_type, __toString, arginfo_reflection__void, 0)
        PHP_FE_END
 };
 
@@ -6553,9 +6594,9 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
        zend_class_implements(reflection_parameter_ptr, 1, reflector_ptr);
        zend_declare_property_string(reflection_parameter_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC);
 
-       INIT_CLASS_ENTRY(_reflection_entry, "ReflectionTypeAnnotation", reflection_typeannotation_functions);
+       INIT_CLASS_ENTRY(_reflection_entry, "ReflectionType", reflection_type_functions);
        _reflection_entry.create_object = reflection_objects_new;
-       reflection_typeannotation_ptr = zend_register_internal_class(&_reflection_entry);
+       reflection_type_ptr = zend_register_internal_class(&_reflection_entry);
 
        INIT_CLASS_ENTRY(_reflection_entry, "ReflectionMethod", reflection_method_functions);
        _reflection_entry.create_object = reflection_objects_new;
index 1bb47b16c8b51d4e7a2837cd2c928e76012e6e96..b50309d9ffe2d86981d4230ddccd67f5c2a3bc32 100644 (file)
@@ -37,6 +37,7 @@ extern PHPAPI zend_class_entry *reflection_ptr;
 extern PHPAPI zend_class_entry *reflection_function_abstract_ptr;
 extern PHPAPI zend_class_entry *reflection_function_ptr;
 extern PHPAPI zend_class_entry *reflection_parameter_ptr;
+extern PHPAPI zend_class_entry *reflection_type_ptr;
 extern PHPAPI zend_class_entry *reflection_class_ptr;
 extern PHPAPI zend_class_entry *reflection_object_ptr;
 extern PHPAPI zend_class_entry *reflection_method_ptr;
index 6ae65d78854bf4feedf4feb4f7d6bed1af7be436..4eda22a3f97992f56575771449f0f3b7954a9be4 100644 (file)
@@ -45,10 +45,10 @@ array(14) {
     ["name"]=>
     string(19) "ReflectionParameter"
   }
-  ["ReflectionTypeAnnotation"]=>
+  ["ReflectionType"]=>
   object(ReflectionClass)#9 (1) {
     ["name"]=>
-    string(24) "ReflectionTypeAnnotation"
+    string(14) "ReflectionType"
   }
   ["ReflectionMethod"]=>
   object(ReflectionClass)#10 (1) {
diff --git a/ext/reflection/tests/ReflectionType_001.phpt b/ext/reflection/tests/ReflectionType_001.phpt
new file mode 100644 (file)
index 0000000..f764cf1
--- /dev/null
@@ -0,0 +1,185 @@
+--TEST--
+ReflectionParameter::get/hasType and ReflectionType tests
+--FILE--
+<?php
+function foo(stdClass $a, array $b, callable $c, stdClass $d = null, $e = null, string $f, bool $g, int $h, float $i, NotExisting $j) { }
+
+function bar(): stdClass { return new stdClass; }
+
+class c extends stdClass {
+  function bar(self $x): int { return 1; }
+  function pbar(parent $x): int { return 1; }
+  function factory(): self { return new c; }
+  function pfactory(): parent { return new stdClass; }
+}
+
+$closure = function (Test $a): Test { return $a; };
+
+echo "*** functions\n";
+
+foreach ([
+  new ReflectionFunction('foo'),
+  new ReflectionFunction($closure),
+] as $idx => $rf) {
+  foreach ($rf->getParameters() as $idx2 => $rp) {
+    echo "** Function $idx - Parameter $idx2\n";
+    var_dump($rp->hasType());
+    $ra = $rp->getType();
+    if ($ra) {
+      var_dump($ra->allowsNull());
+      var_dump($ra->isBuiltin());
+      var_dump((string)$ra);
+    }
+  }
+}
+
+echo "\n*** methods\n";
+
+foreach ([
+  new ReflectionMethod('SplObserver', 'update'),
+  new ReflectionMethod('c', 'bar'),
+  new ReflectionMethod('c', 'pbar'),
+  new ReflectionMethod($closure, '__invoke'),
+] as $idx => $rm) {
+  foreach ($rm->getParameters() as $idx2 => $rp) {
+    echo "** Method $idx - parameter $idx2\n";
+    var_dump($rp->hasType());
+    $ra = $rp->getType();
+    if ($ra) {
+      var_dump($ra->allowsNull());
+      var_dump($ra->isBuiltin());
+      var_dump((string)$ra);
+    }
+  }
+}
+
+echo "\n*** return types\n";
+
+foreach ([
+  new ReflectionMethod('SplObserver', 'update'),
+  new ReflectionFunction('bar'),
+  new ReflectionMethod('c', 'bar'),
+  new ReflectionMethod('c', 'factory'),
+  new ReflectionMethod('c', 'pfactory'),
+  new ReflectionFunction($closure),
+  new ReflectionMethod($closure, '__invoke'),
+] as $idx => $rf) {
+  echo "** Function/method return type $idx\n";
+  var_dump($rf->hasReturnType());
+  $ra = $rf->getReturnType();
+  if ($ra) {
+    var_dump($ra->allowsNull());
+    var_dump($ra->isBuiltin());
+    var_dump((string)$ra);
+  }
+}
+--EXPECT--
+*** functions
+** Function 0 - Parameter 0
+bool(true)
+bool(false)
+bool(false)
+string(8) "stdClass"
+** Function 0 - Parameter 1
+bool(true)
+bool(false)
+bool(true)
+string(5) "array"
+** Function 0 - Parameter 2
+bool(true)
+bool(false)
+bool(true)
+string(8) "callable"
+** Function 0 - Parameter 3
+bool(true)
+bool(true)
+bool(false)
+string(8) "stdClass"
+** Function 0 - Parameter 4
+bool(false)
+** Function 0 - Parameter 5
+bool(true)
+bool(false)
+bool(true)
+string(6) "string"
+** Function 0 - Parameter 6
+bool(true)
+bool(false)
+bool(true)
+string(4) "bool"
+** Function 0 - Parameter 7
+bool(true)
+bool(false)
+bool(true)
+string(3) "int"
+** Function 0 - Parameter 8
+bool(true)
+bool(false)
+bool(true)
+string(5) "float"
+** Function 0 - Parameter 9
+bool(true)
+bool(false)
+bool(false)
+string(11) "NotExisting"
+** Function 1 - Parameter 0
+bool(true)
+bool(false)
+bool(false)
+string(4) "Test"
+
+*** methods
+** Method 0 - parameter 0
+bool(true)
+bool(false)
+bool(false)
+string(10) "SplSubject"
+** Method 1 - parameter 0
+bool(true)
+bool(false)
+bool(false)
+string(4) "self"
+** Method 2 - parameter 0
+bool(true)
+bool(false)
+bool(false)
+string(6) "parent"
+** Method 3 - parameter 0
+bool(true)
+bool(false)
+bool(false)
+string(4) "Test"
+
+*** return types
+** Function/method return type 0
+bool(false)
+** Function/method return type 1
+bool(true)
+bool(false)
+bool(false)
+string(8) "stdClass"
+** Function/method return type 2
+bool(true)
+bool(false)
+bool(true)
+string(3) "int"
+** Function/method return type 3
+bool(true)
+bool(false)
+bool(false)
+string(4) "self"
+** Function/method return type 4
+bool(true)
+bool(false)
+bool(false)
+string(6) "parent"
+** Function/method return type 5
+bool(true)
+bool(false)
+bool(false)
+string(4) "Test"
+** Function/method return type 6
+bool(true)
+bool(false)
+bool(false)
+string(4) "Test"
diff --git a/ext/reflection/tests/ReflectionType_002.phpt b/ext/reflection/tests/ReflectionType_002.phpt
new file mode 100644 (file)
index 0000000..8313862
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+ReflectionType leak
+--FILE--
+<?php
+
+$closure = function(Test $x): Test2 { return new Test2($x); };
+$rm = new ReflectionMethod($closure, '__invoke');
+$rp = $rm->getParameters()[0];
+$rt = $rp->getType();
+$rrt = $rm->getReturnType();
+unset($rm, $rp);
+var_dump((string) $rt, (string) $rrt);
+
+--EXPECT--
+string(4) "Test"
+string(5) "Test2"
+
diff --git a/ext/reflection/tests/parameters_typehint.phpt b/ext/reflection/tests/parameters_typehint.phpt
deleted file mode 100644 (file)
index 61ea5b5..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
---TEST--
-ReflectionParameter::hasTypeAnnotation() / getTypeAnnotation()
---FILE--
-<?php
-function foo(stdClass $a, array $b, callable $c, stdClass $d = null, $e = null) { }
-
-$rf = new ReflectionFunction('foo');
-foreach ($rf->getParameters() as $idx => $rp) {
-  echo "** Parameter $idx\n";
-  var_dump($rp->hasTypeAnnotation());
-  $ra = $rp->getTypeAnnotation();
-  if ($ra) {
-    var_dump($ra->isArray());
-    var_dump($ra->isCallable());
-    var_dump($ra->isNullable());
-    var_dump((string)$ra);
-  }
-}
---EXPECT--
-** Parameter 0
-bool(true)
-bool(false)
-bool(false)
-bool(false)
-string(8) "stdClass"
-** Parameter 1
-bool(true)
-bool(true)
-bool(false)
-bool(false)
-string(5) "array"
-** Parameter 2
-bool(true)
-bool(false)
-bool(true)
-bool(false)
-string(8) "callable"
-** Parameter 3
-bool(true)
-bool(false)
-bool(false)
-bool(true)
-string(8) "stdClass"
-** Parameter 4
-bool(false)