@@ -8,7 +8,7 @@ use darling::FromAttributes;
88use darling:: util:: Flag ;
99use proc_macro2:: TokenStream ;
1010use quote:: { ToTokens , format_ident, quote} ;
11- use syn:: { Expr , Ident , ItemTrait , Path , TraitItem , TraitItemConst , TraitItemFn } ;
11+ use syn:: { Expr , Ident , ItemTrait , Path , TraitItem , TraitItemConst , TraitItemFn , TypeParamBound } ;
1212
1313use crate :: impl_:: { FnBuilder , MethodModifier } ;
1414use crate :: parsing:: { PhpRename , RenameRule , Visibility } ;
@@ -45,11 +45,36 @@ trait Parse<'a, T> {
4545 fn parse ( & ' a mut self ) -> Result < T > ;
4646}
4747
48+ /// Represents a supertrait that should be converted to an interface extension.
49+ /// These are automatically detected from Rust trait bounds (e.g., `trait Foo:
50+ /// Bar`).
51+ struct SupertraitInterface {
52+ /// The name of the supertrait's PHP interface struct (e.g.,
53+ /// `PhpInterfaceBar`)
54+ interface_struct_name : Ident ,
55+ }
56+
57+ impl ToTokens for SupertraitInterface {
58+ fn to_tokens ( & self , tokens : & mut TokenStream ) {
59+ let interface_struct_name = & self . interface_struct_name ;
60+ quote ! {
61+ (
62+ || <#interface_struct_name as :: ext_php_rs:: class:: RegisteredClass >:: get_metadata( ) . ce( ) ,
63+ <#interface_struct_name as :: ext_php_rs:: class:: RegisteredClass >:: CLASS_NAME
64+ )
65+ }
66+ . to_tokens ( tokens) ;
67+ }
68+ }
69+
4870struct InterfaceData < ' a > {
4971 ident : & ' a Ident ,
5072 name : String ,
5173 path : Path ,
74+ /// Extends from `#[php(extends(...))]` attributes
5275 extends : Vec < ClassEntryAttribute > ,
76+ /// Extends from Rust trait bounds (supertraits)
77+ supertrait_extends : Vec < SupertraitInterface > ,
5378 constructor : Option < Function < ' a > > ,
5479 methods : Vec < FnBuilder > ,
5580 constants : Vec < Constant < ' a > > ,
@@ -62,6 +87,7 @@ impl ToTokens for InterfaceData<'_> {
6287 let interface_name = format_ident ! ( "{INTERNAL_INTERFACE_NAME_PREFIX}{}" , self . ident) ;
6388 let name = & self . name ;
6489 let implements = & self . extends ;
90+ let supertrait_implements = & self . supertrait_extends ;
6591 let methods_sig = & self . methods ;
6692 let constants = & self . constants ;
6793 let docs = & self . docs ;
@@ -86,8 +112,10 @@ impl ToTokens for InterfaceData<'_> {
86112
87113 const FLAGS : :: ext_php_rs:: flags:: ClassFlags = :: ext_php_rs:: flags:: ClassFlags :: Interface ;
88114
115+ // Interface inheritance from both explicit #[php(extends(...))] and Rust trait bounds
89116 const IMPLEMENTS : & ' static [ :: ext_php_rs:: class:: ClassEntryInfo ] = & [
90117 #( #implements, ) *
118+ #( #supertrait_implements, ) *
91119 ] ;
92120
93121 const DOC_COMMENTS : & ' static [ & ' static str ] = & [
@@ -202,11 +230,16 @@ impl<'a> Parse<'a, InterfaceData<'a>> for ItemTrait {
202230 let interface_name = format_ident ! ( "{INTERNAL_INTERFACE_NAME_PREFIX}{ident}" ) ;
203231 let ts = quote ! { #interface_name } ;
204232 let path: Path = syn:: parse2 ( ts) ?;
233+
234+ // Parse supertraits to automatically generate interface inheritance
235+ let supertrait_extends = parse_supertraits ( & self . supertraits ) ;
236+
205237 let mut data = InterfaceData {
206238 ident,
207239 name,
208240 path,
209241 extends : attrs. extends ,
242+ supertrait_extends,
210243 constructor : None ,
211244 methods : Vec :: default ( ) ,
212245 constants : Vec :: default ( ) ,
@@ -234,6 +267,31 @@ impl<'a> Parse<'a, InterfaceData<'a>> for ItemTrait {
234267 }
235268}
236269
270+ /// Parses the supertraits of a trait definition and converts them to interface
271+ /// extensions. For a trait like `trait Foo: Bar + Baz`, this will generate
272+ /// references to `PhpInterfaceBar` and `PhpInterfaceBaz`.
273+ fn parse_supertraits (
274+ supertraits : & syn:: punctuated:: Punctuated < TypeParamBound , syn:: token:: Plus > ,
275+ ) -> Vec < SupertraitInterface > {
276+ supertraits
277+ . iter ( )
278+ . filter_map ( |bound| {
279+ if let TypeParamBound :: Trait ( trait_bound) = bound {
280+ // Get the last segment of the trait path (the trait name)
281+ let trait_name = trait_bound. path . segments . last ( ) ?;
282+ // Generate the PHP interface struct name
283+ let interface_struct_name =
284+ format_ident ! ( "{}{}" , INTERNAL_INTERFACE_NAME_PREFIX , trait_name. ident) ;
285+ Some ( SupertraitInterface {
286+ interface_struct_name,
287+ } )
288+ } else {
289+ None
290+ }
291+ } )
292+ . collect ( )
293+ }
294+
237295#[ derive( FromAttributes , Default , Debug ) ]
238296#[ darling( default , attributes( php) , forward_attrs( doc) ) ]
239297pub struct PhpFunctionInterfaceAttribute {
0 commit comments