Skip to content

Commit 8be567c

Browse files
committed
Process delayed variance obligations after each class
1 parent 177bdc5 commit 8be567c

15 files changed

+334
-109
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' not found in %sbug30922.php on line %d
13+
Fatal error: Interface RecurisiveFooFar cannot implement itself in %s on line %d

Zend/tests/type_declarations/variance/class_order_autoload5.phpt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Class order allowed with autoloading (6)
2+
Class order allowed with autoloading (5)
33
--FILE--
44
<?php
55

@@ -22,10 +22,6 @@ spl_autoload_register(function($class) {
2222
public function method(): R {}
2323
}
2424
} else if ($class == 'Q') {
25-
var_dump(class_exists('A'));
26-
var_dump(class_exists('B'));
27-
var_dump(class_exists('X'));
28-
var_dump(class_exists('Y'));
2925
class Q {
3026
public function method(): A {}
3127
}
@@ -41,8 +37,4 @@ $b = new B;
4137
?>
4238
===DONE===
4339
--EXPECT--
44-
bool(true)
45-
bool(false)
46-
bool(true)
47-
bool(false)
4840
===DONE===
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
Class order allowed with autoloading (6)
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
if ($class == 'A') {
8+
class A {
9+
public function method(): X {}
10+
}
11+
var_dump(new A);
12+
} else if ($class == 'B') {
13+
class B extends A {
14+
public function method(): Y {}
15+
}
16+
var_dump(new B);
17+
} else if ($class == 'X') {
18+
class X {
19+
public function method(): A {}
20+
}
21+
var_dump(new X);
22+
} else if ($class == 'Y') {
23+
class Y extends X {
24+
public function method(): B {}
25+
}
26+
var_dump(new Y);
27+
}
28+
});
29+
30+
var_dump(new B);
31+
32+
?>
33+
===DONE===
34+
--EXPECT--
35+
object(A)#2 (0) {
36+
}
37+
object(X)#2 (0) {
38+
}
39+
object(Y)#2 (0) {
40+
}
41+
object(B)#2 (0) {
42+
}
43+
object(B)#2 (0) {
44+
}
45+
===DONE===
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Variance error in the presence of autoloading (4)
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
if ($class == 'A') {
8+
class A {
9+
public function method(): X {}
10+
}
11+
var_dump(new A);
12+
} else if ($class == 'B') {
13+
class B extends A {
14+
public function method(): Y {}
15+
}
16+
var_dump(new B);
17+
} else if ($class == 'X') {
18+
class X {
19+
public function method(): B {}
20+
}
21+
var_dump(new X);
22+
} else if ($class == 'Y') {
23+
class Y extends X {
24+
public function method(): A {}
25+
}
26+
var_dump(new Y);
27+
}
28+
});
29+
30+
var_dump(new B);
31+
32+
?>
33+
--EXPECTF--
34+
object(A)#2 (0) {
35+
}
36+
object(X)#2 (0) {
37+
}
38+
39+
Fatal error: Declaration of Y::method(): A must be compatible with X::method(): B in %s on line %d
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--TEST--
2+
Variance error in the presence of autoloading (5)
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
if ($class == 'A') {
8+
class A {
9+
public function method(): X {}
10+
}
11+
var_dump(new A);
12+
} else if ($class == 'B') {
13+
class B extends A {
14+
public function method(): Y {}
15+
}
16+
var_dump(new B);
17+
} else if ($class == 'X') {
18+
class X {
19+
public function method(Y $a) {}
20+
}
21+
var_dump(new X);
22+
} else if ($class == 'Y') {
23+
class Y extends X {
24+
public function method(Z $a) {}
25+
}
26+
var_dump(new Y);
27+
} else if ($class == 'Z') {
28+
class Z extends Y {
29+
public function method($a) {}
30+
}
31+
var_dump(new Z);
32+
}
33+
});
34+
35+
var_dump(new B);
36+
37+
?>
38+
--EXPECTF--
39+
object(A)#2 (0) {
40+
}
41+
object(X)#2 (0) {
42+
}
43+
44+
Warning: Declaration of Y::method(Z $a) should be compatible with X::method(Y $a) in %s on line %d
45+
object(Z)#2 (0) {
46+
}
47+
object(Y)#2 (0) {
48+
}
49+
object(B)#2 (0) {
50+
}
51+
object(B)#2 (0) {
52+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Variance error in the presence of autoloading (6)
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
if ($class == 'A') {
8+
class A {
9+
public function method(): X {}
10+
}
11+
var_dump(new A);
12+
} else if ($class == 'B') {
13+
class B extends A {
14+
public function method(): Y {}
15+
}
16+
var_dump(new B);
17+
} else if ($class == 'X') {
18+
class X {
19+
public function method(): X {}
20+
}
21+
var_dump(new X);
22+
} else if ($class == 'Y') {
23+
class Y extends X {
24+
public function method(): Unknown {}
25+
}
26+
var_dump(new Y);
27+
}
28+
});
29+
30+
var_dump(new B);
31+
32+
?>
33+
--EXPECTF--
34+
object(A)#2 (0) {
35+
}
36+
object(X)#2 (0) {
37+
}
38+
39+
Fatal error: Could not check compatibility between Y::method(): Unknown and X::method(): X, because class Unknown is not available in %s on line %d

