Skip to content

Commit 3b7c8bb

Browse files
committed
Fix bug #80126
When performing an unlinked instanceof, we also need to consider interfaces of parent classes, as they may not have been inherited yet.
1 parent c6e7969 commit 3b7c8bb

File tree

4 files changed

+61
-12
lines changed

4 files changed

+61
-12
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ PHP NEWS
77
(cmb)
88
. Fixed bug #79423 (copy command is limited to size of file it can copy).
99
(cmb)
10+
. Fixed bug #80126 (Covariant return types failing compilation). (Nikita)
1011

1112
- MySQLnd:
1213
. Fixed bug #80115 (mysqlnd.debug doesn't recognize absolute paths with

Zend/tests/bug80126.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Bug #80126: Covariant return types failing compilation
3+
--FILE--
4+
<?php
5+
6+
interface I {
7+
public function method(): I;
8+
}
9+
10+
abstract class A implements I {
11+
public function method(): I { }
12+
}
13+
14+
class C extends A { }
15+
16+
class C2 extends C {
17+
public function method(): C2 { }
18+
}
19+
20+
?>
21+
===DONE===
22+
--EXPECT--
23+
===DONE===

Zend/tests/bug80126_2.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Bug #80126: Covariant return types failing compilation (variation 2)
3+
--FILE--
4+
<?php
5+
6+
interface I {
7+
public function method(): I;
8+
}
9+
10+
abstract class A implements I {
11+
public function method(): I {
12+
return new static();
13+
}
14+
}
15+
16+
class C extends A { }
17+
18+
interface I2 { }
19+
20+
class C2 extends C implements I2 {
21+
public function method(): C2 { }
22+
}
23+
24+
?>
25+
===DONE===
26+
--EXPECT--
27+
===DONE===

Zend/zend_inheritance.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,6 @@ static zend_class_entry *lookup_class(zend_class_entry *scope, zend_string *name
259259

260260
/* Instanceof that's safe to use on unlinked classes. */
261261
static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
262-
zend_class_entry *ce;
263-
264262
if (ce1 == ce2) {
265263
return 1;
266264
}
@@ -269,18 +267,18 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce
269267
return instanceof_function(ce1, ce2);
270268
}
271269

272-
ce = ce1;
273-
while (ce->parent) {
274-
if (ce->ce_flags & ZEND_ACC_RESOLVED_PARENT) {
275-
ce = ce->parent;
270+
if (ce1->parent) {
271+
zend_class_entry *parent_ce;
272+
if (ce1->ce_flags & ZEND_ACC_RESOLVED_PARENT) {
273+
parent_ce = ce1->parent;
276274
} else {
277-
ce = zend_lookup_class_ex(ce->parent_name, NULL,
275+
parent_ce = zend_lookup_class_ex(ce1->parent_name, NULL,
278276
ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD);
279-
if (!ce) {
280-
break;
281-
}
282277
}
283-
if (ce == ce2) {
278+
279+
/* It's not sufficient to only check the parent chain itself, as need to do a full
280+
* recursive instanceof in case the parent interfaces haven't been copied yet. */
281+
if (parent_ce && unlinked_instanceof(parent_ce, ce2)) {
284282
return 1;
285283
}
286284
}
@@ -297,7 +295,7 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce
297295
}
298296
} else {
299297
for (i = 0; i < ce1->num_interfaces; i++) {
300-
ce = zend_lookup_class_ex(
298+
zend_class_entry *ce = zend_lookup_class_ex(
301299
ce1->interface_names[i].name, ce1->interface_names[i].lc_name,
302300
ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD);
303301
if (ce && unlinked_instanceof(ce, ce2)) {

0 commit comments

Comments
 (0)