]> granicus.if.org Git - php/commitdiff
ReflectionType improvements
authorAaron Piotrowski <aaron@trowski.com>
Tue, 9 Aug 2016 18:54:06 +0000 (13:54 -0500)
committerNikita Popov <nikic@php.net>
Thu, 11 Aug 2016 10:19:33 +0000 (12:19 +0200)
Added ReflectionNamedType and updated ReflectionType::__toString()

NEWS
UPGRADING
ext/reflection/php_reflection.c
ext/reflection/php_reflection.h
ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
ext/reflection/tests/ReflectionNamedType.phpt [new file with mode: 0644]
ext/reflection/tests/ReflectionType_001.phpt

diff --git a/NEWS b/NEWS
index c4f3cdbb01d3078a580de25c3b84be30c0f042c0..126f9ea372245eb9c3aad8639144b1b405386d5a 100644 (file)
--- 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)
index 79714c9fc7b87c838b414bba5027ccdef67cfe8c..8db36e1fc03ef9faf77c6d4052fc7396410a44c9 100644 (file)
--- 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
index 1be2a3680db3e915f29b8b3ac8325738b3557881..82d76457da74e770f587450e6569a680e66fe833 100644 (file)
@@ -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;
index b4f2cd7bc058685dd74294c2cb35e6d0c7c5c089..e89e13a151ede2e0796061a622a37b2d40f0684e 100644 (file)
@@ -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;
index c1f9d99f2b7bde1a74ab0ad40b09d549a9538b62..9b2122d1b92abdf8fb24df1139216515209086c0 100644 (file)
@@ -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 (file)
index 0000000..1e8f55a
--- /dev/null
@@ -0,0 +1,41 @@
+--TEST--
+ReflectionNamedType::getName() and ReflectionNamedType::__toString()
+--FILE--
+<?php
+
+function testInternalTypes(?Traversable $traversable): ?string {
+       return 'test';
+}
+
+function testUserDefinedTypes(?Test $traversable): ?Test {
+       return new Test;
+}
+
+$function = new ReflectionFunction('testInternalTypes');
+$type = $function->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"
index f764cf1519d92a70661fad3cbc852cd99468ba39..a31756688761cfbec5e258690c92fac07c59c629 100644 (file)
@@ -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