@@ -31,6 +31,9 @@ static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *de
31
31
static void add_compatibility_obligation (
32
32
zend_class_entry * ce , const zend_function * child_fn , const zend_function * parent_fn ,
33
33
zend_bool always_error );
34
+ static void add_property_compatibility_obligation (
35
+ zend_class_entry * ce , const zend_property_info * child_prop ,
36
+ const zend_property_info * parent_prop );
34
37
35
38
static void overridden_ptr_dtor (zval * zv ) /* {{{ */
36
39
{
@@ -192,17 +195,16 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */
192
195
}
193
196
/* }}} */
194
197
195
- static zend_string * resolve_class_name (const zend_function * fe , zend_string * name ) {
196
- zend_class_entry * ce = fe -> common .scope ;
197
- ZEND_ASSERT (ce );
198
- if (zend_string_equals_literal_ci (name , "parent" ) && ce -> parent ) {
199
- if (ce -> ce_flags & ZEND_ACC_RESOLVED_PARENT ) {
200
- return ce -> parent -> name ;
198
+ static zend_string * resolve_class_name (zend_class_entry * scope , zend_string * name ) {
199
+ ZEND_ASSERT (scope );
200
+ if (zend_string_equals_literal_ci (name , "parent" ) && scope -> parent ) {
201
+ if (scope -> ce_flags & ZEND_ACC_RESOLVED_PARENT ) {
202
+ return scope -> parent -> name ;
201
203
} else {
202
- return ce -> parent_name ;
204
+ return scope -> parent_name ;
203
205
}
204
206
} else if (zend_string_equals_literal_ci (name , "self" )) {
205
- return ce -> name ;
207
+ return scope -> name ;
206
208
} else {
207
209
return name ;
208
210
}
@@ -218,7 +220,7 @@ static zend_bool class_visible(zend_class_entry *ce) {
218
220
}
219
221
}
220
222
221
- static zend_class_entry * lookup_class (const zend_function * fe , zend_string * name ) {
223
+ static zend_class_entry * lookup_class (zend_class_entry * scope , zend_string * name ) {
222
224
zend_class_entry * ce ;
223
225
if (!CG (in_compilation )) {
224
226
uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD ;
@@ -240,8 +242,8 @@ static zend_class_entry *lookup_class(const zend_function *fe, zend_string *name
240
242
}
241
243
242
244
/* The current class may not be registered yet, so check for it explicitly. */
243
- if (zend_string_equals_ci (fe -> common . scope -> name , name )) {
244
- return fe -> common . scope ;
245
+ if (zend_string_equals_ci (scope -> name , name )) {
246
+ return scope ;
245
247
}
246
248
}
247
249
@@ -320,16 +322,16 @@ static inheritance_status zend_perform_covariant_type_check(
320
322
return INHERITANCE_ERROR ;
321
323
}
322
324
323
- fe_class_name = resolve_class_name (fe , ZEND_TYPE_NAME (fe_type ));
324
- proto_class_name = resolve_class_name (proto , ZEND_TYPE_NAME (proto_type ));
325
+ fe_class_name = resolve_class_name (fe -> common . scope , ZEND_TYPE_NAME (fe_type ));
326
+ proto_class_name = resolve_class_name (proto -> common . scope , ZEND_TYPE_NAME (proto_type ));
325
327
if (zend_string_equals_ci (fe_class_name , proto_class_name )) {
326
328
return INHERITANCE_SUCCESS ;
327
329
}
328
330
329
331
/* Make sure to always load both classes, to avoid only registering one of them as
330
332
* a delayed autoload. */
331
- fe_ce = lookup_class (fe , fe_class_name );
332
- proto_ce = lookup_class (proto , proto_class_name );
333
+ fe_ce = lookup_class (fe -> common . scope , fe_class_name );
334
+ proto_ce = lookup_class (proto -> common . scope , proto_class_name );
333
335
if (!fe_ce ) {
334
336
* unresolved_class = fe_class_name ;
335
337
return INHERITANCE_UNRESOLVED ;
@@ -342,8 +344,9 @@ static inheritance_status zend_perform_covariant_type_check(
342
344
return unlinked_instanceof (fe_ce , proto_ce ) ? INHERITANCE_SUCCESS : INHERITANCE_ERROR ;
343
345
} else if (ZEND_TYPE_CODE (proto_type ) == IS_ITERABLE ) {
344
346
if (ZEND_TYPE_IS_CLASS (fe_type )) {
345
- zend_string * fe_class_name = resolve_class_name (fe , ZEND_TYPE_NAME (fe_type ));
346
- zend_class_entry * fe_ce = lookup_class (fe , fe_class_name );
347
+ zend_string * fe_class_name =
348
+ resolve_class_name (fe -> common .scope , ZEND_TYPE_NAME (fe_type ));
349
+ zend_class_entry * fe_ce = lookup_class (fe -> common .scope , fe_class_name );
347
350
if (!fe_ce ) {
348
351
* unresolved_class = fe_class_name ;
349
352
return INHERITANCE_UNRESOLVED ;
@@ -359,8 +362,9 @@ static inheritance_status zend_perform_covariant_type_check(
359
362
/* Currently, any class name would be allowed here. We still perform a class lookup
360
363
* for forward-compatibility reasons, as we may have named types in the future that
361
364
* are not classes (such as enums or typedefs). */
362
- zend_string * fe_class_name = resolve_class_name (fe , ZEND_TYPE_NAME (fe_type ));
363
- zend_class_entry * fe_ce = lookup_class (fe , fe_class_name );
365
+ zend_string * fe_class_name =
366
+ resolve_class_name (fe -> common .scope , ZEND_TYPE_NAME (fe_type ));
367
+ zend_class_entry * fe_ce = lookup_class (fe -> common .scope , fe_class_name );
364
368
if (!fe_ce ) {
365
369
* unresolved_class = fe_class_name ;
366
370
return INHERITANCE_UNRESOLVED ;
@@ -882,53 +886,53 @@ static zend_always_inline void do_inherit_method(zend_string *key, zend_function
882
886
}
883
887
/* }}} */
884
888
885
- zend_string * zend_resolve_property_type (zend_string * type , zend_class_entry * scope ) /* {{{ */
886
- {
887
- if (zend_string_equals_literal_ci (type , "parent" )) {
888
- if (scope && scope -> parent ) {
889
- return scope -> parent -> name ;
890
- }
891
- }
892
-
893
- if (zend_string_equals_literal_ci (type , "self" )) {
894
- if (scope ) {
895
- return scope -> name ;
896
- }
897
- }
898
-
899
- return type ;
900
- } /* }}} */
901
-
902
- zend_bool property_types_compatible (zend_property_info * parent_info , zend_property_info * child_info ) {
889
+ inheritance_status property_types_compatible (
890
+ const zend_property_info * parent_info , const zend_property_info * child_info ) {
903
891
zend_string * parent_name , * child_name ;
904
892
zend_class_entry * parent_type_ce , * child_type_ce ;
905
893
if (parent_info -> type == child_info -> type ) {
906
- return 1 ;
894
+ return INHERITANCE_SUCCESS ;
907
895
}
908
896
909
897
if (!ZEND_TYPE_IS_CLASS (parent_info -> type ) || !ZEND_TYPE_IS_CLASS (child_info -> type ) ||
910
898
ZEND_TYPE_ALLOW_NULL (parent_info -> type ) != ZEND_TYPE_ALLOW_NULL (child_info -> type )) {
911
- return 0 ;
899
+ return INHERITANCE_ERROR ;
912
900
}
913
901
914
902
parent_name = ZEND_TYPE_IS_CE (parent_info -> type )
915
903
? ZEND_TYPE_CE (parent_info -> type )-> name
916
- : zend_resolve_property_type ( ZEND_TYPE_NAME ( parent_info -> type ), parent_info -> ce );
904
+ : resolve_class_name ( parent_info -> ce , ZEND_TYPE_NAME ( parent_info -> type ) );
917
905
child_name = ZEND_TYPE_IS_CE (child_info -> type )
918
906
? ZEND_TYPE_CE (child_info -> type )-> name
919
- : zend_resolve_property_type ( ZEND_TYPE_NAME ( child_info -> type ), child_info -> ce );
907
+ : resolve_class_name ( child_info -> ce , ZEND_TYPE_NAME ( child_info -> type ) );
920
908
if (zend_string_equals_ci (parent_name , child_name )) {
921
- return 1 ;
909
+ return INHERITANCE_SUCCESS ;
922
910
}
923
911
924
912
/* Check for class aliases */
925
913
parent_type_ce = ZEND_TYPE_IS_CE (parent_info -> type )
926
914
? ZEND_TYPE_CE (parent_info -> type )
927
- : zend_lookup_class ( parent_name );
915
+ : lookup_class ( parent_info -> ce , parent_name );
928
916
child_type_ce = ZEND_TYPE_IS_CE (child_info -> type )
929
917
? ZEND_TYPE_CE (child_info -> type )
930
- : zend_lookup_class (child_name );
931
- return parent_type_ce && child_type_ce && parent_type_ce == child_type_ce ;
918
+ : lookup_class (child_info -> ce , child_name );
919
+ if (!parent_type_ce || !child_type_ce ) {
920
+ return INHERITANCE_UNRESOLVED ;
921
+ }
922
+ return parent_type_ce == child_type_ce ? INHERITANCE_SUCCESS : INHERITANCE_ERROR ;
923
+ }
924
+
925
+ static void emit_incompatible_property_error (
926
+ const zend_property_info * child , const zend_property_info * parent ) {
927
+ zend_error_noreturn (E_COMPILE_ERROR ,
928
+ "Type of %s::$%s must be %s%s (as in class %s)" ,
929
+ ZSTR_VAL (child -> ce -> name ),
930
+ ZSTR_VAL (child -> name ),
931
+ ZEND_TYPE_ALLOW_NULL (parent -> type ) ? "?" : "" ,
932
+ ZEND_TYPE_IS_CLASS (parent -> type )
933
+ ? ZSTR_VAL (ZEND_TYPE_IS_CE (parent -> type ) ? ZEND_TYPE_CE (parent -> type )-> name : resolve_class_name (parent -> ce , ZEND_TYPE_NAME (parent -> type )))
934
+ : zend_get_type_by_const (ZEND_TYPE_CODE (parent -> type )),
935
+ ZSTR_VAL (parent -> ce -> name ));
932
936
}
933
937
934
938
static void do_inherit_property (zend_property_info * parent_info , zend_string * key , zend_class_entry * ce ) /* {{{ */
@@ -962,16 +966,12 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
962
966
}
963
967
964
968
if (UNEXPECTED (ZEND_TYPE_IS_SET (parent_info -> type ))) {
965
- if (!property_types_compatible (parent_info , child_info )) {
966
- zend_error_noreturn (E_COMPILE_ERROR ,
967
- "Type of %s::$%s must be %s%s (as in class %s)" ,
968
- ZSTR_VAL (ce -> name ),
969
- ZSTR_VAL (key ),
970
- ZEND_TYPE_ALLOW_NULL (parent_info -> type ) ? "?" : "" ,
971
- ZEND_TYPE_IS_CLASS (parent_info -> type )
972
- ? ZSTR_VAL (ZEND_TYPE_IS_CE (parent_info -> type ) ? ZEND_TYPE_CE (parent_info -> type )-> name : zend_resolve_property_type (ZEND_TYPE_NAME (parent_info -> type ), parent_info -> ce ))
973
- : zend_get_type_by_const (ZEND_TYPE_CODE (parent_info -> type )),
974
- ZSTR_VAL (ce -> parent -> name ));
969
+ inheritance_status status = property_types_compatible (parent_info , child_info );
970
+ if (status == INHERITANCE_ERROR ) {
971
+ emit_incompatible_property_error (child_info , parent_info );
972
+ }
973
+ if (status == INHERITANCE_UNRESOLVED ) {
974
+ add_property_compatibility_obligation (ce , child_info , parent_info );
975
975
}
976
976
} else if (UNEXPECTED (ZEND_TYPE_IS_SET (child_info -> type ) && !ZEND_TYPE_IS_SET (parent_info -> type ))) {
977
977
zend_error_noreturn (E_COMPILE_ERROR ,
@@ -1958,7 +1958,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
1958
1958
1959
1959
if ((coliding_prop -> flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC ))
1960
1960
== (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC )) &&
1961
- property_types_compatible (property_info , coliding_prop )
1961
+ property_types_compatible (property_info , coliding_prop ) == INHERITANCE_SUCCESS
1962
1962
) {
1963
1963
/* the flags are identical, thus, the properties may be compatible */
1964
1964
zval * op1 , * op2 ;
@@ -2223,14 +2223,22 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
2223
2223
/* }}} */
2224
2224
2225
2225
typedef struct {
2226
- enum { OBLIGATION_DEPENDENCY , OBLIGATION_COMPATIBILITY } type ;
2226
+ enum {
2227
+ OBLIGATION_DEPENDENCY ,
2228
+ OBLIGATION_COMPATIBILITY ,
2229
+ OBLIGATION_PROPERTY_COMPATIBILITY
2230
+ } type ;
2227
2231
union {
2228
2232
zend_class_entry * dependency_ce ;
2229
2233
struct {
2230
2234
const zend_function * parent_fn ;
2231
2235
const zend_function * child_fn ;
2232
2236
zend_bool always_error ;
2233
2237
};
2238
+ struct {
2239
+ const zend_property_info * parent_prop ;
2240
+ const zend_property_info * child_prop ;
2241
+ };
2234
2242
};
2235
2243
} variance_obligation ;
2236
2244
@@ -2284,6 +2292,17 @@ static void add_compatibility_obligation(
2284
2292
zend_hash_next_index_insert_ptr (obligations , obligation );
2285
2293
}
2286
2294
2295
+ static void add_property_compatibility_obligation (
2296
+ zend_class_entry * ce , const zend_property_info * child_prop ,
2297
+ const zend_property_info * parent_prop ) {
2298
+ HashTable * obligations = get_or_init_obligations_for_class (ce );
2299
+ variance_obligation * obligation = emalloc (sizeof (variance_obligation ));
2300
+ obligation -> type = OBLIGATION_PROPERTY_COMPATIBILITY ;
2301
+ obligation -> child_prop = child_prop ;
2302
+ obligation -> parent_prop = parent_prop ;
2303
+ zend_hash_next_index_insert_ptr (obligations , obligation );
2304
+ }
2305
+
2287
2306
static void resolve_delayed_variance_obligations (zend_class_entry * ce );
2288
2307
2289
2308
static int check_variance_obligation (zval * zv ) {
@@ -2296,7 +2315,7 @@ static int check_variance_obligation(zval *zv) {
2296
2315
if (!(dependency_ce -> ce_flags & ZEND_ACC_LINKED )) {
2297
2316
return ZEND_HASH_APPLY_KEEP ;
2298
2317
}
2299
- } else {
2318
+ } else if ( obligation -> type == OBLIGATION_COMPATIBILITY ) {
2300
2319
zend_string * unresolved_class ;
2301
2320
inheritance_status status = zend_do_perform_implementation_check (
2302
2321
& unresolved_class , obligation -> child_fn , obligation -> parent_fn );
@@ -2311,6 +2330,17 @@ static int check_variance_obligation(zval *zv) {
2311
2330
obligation -> always_error );
2312
2331
}
2313
2332
/* Either the compatibility check was successful or only threw a warning. */
2333
+ } else {
2334
+ ZEND_ASSERT (obligation -> type == OBLIGATION_PROPERTY_COMPATIBILITY );
2335
+ inheritance_status status =
2336
+ property_types_compatible (obligation -> parent_prop , obligation -> child_prop );
2337
+ if (status != INHERITANCE_SUCCESS ) {
2338
+ if (status == INHERITANCE_UNRESOLVED ) {
2339
+ return ZEND_HASH_APPLY_KEEP ;
2340
+ }
2341
+ ZEND_ASSERT (status == INHERITANCE_ERROR );
2342
+ emit_incompatible_property_error (obligation -> child_prop , obligation -> parent_prop );
2343
+ }
2314
2344
}
2315
2345
return ZEND_HASH_APPLY_REMOVE ;
2316
2346
}
@@ -2363,16 +2393,19 @@ static void report_variance_errors(zend_class_entry *ce) {
2363
2393
inheritance_status status ;
2364
2394
zend_string * unresolved_class ;
2365
2395
2366
- /* There should not be any unresolved parents at this point. */
2367
- ZEND_ASSERT (obligation -> type == OBLIGATION_COMPATIBILITY );
2368
-
2369
- /* Just used to fetch the unresolved_class in this case. */
2370
- status = zend_do_perform_implementation_check (
2371
- & unresolved_class , obligation -> child_fn , obligation -> parent_fn );
2372
- ZEND_ASSERT (status == INHERITANCE_UNRESOLVED );
2373
- emit_incompatible_method_error_or_warning (
2374
- obligation -> child_fn , obligation -> parent_fn ,
2375
- status , unresolved_class , obligation -> always_error );
2396
+ if (obligation -> type == OBLIGATION_COMPATIBILITY ) {
2397
+ /* Just used to fetch the unresolved_class in this case. */
2398
+ status = zend_do_perform_implementation_check (
2399
+ & unresolved_class , obligation -> child_fn , obligation -> parent_fn );
2400
+ ZEND_ASSERT (status == INHERITANCE_UNRESOLVED );
2401
+ emit_incompatible_method_error_or_warning (
2402
+ obligation -> child_fn , obligation -> parent_fn ,
2403
+ status , unresolved_class , obligation -> always_error );
2404
+ } else if (obligation -> type == OBLIGATION_PROPERTY_COMPATIBILITY ) {
2405
+ emit_incompatible_property_error (obligation -> child_prop , obligation -> parent_prop );
2406
+ } else {
2407
+ zend_error_noreturn (E_CORE_ERROR , "Bug #78647" );
2408
+ }
2376
2409
} ZEND_HASH_FOREACH_END ();
2377
2410
2378
2411
/* Only warnings were thrown above -- that means that there are incompatibilities, but only
@@ -2483,6 +2516,7 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
2483
2516
inheritance_status ret = INHERITANCE_SUCCESS ;
2484
2517
zend_string * key ;
2485
2518
zend_function * parent_func ;
2519
+ zend_property_info * parent_info ;
2486
2520
2487
2521
ZEND_HASH_FOREACH_STR_KEY_PTR (& parent_ce -> function_table , key , parent_func ) {
2488
2522
zval * zv = zend_hash_find_ex (& ce -> function_table , key , 1 );
@@ -2501,6 +2535,28 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
2501
2535
}
2502
2536
} ZEND_HASH_FOREACH_END ();
2503
2537
2538
+ ZEND_HASH_FOREACH_STR_KEY_PTR (& parent_ce -> properties_info , key , parent_info ) {
2539
+ zval * zv ;
2540
+ if ((parent_info -> flags & ZEND_ACC_PRIVATE ) || !ZEND_TYPE_IS_SET (parent_info -> type )) {
2541
+ continue ;
2542
+ }
2543
+
2544
+ zv = zend_hash_find_ex (& ce -> properties_info , key , 1 );
2545
+ if (zv ) {
2546
+ zend_property_info * child_info = Z_PTR_P (zv );
2547
+ if (ZEND_TYPE_IS_SET (child_info -> type )) {
2548
+ inheritance_status status = property_types_compatible (parent_info , child_info );
2549
+ if (UNEXPECTED (status != INHERITANCE_SUCCESS )) {
2550
+ if (EXPECTED (status == INHERITANCE_UNRESOLVED )) {
2551
+ return INHERITANCE_UNRESOLVED ;
2552
+ }
2553
+ ZEND_ASSERT (status == INHERITANCE_ERROR );
2554
+ ret = INHERITANCE_ERROR ;
2555
+ }
2556
+ }
2557
+ }
2558
+ } ZEND_HASH_FOREACH_END ();
2559
+
2504
2560
return ret ;
2505
2561
}
2506
2562
/* }}} */
0 commit comments