mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Reflection support for type hints and return types
This commit is contained in:
2
NEWS
2
NEWS
@@ -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
|
||||
|
||||
@@ -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,44 +2941,12 @@ 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)
|
||||
{
|
||||
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_CALLABLE);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto public bool ReflectionTypeAnnotation::isNullable()
|
||||
/* {{{ proto public bool ReflectionType::allowsNull()
|
||||
Returns whether parameter MAY be null */
|
||||
ZEND_METHOD(reflection_typeannotation, isNullable)
|
||||
ZEND_METHOD(reflection_type, allowsNull)
|
||||
{
|
||||
reflection_object *intern;
|
||||
typeannotation_reference *param;
|
||||
type_reference *param;
|
||||
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
return;
|
||||
@@ -2979,12 +2957,28 @@ ZEND_METHOD(reflection_typeannotation, isNullable)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto public string ReflectionTypeAnnotation::__toString()
|
||||
Return the text of the type annotation */
|
||||
ZEND_METHOD(reflection_typeannotation, __toString)
|
||||
/* {{{ 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->type_hint != IS_OBJECT);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto public string ReflectionType::__toString()
|
||||
Return the text of the type hint */
|
||||
ZEND_METHOD(reflection_type, __toString)
|
||||
{
|
||||
reflection_object *intern;
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
185
ext/reflection/tests/ReflectionType_001.phpt
Normal file
185
ext/reflection/tests/ReflectionType_001.phpt
Normal file
@@ -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"
|
||||
17
ext/reflection/tests/ReflectionType_002.phpt
Normal file
17
ext/reflection/tests/ReflectionType_002.phpt
Normal file
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user