]> granicus.if.org Git - php/commitdiff
Merge remote-tracking branch 'pollita/reflection.typehint'
authorSara Golemon <pollita@php.net>
Sun, 15 Feb 2015 08:05:38 +0000 (09:05 +0100)
committerMatteo Beccati <mbeccati@php.net>
Mon, 8 Jun 2015 06:33:32 +0000 (08:33 +0200)
Conflicts:
ext/reflection/php_reflection.c
ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt

ext/reflection/php_reflection.c
ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
ext/reflection/tests/parameters_typehint.phpt [new file with mode: 0644]

index 92f67972d213ec36bdd1306b116eff1af7029d78..7cc0d9c2b5f45fe71385b8734ac86387246c3ce0 100644 (file)
@@ -59,6 +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_class_ptr;
 PHPAPI zend_class_entry *reflection_object_ptr;
 PHPAPI zend_class_entry *reflection_method_ptr;
@@ -201,11 +202,17 @@ typedef struct _parameter_reference {
        zend_function *fptr;
 } parameter_reference;
 
+/* Struct for type annotations */
+typedef struct _typeannotation_reference {
+       struct _zend_arg_info *arg_info;
+} typeannotation_reference;
+
 typedef enum {
        REF_TYPE_OTHER,      /* Must be 0 */
        REF_TYPE_FUNCTION,
        REF_TYPE_GENERATOR,
        REF_TYPE_PARAMETER,
+       REF_TYPE_ANNOTATION,
        REF_TYPE_PROPERTY,
        REF_TYPE_DYNAMIC_PROPERTY
 } reflection_type_t;
@@ -305,6 +312,8 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */
                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_FUNCTION:
@@ -1236,6 +1245,26 @@ 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_object *intern;
+       typeannotation_reference *reference;
+
+       reflection_instantiate(reflection_typeannotation_ptr, object);
+       intern = Z_REFLECTION_P(object);
+       reference = (typeannotation_reference*) emalloc(sizeof(typeannotation_reference));
+       reference->arg_info = arg_info;
+       intern->ptr = reference;
+       intern->ref_type = REF_TYPE_ANNOTATION;
+       intern->ce = fptr->common.scope;
+       if (closure_object) {
+               Z_ADDREF_P(closure_object);
+               ZVAL_COPY_VALUE(&intern->obj, closure_object);
+       }
+}
+/* }}} */
+
 /* {{{ reflection_function_factory */
 static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object)
 {
@@ -2630,6 +2659,41 @@ ZEND_METHOD(reflection_parameter, getClass)
 }
 /* }}} */
 
+/* {{{ proto public bool ReflectionParameter::hasTypeAnnotation()
+   Rethern whether parameter has a type hint */
+ZEND_METHOD(reflection_parameter, hasTypeAnnotation)
+{
+       reflection_object *intern;
+       parameter_reference *param;
+
+       if (zend_parse_parameters_none() == FAILURE) {
+               return;
+       }
+       GET_REFLECTION_OBJECT_PTR(param);
+
+       RETVAL_BOOL(param->arg_info->type_hint != 0);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionParameter::getTypeAnnotation()
+   Returns the typehint associated with the parameter */
+ZEND_METHOD(reflection_parameter, getTypeAnnotation)
+{
+       reflection_object *intern;
+       parameter_reference *param;
+
+       if (zend_parse_parameters_none() == FAILURE) {
+               return;
+       }
+       GET_REFLECTION_OBJECT_PTR(param);
+
+       if (!param->arg_info->type_hint) {
+               RETURN_NULL();
+       }
+       reflection_typeannotation_factory(param->fptr, Z_ISUNDEF(intern->obj)? NULL : &intern->obj, param->arg_info, return_value);
+}
+/* }}} */
+
 /* {{{ proto public bool ReflectionParameter::isArray()
    Returns whether parameter MUST be an array */
 ZEND_METHOD(reflection_parameter, isArray)
@@ -2867,6 +2931,77 @@ 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()
+  Returns whether parameter MAY be null */
+ZEND_METHOD(reflection_typeannotation, isNullable)
+{
+       reflection_object *intern;
+       typeannotation_reference *param;
+
+       if (zend_parse_parameters_none() == FAILURE) {
+               return;
+       }
+       GET_REFLECTION_OBJECT_PTR(param);
+
+       RETVAL_BOOL(param->arg_info->allow_null);
+}
+/* }}} */
+
+/* {{{ proto public string ReflectionTypeAnnotation::__toString()
+   Return the text of the type annotation */
+ZEND_METHOD(reflection_typeannotation, __toString)
+{
+       reflection_object *intern;
+       typeannotation_reference *param;
+
+       if (zend_parse_parameters_none() == FAILURE) {
+               return;
+       }
+       GET_REFLECTION_OBJECT_PTR(param);
+
+       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();
+       }
+}
+/* }}} */
+
 /* {{{ proto public static mixed ReflectionMethod::export(mixed class, string name [, bool return]) throws ReflectionException
    Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */
 ZEND_METHOD(reflection_method, export)
