diff options
author | Matteo Beccati <mbeccati@php.net> | 2015-06-08 08:32:18 +0200 |
---|---|---|
committer | Matteo Beccati <mbeccati@php.net> | 2015-06-08 08:46:30 +0200 |
commit | ec281fef480a3159d9974f3fc8ce93033bc0546b (patch) | |
tree | 7a87ce433f9c0a218ef4fa459b65ea64bf047d26 | |
parent | cf8898fbd53ccab619f225c4a8e031d5f3c0a785 (diff) | |
download | php-git-ec281fef480a3159d9974f3fc8ce93033bc0546b.tar.gz |
Reflection support for type hints and return types
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | UPGRADING | 7 | ||||
-rw-r--r-- | ext/reflection/php_reflection.c | 173 | ||||
-rw-r--r-- | ext/reflection/php_reflection.h | 1 | ||||
-rw-r--r-- | ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt | 4 | ||||
-rw-r--r-- | ext/reflection/tests/ReflectionType_001.phpt | 185 | ||||
-rw-r--r-- | ext/reflection/tests/ReflectionType_002.phpt | 17 | ||||
-rw-r--r-- | ext/reflection/tests/parameters_typehint.phpt | 45 |
8 files changed, 320 insertions, 114 deletions
@@ -193,6 +193,8 @@ - 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) @@ -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 diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 7cc0d9c2b5..d2ccfe4442 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -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; diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h index 1bb47b16c8..b50309d9ff 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -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; diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 6ae65d7885..4eda22a3f9 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -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 index 0000000000..f764cf1519 --- /dev/null +++ b/ext/reflection/tests/ReflectionType_001.phpt @@ -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 index 0000000000..8313862ec5 --- /dev/null +++ b/ext/reflection/tests/ReflectionType_002.phpt @@ -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 index 61ea5b50fc..0000000000 --- a/ext/reflection/tests/parameters_typehint.phpt +++ /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) |