@@ -495,8 +495,9 @@ static inheritance_status zend_do_perform_arg_type_hint_check(
495495static inheritance_status zend_do_perform_implementation_check (
496496 const zend_function * fe , const zend_function * proto ) /* {{{ */
497497{
498- uint32_t i , num_args ;
498+ uint32_t i , num_args , proto_num_args , fe_num_args ;
499499 inheritance_status status , local_status ;
500+ zend_bool proto_is_variadic , fe_is_variadic ;
500501
501502 /* If it's a user function then arg_info == NULL means we don't have any parameters but
502503 * we still need to do the arg number checks. We are only willing to ignore this for internal
@@ -516,9 +517,8 @@ static inheritance_status zend_do_perform_implementation_check(
516517 /* If the prototype method is private do not enforce a signature */
517518 ZEND_ASSERT (!(proto -> common .fn_flags & ZEND_ACC_PRIVATE ));
518519
519- /* check number of arguments */
520- if (proto -> common .required_num_args < fe -> common .required_num_args
521- || proto -> common .num_args > fe -> common .num_args ) {
520+ /* The number of required arguments cannot increase. */
521+ if (proto -> common .required_num_args < fe -> common .required_num_args ) {
522522 return INHERITANCE_ERROR ;
523523 }
524524
@@ -528,35 +528,36 @@ static inheritance_status zend_do_perform_implementation_check(
528528 return INHERITANCE_ERROR ;
529529 }
530530
531- if ((proto -> common .fn_flags & ZEND_ACC_VARIADIC )
532- && !(fe -> common .fn_flags & ZEND_ACC_VARIADIC )) {
531+ proto_is_variadic = (proto -> common .fn_flags & ZEND_ACC_VARIADIC ) != 0 ;
532+ fe_is_variadic = (fe -> common .fn_flags & ZEND_ACC_VARIADIC ) != 0 ;
533+
534+ /* A variadic function cannot become non-variadic */
535+ if (proto_is_variadic && !fe_is_variadic ) {
533536 return INHERITANCE_ERROR ;
534537 }
535538
536- /* For variadic functions any additional (optional) arguments that were added must be
537- * checked against the signature of the variadic argument, so in this case we have to
538- * go through all the parameters of the function and not just those present in the
539- * prototype. */
540- num_args = proto -> common .num_args ;
541- if (proto -> common .fn_flags & ZEND_ACC_VARIADIC ) {
542- num_args ++ ;
543- if (fe -> common .num_args >= proto -> common .num_args ) {
544- num_args = fe -> common .num_args ;
545- if (fe -> common .fn_flags & ZEND_ACC_VARIADIC ) {
546- num_args ++ ;
547- }
548- }
549- }
539+ /* The variadic argument is not included in the stored argument count. */
540+ proto_num_args = proto -> common .num_args + proto_is_variadic ;
541+ fe_num_args = fe -> common .num_args + fe_is_variadic ;
542+ num_args = MAX (proto_num_args , fe_num_args );
550543
551544 status = INHERITANCE_SUCCESS ;
552545 for (i = 0 ; i < num_args ; i ++ ) {
553- zend_arg_info * fe_arg_info = & fe -> common .arg_info [i ];
554-
555- zend_arg_info * proto_arg_info ;
556- if (i < proto -> common .num_args ) {
557- proto_arg_info = & proto -> common .arg_info [i ];
558- } else {
559- proto_arg_info = & proto -> common .arg_info [proto -> common .num_args ];
546+ zend_arg_info * proto_arg_info =
547+ i < proto_num_args ? & proto -> common .arg_info [i ] :
548+ proto_is_variadic ? & proto -> common .arg_info [proto_num_args - 1 ] : NULL ;
549+ zend_arg_info * fe_arg_info =
550+ i < fe_num_args ? & fe -> common .arg_info [i ] :
551+ fe_is_variadic ? & fe -> common .arg_info [fe_num_args - 1 ] : NULL ;
552+ if (!proto_arg_info ) {
553+ /* A new (optional) argument has been added, which is fine. */
554+ continue ;
555+ }
556+ if (!fe_arg_info ) {
557+ /* An argument has been removed. This is considered illegal, because arity checks
558+ * work based on a model where passing more than the declared number of parameters
559+ * to a function is an error. */
560+ return INHERITANCE_ERROR ;
560561 }
561562
562563 local_status = zend_do_perform_arg_type_hint_check (fe , fe_arg_info , proto , proto_arg_info );
0 commit comments