@@ -6281,6 +6416,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, isArray, arginfo_reflection__void, 0)
        ZEND_ME(reflection_parameter, isCallable, arginfo_reflection__void, 0)
        ZEND_ME(reflection_parameter, allowsNull, arginfo_reflection__void, 0)
@@ -6294,6 +6431,15 @@ static const zend_function_entry reflection_parameter_functions[] = {
        PHP_FE_END
 };
 
+static const zend_function_entry reflection_typeannotation_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)
+       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)
@@ -6407,6 +6553,10 @@ 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);
+       _reflection_entry.create_object = reflection_objects_new;
+       reflection_typeannotation_ptr = zend_register_internal_class(&_reflection_entry);
+
        INIT_CLASS_ENTRY(_reflection_entry, "ReflectionMethod", reflection_method_functions);
        _reflection_entry.create_object = reflection_objects_new;
        reflection_method_ptr = zend_register_internal_class_ex(&_reflection_entry, reflection_function_abstract_ptr);
index 508f73ee55a345eacd4bd0a3667cb10543ce5a79..6ae65d78854bf4feedf4feb4f7d6bed1af7be436 100644 (file)
@@ -9,7 +9,7 @@ var_dump($ext->getClasses());
 ?>
 ==DONE==
 --EXPECT--
-array(13) {
+array(14) {
   ["ReflectionException"]=>
   object(ReflectionClass)#2 (1) {
     ["name"]=>
@@ -45,33 +45,38 @@ array(13) {
     ["name"]=>
     string(19) "ReflectionParameter"
   }
-  ["ReflectionMethod"]=>
+  ["ReflectionTypeAnnotation"]=>
   object(ReflectionClass)#9 (1) {
+    ["name"]=>
+    string(24) "ReflectionTypeAnnotation"
+  }
+  ["ReflectionMethod"]=>
+  object(ReflectionClass)#10 (1) {
     ["name"]=>
     string(16) "ReflectionMethod"
   }
   ["ReflectionClass"]=>
-  object(ReflectionClass)#10 (1) {
+  object(ReflectionClass)#11 (1) {
     ["name"]=>
     string(15) "ReflectionClass"
   }
   ["ReflectionObject"]=>
-  object(ReflectionClass)#11 (1) {
+  object(ReflectionClass)#12 (1) {
     ["name"]=>
     string(16) "ReflectionObject"
   }
   ["ReflectionProperty"]=>
-  object(ReflectionClass)#12 (1) {
+  object(ReflectionClass)#13 (1) {
     ["name"]=>
     string(18) "ReflectionProperty"
   }
   ["ReflectionExtension"]=>
-  object(ReflectionClass)#13 (1) {
+  object(ReflectionClass)#14 (1) {
     ["name"]=>
     string(19) "ReflectionExtension"
   }
   ["ReflectionZendExtension"]=>
-  object(ReflectionClass)#14 (1) {
+  object(ReflectionClass)#15 (1) {
     ["name"]=>
     string(23) "ReflectionZendExtension"
   }
diff --git a/ext/reflection/tests/parameters_typehint.phpt b/ext/reflection/tests/parameters_typehint.phpt
new file mode 100644 (file)
index 0000000..61ea5b5
--- /dev/null
@@ -0,0 +1,45 @@
+--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)