]> granicus.if.org Git - php/commitdiff
Only allow "nearly linked" classes for parent/interface
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 11 Sep 2019 14:27:28 +0000 (16:27 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 11 Sep 2019 14:27:28 +0000 (16:27 +0200)
The requirements for parent/interface are difference than for the
variance checks in type declarations. The latter can work on fully
unlinked classes, but the former need inheritance to be essentially
finished, only variance checks may still be outstanding.

Adding a new flag for this because we have lots of space, but we
could also represent these "inheritance states" more compactly in
the future.

Zend/tests/bug30922.phpt
Zend/tests/type_declarations/variance/unlinked_parent_1.phpt [new file with mode: 0644]
Zend/tests/type_declarations/variance/unlinked_parent_2.phpt [new file with mode: 0644]
Zend/zend_compile.h
Zend/zend_execute_API.c
Zend/zend_inheritance.c

index 001845a7cb38d8f6e4ea1ae74f81188438ec4445..444758c903a8ebf889b13450804876c9376081a3 100644 (file)
@@ -10,4 +10,4 @@ var_dump($a instanceOf A);
 echo "ok\n";
 ?>
 --EXPECTF--
-Fatal error: Interface RecurisiveFooFar cannot implement itself in %s on line %d
+Fatal error: Interface 'RecurisiveFooFar' not found in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt b/Zend/tests/type_declarations/variance/unlinked_parent_1.phpt
new file mode 100644 (file)
index 0000000..2030f3b
--- /dev/null
@@ -0,0 +1,15 @@
+--TEST--
+Using an unlinked parent class
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+    class X extends B {}
+});
+
+class B extends A {
+}
+
+?>
+--EXPECTF--
+Fatal error: Class 'B' not found in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt b/Zend/tests/type_declarations/variance/unlinked_parent_2.phpt
new file mode 100644 (file)
index 0000000..9e21f85
--- /dev/null
@@ -0,0 +1,15 @@
+--TEST--
+Using an unlinked parent interface
+--FILE--
+<?php
+
+spl_autoload_register(function($class) {
+    class X implements B {}
+});
+
+interface B extends A {
+}
+
+?>
+--EXPECTF--
+Fatal error: Interface 'B' not found in %s on line %d
index 3e2a9cbe96edfc2ae53f2a7d499f7c55a8c091d4..2a8ac77145a250d5cd38c9a5b224d479334c5ec8 100644 (file)
@@ -229,7 +229,7 @@ typedef struct _zend_oparray_context {
 /* op_array or class is preloaded                         |     |     |     */
 #define ZEND_ACC_PRELOADED               (1 << 10) /*  X  |  X  |     |     */
 /*                                                        |     |     |     */
-/* Class Flags (unused: 22...)                            |     |     |     */
+/* Class Flags (unused: 23...)                            |     |     |     */
 /* ===========                                            |     |     |     */
 /*                                                        |     |     |     */
 /* Special class types                                    |     |     |     */
@@ -278,6 +278,9 @@ typedef struct _zend_oparray_context {
 /* Class has unresolved variance obligations.             |     |     |     */
 #define ZEND_ACC_UNRESOLVED_VARIANCE     (1 << 21) /*  X  |     |     |     */
 /*                                                        |     |     |     */
+/* Class is linked apart from variance obligations.       |     |     |     */
+#define ZEND_ACC_NEARLY_LINKED           (1 << 22) /*  X  |     |     |     */
+/*                                                        |     |     |     */
 /* Function Flags (unused: 23, 26)                        |     |     |     */
 /* ==============                                         |     |     |     */
 /*                                                        |     |     |     */
@@ -864,6 +867,7 @@ void zend_assert_valid_class_name(const zend_string *const_name);
 #define ZEND_FETCH_CLASS_SILENT      0x0100
 #define ZEND_FETCH_CLASS_EXCEPTION   0x0200
 #define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400
+#define ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED 0x0800
 
 #define ZEND_PARAM_REF      (1<<0)
 #define ZEND_PARAM_VARIADIC (1<<1)
index 4f7f54f893030af13f3785e52a3e4fb7fae31b5b..ddef72ebd3ad0a0285ec4a41102bd69af0c7b67f 100644 (file)
@@ -917,8 +917,12 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
                        zend_string_release_ex(lc_name, 0);
                }
                ce = (zend_class_entry*)Z_PTR_P(zv);
-               if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED)) &&
-                               !(flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED)) {
+               if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) {
+                       if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) ||
+                               ((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) &&
+                                       (ce->ce_flags & ZEND_ACC_NEARLY_LINKED))) {
+                               return ce;
+                       }
                        return NULL;
                }
                return ce;
index 9c8acca865666ff1ea10f7bf03db7655f19603c8..a108b2e85ea981a492b74307175ad6dfe03273ee 100644 (file)
@@ -1005,9 +1005,8 @@ static inline void do_implement_interface(zend_class_entry *ce, zend_class_entry
        if (!(ce->ce_flags & ZEND_ACC_INTERFACE) && iface->interface_gets_implemented && iface->interface_gets_implemented(iface, ce) == FAILURE) {
                zend_error_noreturn(E_CORE_ERROR, "Class %s could not implement interface %s", ZSTR_VAL(ce->name), ZSTR_VAL(iface->name));
        }
-       if (UNEXPECTED(ce == iface)) {
-               zend_error_noreturn(E_ERROR, "Interface %s cannot implement itself", ZSTR_VAL(ce->name));
-       }
+       /* This should be prevented by the class lookup logic. */
+       ZEND_ASSERT(ce != iface);
 }
 /* }}} */
 
@@ -1457,7 +1456,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce) /* {{{ */
        for (i = 0; i < ce->num_interfaces; i++) {
                iface = zend_fetch_class_by_name(
                        ce->interface_names[i].name, ce->interface_names[i].lc_name,
-                       ZEND_FETCH_CLASS_INTERFACE|ZEND_FETCH_CLASS_ALLOW_UNLINKED);
+                       ZEND_FETCH_CLASS_INTERFACE|ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED);
                if (!(iface->ce_flags & ZEND_ACC_LINKED)) {
                        add_dependency_obligation(ce, iface);
                }
@@ -2404,7 +2403,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
 {
        if (ce->parent_name) {
                zend_class_entry *parent = zend_fetch_class_by_name(
-                       ce->parent_name, lc_parent_name, ZEND_FETCH_CLASS_ALLOW_UNLINKED);
+                       ce->parent_name, lc_parent_name, ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED);
                if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
                        add_dependency_obligation(ce, parent);
                }
@@ -2427,6 +2426,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
                return;
        }
 
+       ce->ce_flags |= ZEND_ACC_NEARLY_LINKED;
        load_delayed_classes();
        if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
                resolve_delayed_variance_obligations(ce);