2222#include "zend_compile.h"
2323#include "zend_execute.h"
2424#include "zend_inheritance.h"
25+ #include "zend_interfaces.h"
2526#include "zend_smart_str.h"
2627#include "zend_operators.h"
2728
@@ -225,7 +226,7 @@ typedef enum {
225226} _variance ;
226227
227228static
228- int _check_inherited_type_constraint (
229+ int _check_zce_variance (
229230 const zend_class_entry * ce1 , const zend_class_entry * ce2 ,
230231 _variance variance ) /* {{{ */
231232{
@@ -245,53 +246,85 @@ int _check_inherited_type_constraint(
245246}
246247
247248static
248- int _do_perform_type_hint_check (
249+ int _check_inherited_arg_info (
249250 const zend_function * fe , zend_arg_info * fe_arg_info ,
250251 const zend_function * proto , zend_arg_info * proto_arg_info ,
251252 _variance variance ) /* {{{ */
252253{
253254 zend_type fe_type = fe_arg_info -> type ;
254255 zend_type proto_type = proto_arg_info -> type ;
256+ zend_long fe_type_code = ZEND_TYPE_CODE (fe_type );
257+ zend_long proto_type_code = ZEND_TYPE_CODE (proto_type );
258+
255259 ZEND_ASSERT (ZEND_TYPE_IS_SET (fe_type ));
256260 ZEND_ASSERT (ZEND_TYPE_IS_SET (proto_type ));
257261
258- if (ZEND_TYPE_IS_CLASS (fe_type ) && ZEND_TYPE_IS_CLASS (proto_type )) {
262+ if (variance == COVARIANT && ZEND_TYPE_ALLOW_NULL (fe_type ) && !ZEND_TYPE_ALLOW_NULL (proto_type )) {
263+ return 0 ;
264+ }
265+
266+ // This introduces BC break described at https://bugs.php.net/bug.php?id=72119
267+ if (variance == CONTRAVARIANT && ZEND_TYPE_ALLOW_NULL (proto_type ) && !ZEND_TYPE_ALLOW_NULL (fe_type )) {
268+ return 0 ;
269+ }
270+
271+ if (ZEND_TYPE_IS_CLASS (fe_type )) {
259272 zend_string * fe_class_name =
260273 _resolve_parent_and_self (fe , ZEND_TYPE_NAME (fe_type ));
261- zend_string * proto_class_name =
262- _resolve_parent_and_self (proto , ZEND_TYPE_NAME (proto_type ));
263274 int code = 1 ;
275+ if (ZEND_TYPE_IS_CLASS (proto_type )) {
276+ zend_string * proto_class_name =
277+ _resolve_parent_and_self (proto , ZEND_TYPE_NAME (proto_type ));
264278
265- if (!zend_string_equals_ci (fe_class_name , proto_class_name )) {
266- if (fe -> common .type == ZEND_USER_FUNCTION ) {
267- zend_class_entry * fe_ce = zend_lookup_class (fe_class_name );
268- zend_class_entry * proto_ce = zend_lookup_class (proto_class_name );
279+ if (!zend_string_equals_ci (fe_class_name , proto_class_name )) {
280+ if (fe -> common .type == ZEND_USER_FUNCTION ) {
281+ zend_class_entry * fe_ce = zend_lookup_class (fe_class_name );
282+ zend_class_entry * proto_ce = zend_lookup_class (proto_class_name );
269283
270- if (fe_ce && proto_ce ) {
271- code = _check_inherited_type_constraint (fe_ce , proto_ce , variance );
284+ if (fe_ce && proto_ce ) {
285+ code = _check_zce_variance (fe_ce , proto_ce , variance );
286+ } else {
287+ /* todo: delay to runtime. How? */
288+ /* Don't break bug62441! */
289+ code = 0 ;
290+ }
272291 } else {
273- /* todo: delay to runtime. How? */
274- /* Don't break bug62441! */
275292 code = 0 ;
276293 }
277- } else {
278- code = 0 ;
279294 }
295+ zend_string_release (proto_class_name );
296+ } else if (proto_type_code == IS_ITERABLE && variance == COVARIANT ) {
297+ zend_class_entry * fe_ce = zend_lookup_class (fe_class_name );
298+ code = fe_ce && instanceof_function (fe_ce , zend_ce_traversable );
299+ } else {
300+ code = 0 ;
280301 }
281- zend_string_release ( proto_class_name );
302+
282303 zend_string_release (fe_class_name );
283304 return code ;
284- } else if (ZEND_TYPE_CODE (fe_type ) != ZEND_TYPE_CODE (proto_type )) {
285- /* Incompatible built-in types */
286- return 0 ;
305+ } else if (ZEND_TYPE_IS_CLASS (proto_type )) {
306+ if (variance == CONTRAVARIANT && fe_type_code == IS_ITERABLE ) {
307+ zend_string * proto_class_name =
308+ _resolve_parent_and_self (proto , ZEND_TYPE_NAME (proto_type ));
309+ zend_class_entry * proto_ce = zend_lookup_class (proto_class_name );
310+ zend_string_release (proto_class_name );
311+ return proto_ce && instanceof_function (proto_ce , zend_ce_traversable );
312+ } else {
313+ return 0 ;
314+ }
315+ } else if (fe_type_code == IS_ITERABLE || proto_type_code == IS_ITERABLE ) {
316+ return (variance == COVARIANT && fe_type_code == IS_ARRAY )
317+ || (variance == CONTRAVARIANT && proto_type_code == IS_ARRAY );
318+ } else if (fe_type_code == proto_type_code ) {
319+ return 1 ;
287320 }
288321
289- return 1 ;
322+ return 0 ;
290323}
291324/* }}} */
292325
293326static
294- int _check_inherited_return_type_constraint (
327+ int _check_inherited_return_type (
295328 const zend_function * fe , zend_arg_info * fe_arg_info ,
296329 const zend_function * proto , zend_arg_info * proto_arg_info ) /* {{{ */
297330{
@@ -300,32 +333,24 @@ int _check_inherited_return_type_constraint(
300333 return 0 ;
301334 }
302335
303- if (!_do_perform_type_hint_check (fe , fe_arg_info , proto , proto_arg_info , COVARIANT )) {
304- switch (ZEND_TYPE_CODE (proto_arg_info -> type )) {
305- case IS_ITERABLE :
306- if (!zend_iterable_compatibility_check (fe_arg_info )) {
307- return 0 ;
308- }
309- break ;
310-
311- default :
312- return 0 ;
313- }
314- }
315-
316- /* Adding nullability is not valid */
317- if (ZEND_TYPE_ALLOW_NULL (fe_arg_info -> type ) && !ZEND_TYPE_ALLOW_NULL (proto_arg_info -> type )) {
336+ if (!_check_inherited_arg_info (fe , fe_arg_info , proto , proto_arg_info , COVARIANT )) {
318337 return 0 ;
319338 }
320339
321340 return 1 ;
322341}
323342
324343static
325- int zend_do_perform_arg_type_hint_check (
344+ int _check_inherited_parameter_type (
326345 const zend_function * fe , zend_arg_info * fe_arg_info ,
327346 const zend_function * proto , zend_arg_info * proto_arg_info ) /* {{{ */
328347{
348+
349+ /* by-ref constraints on arguments are invariant */
350+ if (fe_arg_info -> pass_by_reference != proto_arg_info -> pass_by_reference ) {
351+ return 0 ;
352+ }
353+
329354 if (!ZEND_TYPE_IS_SET (fe_arg_info -> type )) {
330355 /* Child with no type is always compatible */
331356 return 1 ;
@@ -336,7 +361,7 @@ int zend_do_perform_arg_type_hint_check(
336361 return 0 ;
337362 }
338363
339- return _do_perform_type_hint_check (fe , fe_arg_info , proto , proto_arg_info , CONTRAVARIANT );
364+ return _check_inherited_arg_info (fe , fe_arg_info , proto , proto_arg_info , CONTRAVARIANT );
340365}
341366/* }}} */
342367
@@ -390,7 +415,7 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
390415 num_args = proto -> common .num_args ;
391416 if (proto -> common .fn_flags & ZEND_ACC_VARIADIC ) {
392417 num_args ++ ;
393- if (fe -> common .num_args >= proto -> common .num_args ) {
418+ if (fe -> common .num_args >= proto -> common .num_args ) {
394419 num_args = fe -> common .num_args ;
395420 if (fe -> common .fn_flags & ZEND_ACC_VARIADIC ) {
396421 num_args ++ ;
@@ -400,43 +425,20 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
400425
401426 for (i = 0 ; i < num_args ; i ++ ) {
402427 zend_arg_info * fe_arg_info = & fe -> common .arg_info [i ];
428+ zend_arg_info * proto_arg_info = (i < proto -> common .num_args )
429+ ? & proto -> common .arg_info [i ]
430+ : & proto -> common .arg_info [proto -> common .num_args ];
403431
404- zend_arg_info * proto_arg_info ;
405- if (i < proto -> common .num_args ) {
406- proto_arg_info = & proto -> common .arg_info [i ];
407- } else {
408- proto_arg_info = & proto -> common .arg_info [proto -> common .num_args ];
409- }
410-
411- if (!zend_do_perform_arg_type_hint_check (fe , fe_arg_info , proto , proto_arg_info )) {
412- switch (ZEND_TYPE_CODE (fe_arg_info -> type )) {
413- case IS_ITERABLE :
414- if (!zend_iterable_compatibility_check (proto_arg_info )) {
415- return 0 ;
416- }
417- break ;
418-
419- default :
420- return 0 ;
421- }
422- }
423-
424- // This introduces BC break described at https://bugs.php.net/bug.php?id=72119
425- if (ZEND_TYPE_IS_SET (proto_arg_info -> type ) && ZEND_TYPE_ALLOW_NULL (proto_arg_info -> type ) && !ZEND_TYPE_ALLOW_NULL (fe_arg_info -> type )) {
426- /* incompatible nullability */
427- return 0 ;
428- }
429-
430- /* by-ref constraints on arguments are invariant */
431- if (fe_arg_info -> pass_by_reference != proto_arg_info -> pass_by_reference ) {
432+ if (!_check_inherited_parameter_type (fe , fe_arg_info , proto , proto_arg_info )) {
432433 return 0 ;
433434 }
434435 }
435436
436437 /* Check return type compatibility, but only if the prototype already specifies
437438 * a return type. Adding a new return type is always valid. */
438439 if (proto -> common .fn_flags & ZEND_ACC_HAS_RETURN_TYPE ) {
439- return _check_inherited_return_type_constraint (fe , fe -> common .arg_info - 1 , proto , proto -> common .arg_info - 1 );
440+ return _check_inherited_return_type (
441+ fe , fe -> common .arg_info - 1 , proto , proto -> common .arg_info - 1 );
440442 }
441443 return 1 ;
442444}
@@ -687,7 +689,7 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
687689 error_level = E_COMPILE_ERROR ;
688690 error_verb = "must" ;
689691 } else if ((parent -> common .fn_flags & ZEND_ACC_HAS_RETURN_TYPE ) &&
690- !_check_inherited_return_type_constraint (child , child -> common .arg_info - 1 , parent , parent -> common .arg_info - 1 )) {
692+ !_check_inherited_return_type (child , child -> common .arg_info - 1 , parent , parent -> common .arg_info - 1 )) {
691693 error_level = E_COMPILE_ERROR ;
692694 error_verb = "must" ;
693695 } else {
0 commit comments