From 622d2f41d1cdb597f4fafecaaacf66e238742bd4 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Tue, 9 Aug 2016 13:54:06 -0500 Subject: [PATCH] ReflectionType improvements Added ReflectionNamedType and updated ReflectionType::__toString() --- NEWS | 4 +- UPGRADING | 3 + ext/reflection/php_reflection.c | 75 ++++++++++++++----- ext/reflection/php_reflection.h | 1 + .../ReflectionExtension_getClasses_basic.phpt | 21 ++++-- ext/reflection/tests/ReflectionNamedType.phpt | 41 ++++++++++ ext/reflection/tests/ReflectionType_001.phpt | 2 +- 7 files changed, 120 insertions(+), 27 deletions(-) create mode 100644 ext/reflection/tests/ReflectionNamedType.phpt diff --git a/NEWS b/NEWS index c4f3cdbb01..126f9ea372 100644 --- a/NEWS +++ b/NEWS @@ -28,7 +28,9 @@ PHP NEWS - Reflection: . Implemented request #38992 (invoke() and invokeArgs() static method calls - should match). (cmb) + should match). (cmb). + . Add ReflectionNamedType::getName() and return leading "?" for nullable types + from ReflectionType::__toString(). (Trowski) - SQLite3: . Updated to SQLite3 3.14.0. (cmb) diff --git a/UPGRADING b/UPGRADING index 79714c9fc7..8db36e1fc0 100644 --- a/UPGRADING +++ b/UPGRADING @@ -93,6 +93,9 @@ PHP 7.1 UPGRADE NOTES . The behavior of ReflectionMethod::invoke() and ::invokeArgs() has been aligned, what causes slightly different behavior than before for some pathological cases. + . ReflectionType::__toString() will now return the type name with a leading + "?" if it is nullable. To retrieve the type name without leading "?" the new + ReflectionNamedType::getName() method can be used. ======================================== 2. New Features diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 1be2a3680d..82d76457da 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -60,6 +60,7 @@ 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_type_ptr; +PHPAPI zend_class_entry *reflection_named_type_ptr; PHPAPI zend_class_entry *reflection_class_ptr; PHPAPI zend_class_entry *reflection_object_ptr; PHPAPI zend_class_entry *reflection_method_ptr; @@ -1276,7 +1277,7 @@ static void reflection_type_factory(zend_function *fptr, zval *closure_object, s reflection_object *intern; type_reference *reference; - reflection_instantiate(reflection_type_ptr, object); + reflection_instantiate(reflection_named_type_ptr, object); intern = Z_REFLECTION_P(object); reference = (type_reference*) emalloc(sizeof(type_reference)); reference->arg_info = arg_info; @@ -2999,35 +3000,66 @@ ZEND_METHOD(reflection_type, isBuiltin) } /* }}} */ +/* {{{ reflection_type_name */ +static zend_string *reflection_type_name(type_reference *param) { + switch (param->arg_info->type_hint) { + case IS_ARRAY: return zend_string_init("array", sizeof("array") - 1, 0); + case IS_CALLABLE: return zend_string_init("callable", sizeof("callable") - 1, 0); + case IS_OBJECT: + if (param->fptr->type == ZEND_INTERNAL_FUNCTION && + !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { + return zend_string_init(((zend_internal_arg_info*)param->arg_info)->class_name, strlen(((zend_internal_arg_info*)param->arg_info)->class_name), 0); + } + return zend_string_copy(param->arg_info->class_name); + case IS_STRING: return zend_string_init("string", sizeof("string") - 1, 0); + case _IS_BOOL: return zend_string_init("bool", sizeof("bool") - 1, 0); + case IS_LONG: return zend_string_init("int", sizeof("int") - 1, 0); + case IS_DOUBLE: return zend_string_init("float", sizeof("float") - 1, 0); + case IS_VOID: return zend_string_init("void", sizeof("void") - 1, 0); + case IS_ITERABLE: return zend_string_init("iterable", sizeof("iterable") - 1, 0); + EMPTY_SWITCH_DEFAULT_CASE() + } +} +/* }}} */ + /* {{{ proto public string ReflectionType::__toString() Return the text of the type hint */ ZEND_METHOD(reflection_type, __toString) { reflection_object *intern; type_reference *param; + zend_string *str; if (zend_parse_parameters_none() == FAILURE) { return; } GET_REFLECTION_OBJECT_PTR(param); + + str = reflection_type_name(param); + + if (param->arg_info->allow_null) { + str = zend_string_extend(str, ZSTR_LEN(str) + 1, 0); + memmove(ZSTR_VAL(str) + 1, ZSTR_VAL(str), ZSTR_LEN(str) + 1); + ZSTR_VAL(str)[0] = '?'; + } + + RETURN_STR(str); +} +/* }}} */ - 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: - if (param->fptr->type == ZEND_INTERNAL_FUNCTION && - !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - 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); - case IS_VOID: RETURN_STRINGL("void", sizeof("void") - 1); - case IS_ITERABLE: RETURN_STRINGL("iterable", sizeof("iterable") - 1); - EMPTY_SWITCH_DEFAULT_CASE() +/* {{{ proto public string ReflectionNamedType::getName() + Return the text of the type hint */ +ZEND_METHOD(reflection_named_type, getName) +{ + reflection_object *intern; + type_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + return; } + GET_REFLECTION_OBJECT_PTR(param); + + RETURN_STR(reflection_type_name(param)); } /* }}} */ @@ -6688,6 +6720,11 @@ static const zend_function_entry reflection_type_functions[] = { PHP_FE_END }; +static const zend_function_entry reflection_named_type_functions[] = { + ZEND_ME(reflection_named_type, getName, arginfo_reflection__void, 0) + PHP_FE_END +}; + ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_extension_export, 0, 0, 1) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, return) @@ -6804,6 +6841,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ INIT_CLASS_ENTRY(_reflection_entry, "ReflectionType", reflection_type_functions); _reflection_entry.create_object = reflection_objects_new; reflection_type_ptr = zend_register_internal_class(&_reflection_entry); + + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionNamedType", reflection_named_type_functions); + _reflection_entry.create_object = reflection_objects_new; + reflection_named_type_ptr = zend_register_internal_class_ex(&_reflection_entry, reflection_type_ptr); 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 b4f2cd7bc0..e89e13a151 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -38,6 +38,7 @@ 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_named_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 c1f9d99f2b..9b2122d1b9 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -9,7 +9,7 @@ var_dump($ext->getClasses()); ?> ==DONE== --EXPECT-- -array(15) { +array(16) { ["ReflectionException"]=> object(ReflectionClass)#2 (1) { ["name"]=> @@ -50,38 +50,43 @@ array(15) { ["name"]=> string(14) "ReflectionType" } - ["ReflectionMethod"]=> + ["ReflectionNamedType"]=> object(ReflectionClass)#10 (1) { + ["name"]=> + string(19) "ReflectionNamedType" + } + ["ReflectionMethod"]=> + object(ReflectionClass)#11 (1) { ["name"]=> string(16) "ReflectionMethod" } ["ReflectionClass"]=> - object(ReflectionClass)#11 (1) { + object(ReflectionClass)#12 (1) { ["name"]=> string(15) "ReflectionClass" } ["ReflectionObject"]=> - object(ReflectionClass)#12 (1) { + object(ReflectionClass)#13 (1) { ["name"]=> string(16) "ReflectionObject" } ["ReflectionProperty"]=> - object(ReflectionClass)#13 (1) { + object(ReflectionClass)#14 (1) { ["name"]=> string(18) "ReflectionProperty" } ["ReflectionClassConstant"]=> - object(ReflectionClass)#14 (1) { + object(ReflectionClass)#15 (1) { ["name"]=> string(23) "ReflectionClassConstant" } ["ReflectionExtension"]=> - object(ReflectionClass)#15 (1) { + object(ReflectionClass)#16 (1) { ["name"]=> string(19) "ReflectionExtension" } ["ReflectionZendExtension"]=> - object(ReflectionClass)#16 (1) { + object(ReflectionClass)#17 (1) { ["name"]=> string(23) "ReflectionZendExtension" } diff --git a/ext/reflection/tests/ReflectionNamedType.phpt b/ext/reflection/tests/ReflectionNamedType.phpt new file mode 100644 index 0000000000..1e8f55ac9d --- /dev/null +++ b/ext/reflection/tests/ReflectionNamedType.phpt @@ -0,0 +1,41 @@ +--TEST-- +ReflectionNamedType::getName() and ReflectionNamedType::__toString() +--FILE-- +getParameters()[0]->getType(); +$return = $function->getReturnType(); + +var_dump($type->getName()); +var_dump((string) $type); +var_dump($return->getName()); +var_dump((string) $return); + +$function = new ReflectionFunction('testUserDefinedTypes'); +$type = $function->getParameters()[0]->getType(); +$return = $function->getReturnType(); + +var_dump($type->getName()); +var_dump((string) $type); +var_dump($return->getName()); +var_dump((string) $return); + +?> +--EXPECTF-- +string(11) "Traversable" +string(12) "?Traversable" +string(6) "string" +string(7) "?string" +string(4) "Test" +string(5) "?Test" +string(4) "Test" +string(5) "?Test" diff --git a/ext/reflection/tests/ReflectionType_001.phpt b/ext/reflection/tests/ReflectionType_001.phpt index f764cf1519..a317566887 100644 --- a/ext/reflection/tests/ReflectionType_001.phpt +++ b/ext/reflection/tests/ReflectionType_001.phpt @@ -94,7 +94,7 @@ string(8) "callable" bool(true) bool(true) bool(false) -string(8) "stdClass" +string(9) "?stdClass" ** Function 0 - Parameter 4 bool(false) ** Function 0 - Parameter 5 -- 2.50.1