Skip to content

Commit c318691

Browse files
committed
Refactor
1 parent e1d8606 commit c318691

File tree

1 file changed

+71
-69
lines changed

1 file changed

+71
-69
lines changed

Zend/zend_inheritance.c

Lines changed: 71 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
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

227228
static
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

247248
static
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

293326
static
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

324343
static
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

Comments
 (0)