]> granicus.if.org Git - php/commitdiff
Fixed bug #71978 (Existence of return type hint affects other compatibility rules)
authorDmitry Stogov <dmitry@zend.com>
Wed, 6 Apr 2016 14:17:10 +0000 (17:17 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 6 Apr 2016 14:17:10 +0000 (17:17 +0300)
NEWS
Zend/tests/return_types/bug71978.phpt [new file with mode: 0644]
Zend/zend_inheritance.c

diff --git a/NEWS b/NEWS
index 24da274e037f7d69feea0392001472ca94cc54c4..56e81c9d92e5e862c03674841354fe6d77afd895 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,8 @@ PHP                                                                        NEWS
 ?? ??? 2016 PHP 7.0.6
 
 - Core:
+  . Fixed bug #71978 (Existence of return type hint affects other compatibility
+    rules). (Dmitry)
   . Fixed bug #71930 (_zval_dtor_func: Assertion `(arr)->gc.refcount <= 1'
     failed). (Laruence)
   . Fixed bug #71922 (Crash on assert(new class{})). (Nikita)
diff --git a/Zend/tests/return_types/bug71978.phpt b/Zend/tests/return_types/bug71978.phpt
new file mode 100644 (file)
index 0000000..e3c8440
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+Bug #71978 (Existence of return type hint affects other compatibility rules)
+--FILE--
+<?php
+class A {
+       function foo(int $a) {}
+}
+class B extends A {
+       function foo(string $a) {}
+}
+class A1 {
+       function foo(int $a): int {}
+}
+class B1 extends A1 {
+       function foo(string $a): int {}
+}
+?>
+--EXPECTF--
+Warning: Declaration of B::foo(string $a) should be compatible with A::foo(int $a) in %s on line %d
+
+Warning: Declaration of B1::foo(string $a): int should be compatible with A1::foo(int $a): int in %s on line %d
index 55a9fdc9095730bf7bcaf9ff3bf17045ee7dbec7..e6721879ee817525e225200970cad0710a500c0b 100644 (file)
@@ -243,11 +243,6 @@ static int zend_do_perform_type_hint_check(const zend_function *fe, zend_arg_inf
                return 0;
        }
 
-       if (proto_arg_info->type_hint && proto_arg_info->allow_null && !fe_arg_info->allow_null) {
-               /* incompatible nullability */
-               return 0;
-       }
-
        return 1;
 }
 /* }}} */
@@ -324,6 +319,11 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
                        return 0;
                }
 
+               if (proto_arg_info->type_hint && proto_arg_info->allow_null && !fe_arg_info->allow_null) {
+                       /* incompatible nullability */
+                       return 0;
+               }
+
                /* by-ref constraints on arguments are invariant */
                if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) {
                        return 0;
@@ -570,17 +570,31 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
        }
 
        if (child->common.prototype && (
-               child->common.prototype->common.fn_flags & (ZEND_ACC_ABSTRACT | ZEND_ACC_HAS_RETURN_TYPE)
+               child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT
        )) {
-               if (UNEXPECTED(!zend_do_perform_implementation_check(child, child->common.prototype))) {
-                       zend_string *method_prototype = zend_get_function_declaration(child->common.prototype);
-                       zend_string *child_prototype = zend_get_function_declaration(child);
-                       zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", ZSTR_VAL(child_prototype), ZSTR_VAL(method_prototype));
+               parent = child->common.prototype;
+       }
+       if (UNEXPECTED(!zend_do_perform_implementation_check(child, parent))) {
+               int error_level;
+               const char *error_verb;
+
+               if (child->common.prototype && (
+                       child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT
+               )) {
+                       error_level = E_COMPILE_ERROR;
+                       error_verb = "must";
+               } else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
+                   (!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+                           !zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1))) {
+                       error_level = E_COMPILE_ERROR;
+                       error_verb = "must";
+               } else {
+                       error_level = E_WARNING;
+                       error_verb = "should";
                }
-       } else if (UNEXPECTED(!zend_do_perform_implementation_check(child, parent))) {
                zend_string *method_prototype = zend_get_function_declaration(parent);
                zend_string *child_prototype = zend_get_function_declaration(child);
-               zend_error(E_WARNING, "Declaration of %s should be compatible with %s", ZSTR_VAL(child_prototype), ZSTR_VAL(method_prototype));
+               zend_error(error_level, "Declaration of %s %s be compatible with %s", ZSTR_VAL(child_prototype), error_verb, ZSTR_VAL(method_prototype));
                zend_string_free(child_prototype);
                zend_string_free(method_prototype);
        }