@@ -8963,17 +8963,97 @@ static void zend_compile_implements(zend_ast *ast) /* {{{ */
89638963 zend_class_name * interface_names ;
89648964 uint32_t i ;
89658965
8966- interface_names = emalloc (sizeof (zend_class_name ) * list -> children );
8966+ // Attribute validators run before `implements` parts are compiled, an
8967+ // internal attribute from an extension might have added an interface
8968+ // already so we cannot assume that the list of interfaces is empty.
8969+ // See GH-22354. The attribute validator might have also added an interface
8970+ // that the user is trying to manually implement, skip those since otherwise
8971+ // there are errors about trying to implement an interface that was already
8972+ // implemented.
8973+ if (EXPECTED (ce -> num_interfaces == 0 )) {
8974+ interface_names = emalloc (sizeof (zend_class_name ) * list -> children );
8975+ for (i = 0 ; i < list -> children ; ++ i ) {
8976+ zend_ast * class_ast = list -> child [i ];
8977+ interface_names [i ].name =
8978+ zend_resolve_const_class_name_reference (class_ast , "interface name" );
8979+ interface_names [i ].lc_name = zend_string_tolower (interface_names [i ].name );
8980+ }
8981+
8982+ ce -> num_interfaces = list -> children ;
8983+ ce -> interface_names = interface_names ;
8984+ return ;
8985+ }
89678986
8987+ // Figure out which interfaces in the list should be skipped; first, resolve
8988+ // the names
8989+ // BUT, only skip the *first* usage of an interface in the manual `implements`
8990+ // part, if an interface is added by an attribute but also manually declared
8991+ // twice it should still be warned about
8992+ zend_string * * to_add_names = safe_emalloc (list -> children , sizeof (* to_add_names ), 0 );
8993+ zend_string * * skipped_names = safe_emalloc (list -> children , sizeof (* skipped_names ), 0 );
8994+ uint32_t to_add_count = 0 ;
8995+ uint32_t skipped_count = 0 ;
89688996 for (i = 0 ; i < list -> children ; ++ i ) {
89698997 zend_ast * class_ast = list -> child [i ];
8970- interface_names [i ].name =
8971- zend_resolve_const_class_name_reference (class_ast , "interface name" );
8972- interface_names [i ].lc_name = zend_string_tolower (interface_names [i ].name );
8998+ zend_string * name = zend_resolve_const_class_name_reference (class_ast , "interface name" );
8999+ bool already_added = false;
9000+ for (uint32_t idx = 0 ; idx < ce -> num_interfaces ; ++ idx ) {
9001+ if (zend_string_equals_ci (name , ce -> interface_names [idx ].name )) {
9002+ already_added = true;
9003+ break ;
9004+ }
9005+ }
9006+ if (already_added ) {
9007+ // Did we already skip this interface name once?
9008+ bool already_skipped = false;
9009+ for (uint32_t idx = 0 ; idx < skipped_count ; ++ idx ) {
9010+ if (zend_string_equals_ci (name , skipped_names [idx ])) {
9011+ already_skipped = true;
9012+ break ;
9013+ }
9014+ }
9015+ if (already_skipped ) {
9016+ to_add_names [i ] = name ;
9017+ to_add_count ++ ;
9018+ } else {
9019+ to_add_names [i ] = NULL ;
9020+ skipped_names [i ] = name ;
9021+ skipped_count ++ ;
9022+ }
9023+ } else {
9024+ to_add_names [i ] = name ;
9025+ to_add_count ++ ;
9026+ }
9027+ }
9028+ ZEND_ASSERT (skipped_count + to_add_count == list -> children );
9029+
9030+ // Free the skipped names
9031+ for (uint32_t idx = 0 ; idx < skipped_count ; ++ idx ) {
9032+ zend_string_release (skipped_names [idx ]);
9033+ }
9034+ efree (skipped_names );
9035+
9036+ const uint32_t initial_count = ce -> num_interfaces ;
9037+ interface_names = safe_erealloc (ce -> interface_names , (initial_count + to_add_count ), sizeof (* interface_names ), 0 );
9038+
9039+ uint32_t added_count = 0 ;
9040+ for (i = 0 ; i < list -> children ; ++ i ) {
9041+ zend_string * name = to_add_names [i ];
9042+ if (name == NULL ) {
9043+ // This was one of the names that was already added by a validator
9044+ continue ;
9045+ }
9046+ interface_names [initial_count + added_count ].name = name ;
9047+ interface_names [initial_count + added_count ].lc_name = zend_string_tolower (name );
9048+ // To make it clear that the to_add_names no longer owns the reference
9049+ to_add_names [i ] = NULL ;
9050+ added_count += 1 ;
89739051 }
9052+ ZEND_ASSERT (added_count == to_add_count );
89749053
8975- ce -> num_interfaces = list -> children ;
9054+ ce -> num_interfaces = initial_count + added_count ;
89769055 ce -> interface_names = interface_names ;
9056+ efree (to_add_names );
89779057}
89789058/* }}} */
89799059
0 commit comments