Zend/zend_API.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2649,7 +2649,7 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class
26492649

26502650
class_entry->type = ZEND_INTERNAL_CLASS;
26512651
zend_initialize_class_data(class_entry, 0);
2652-
class_entry->ce_flags = ce_flags | ZEND_ACC_CONSTANTS_UPDATED | ZEND_ACC_LINKED;
2652+
class_entry->ce_flags = ce_flags | ZEND_ACC_CONSTANTS_UPDATED | ZEND_ACC_LINKED | ZEND_ACC_RESOLVED_PARENT | ZEND_ACC_RESOLVED_INTERFACES;
26532653
class_entry->info.internal.module = EG(current_module);
26542654

26552655
if (class_entry->info.internal.builtin_functions) {

Zend/zend_compile.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ void init_compiler(void) /* {{{ */
373373

374374
CG(link_depth) = 0;
375375
CG(delayed_variance_obligations) = NULL;
376+
CG(delayed_autoloads) = NULL;
376377
}
377378
/* }}} */
378379

@@ -388,6 +389,11 @@ void shutdown_compiler(void) /* {{{ */
388389
FREE_HASHTABLE(CG(delayed_variance_obligations));
389390
CG(delayed_variance_obligations) = NULL;
390391
}
392+
if (CG(delayed_autoloads)) {
393+
zend_hash_destroy(CG(delayed_autoloads));
394+
FREE_HASHTABLE(CG(delayed_autoloads));
395+
CG(delayed_autoloads) = NULL;
396+
}
391397
}
392398
/* }}} */
393399

Zend/zend_compile.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,14 @@ typedef struct _zend_oparray_context {
269269
/* Children must reuse parent get_iterator() | | | */
270270
#define ZEND_ACC_REUSE_GET_ITERATOR (1 << 18) /* X | | | */
271271
/* | | | */
272-
/* Class is being linked. Don't free strings. | | | */
273-
#define ZEND_ACC_LINKING_IN_PROGRESS (1 << 19) /* X | | | */
272+
/* Parent class is resolved (CE). | | | */
273+
#define ZEND_ACC_RESOLVED_PARENT (1 << 19) /* X | | | */
274+
/* | | | */
275+
/* Interfaces are resolved (CEs). | | | */
276+
#define ZEND_ACC_RESOLVED_INTERFACES (1 << 20) /* X | | | */
274277
/* | | | */
275278
/* Class has unresolved variance obligations. | | | */
276-
#define ZEND_ACC_UNRESOLVED_VARIANCE (1 << 20) /* X | | | */
279+
#define ZEND_ACC_UNRESOLVED_VARIANCE (1 << 21) /* X | | | */
277280
/* | | | */
278281
/* Function Flags (unused: 28...30) | | | */
279282
/* ============== | | | */
@@ -855,7 +858,7 @@ void zend_assert_valid_class_name(const zend_string *const_name);
855858
#define ZEND_FETCH_CLASS_NO_AUTOLOAD 0x80
856859
#define ZEND_FETCH_CLASS_SILENT 0x0100
857860
#define ZEND_FETCH_CLASS_EXCEPTION 0x0200
858-
#define ZEND_FETCH_CLASS_ALLOW_UNRESOLVED_VARIANCE 0x0400
861+
#define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400
859862

860863
#define ZEND_PARAM_REF (1<<0)
861864
#define ZEND_PARAM_VARIADIC (1<<1)

Zend/zend_execute_API.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "zend_vm.h"
3535
#include "zend_float.h"
3636
#include "zend_weakrefs.h"
37+
#include "zend_inheritance.h"
3738
#ifdef HAVE_SYS_TIME_H
3839
#include <sys/time.h>
3940
#endif
@@ -916,11 +917,8 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
916917
zend_string_release_ex(lc_name, 0);
917918
}
918919
ce = (zend_class_entry*)Z_PTR_P(zv);
919-
if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) {
920-
if ((flags & ZEND_FETCH_CLASS_ALLOW_UNRESOLVED_VARIANCE)
921-
&& (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)) {
922-
return ce;
923-
}
920+
if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED)) &&
921+
!(flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED)) {
924922
return NULL;
925923
}
926924
return ce;

Zend/zend_globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ struct _zend_compiler_globals {
127127

128128
uint32_t link_depth;
129129
HashTable *delayed_variance_obligations;
130+
HashTable *delayed_autoloads;
130131
};
131132

132133

0 commit comments

Comments
 (0)