Skip to content

Commit 270e5e3

Browse files
committed
Only allow "nearly linked" classes for parent/interface
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.
1 parent b7c353c commit 270e5e3

File tree

6 files changed

+47
-9
lines changed

6 files changed

+47
-9
lines changed

Zend/tests/bug30922.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ var_dump($a instanceOf A);
1010
echo "ok\n";
1111
?>
1212
--EXPECTF--
13-
Fatal error: Interface RecurisiveFooFar cannot implement itself in %s on line %d
13+
Fatal error: Interface 'RecurisiveFooFar' not found in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Using an unlinked parent class
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
class X extends B {}
8+
});
9+
10+
class B extends A {
11+
}
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: Class 'B' not found in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Using an unlinked parent interface
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
class X implements B {}
8+
});
9+
10+
interface B extends A {
11+
}
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: Interface 'B' not found in %s on line %d

Zend/zend_compile.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ typedef struct _zend_oparray_context {
229229
/* op_array or class is preloaded | | | */
230230
#define ZEND_ACC_PRELOADED (1 << 10) /* X | X | | */
231231
/* | | | */
232-
/* Class Flags (unused: 22...) | | | */
232+
/* Class Flags (unused: 23...) | | | */
233233
/* =========== | | | */
234234
/* | | | */
235235
/* Special class types | | | */
@@ -278,6 +278,9 @@ typedef struct _zend_oparray_context {
278278
/* Class has unresolved variance obligations. | | | */
279279
#define ZEND_ACC_UNRESOLVED_VARIANCE (1 << 21) /* X | | | */
280280
/* | | | */
281+
/* Class is linked apart from variance obligations. | | | */
282+
#define ZEND_ACC_NEARLY_LINKED (1 << 22) /* X | | | */
283+
/* | | | */
281284
/* Function Flags (unused: 23, 26) | | | */
282285
/* ============== | | | */
283286
/* | | | */
@@ -864,6 +867,7 @@ void zend_assert_valid_class_name(const zend_string *const_name);
864867
#define ZEND_FETCH_CLASS_SILENT 0x0100
865868
#define ZEND_FETCH_CLASS_EXCEPTION 0x0200
866869
#define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400
870+
#define ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED 0x0800
867871

868872
#define ZEND_PARAM_REF (1<<0)
869873
#define ZEND_PARAM_VARIADIC (1<<1)

Zend/zend_execute_API.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -917,8 +917,12 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
917917
zend_string_release_ex(lc_name, 0);
918918
}
919919
ce = (zend_class_entry*)Z_PTR_P(zv);
920-
if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED)) &&
921-
!(flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED)) {
920+
if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) {
921+
if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) ||
922+
((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) &&
923+
(ce->ce_flags & ZEND_ACC_NEARLY_LINKED))) {
924+
return ce;
925+
}
922926
return NULL;
923927
}
924928
return ce;

Zend/zend_inheritance.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,9 +1005,8 @@ static inline void do_implement_interface(zend_class_entry *ce, zend_class_entry
10051005
if (!(ce->ce_flags & ZEND_ACC_INTERFACE) && iface->interface_gets_implemented && iface->interface_gets_implemented(iface, ce) == FAILURE) {
10061006
zend_error_noreturn(E_CORE_ERROR, "Class %s could not implement interface %s", ZSTR_VAL(ce->name), ZSTR_VAL(iface->name));
10071007
}
1008-
if (UNEXPECTED(ce == iface)) {
1009-
zend_error_noreturn(E_ERROR, "Interface %s cannot implement itself", ZSTR_VAL(ce->name));
1010-
}
1008+
/* This should be prevented by the class lookup logic. */
1009+
ZEND_ASSERT(ce != iface);
10111010
}
10121011
/* }}} */
10131012

@@ -1457,7 +1456,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce) /* {{{ */
14571456
for (i = 0; i < ce->num_interfaces; i++) {
14581457
iface = zend_fetch_class_by_name(
14591458
ce->interface_names[i].name, ce->interface_names[i].lc_name,
1460-
ZEND_FETCH_CLASS_INTERFACE|ZEND_FETCH_CLASS_ALLOW_UNLINKED);
1459+
ZEND_FETCH_CLASS_INTERFACE|ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED);
14611460
if (!(iface->ce_flags & ZEND_ACC_LINKED)) {
14621461
add_dependency_obligation(ce, iface);
14631462
}
@@ -2404,7 +2403,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
24042403
{
24052404
if (ce->parent_name) {
24062405
zend_class_entry *parent = zend_fetch_class_by_name(
2407-
ce->parent_name, lc_parent_name, ZEND_FETCH_CLASS_ALLOW_UNLINKED);
2406+
ce->parent_name, lc_parent_name, ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED);
24082407
if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
24092408
add_dependency_obligation(ce, parent);
24102409
}
@@ -2427,6 +2426,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
24272426
return;
24282427
}
24292428

2429+
ce->ce_flags |= ZEND_ACC_NEARLY_LINKED;
24302430
load_delayed_classes();
24312431
if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
24322432
resolve_delayed_variance_obligations(ce);

0 commit comments

Comments
 (0)