@@ -76,6 +76,8 @@ pub fn parser(mut input: ItemStruct) -> Result<TokenStream> {
7676#[ darling( attributes( php) , forward_attrs( doc) , default ) ]
7777struct PropAttributes {
7878 prop : Flag ,
79+ #[ darling( rename = "static" ) ]
80+ static_ : Flag ,
7981 #[ darling( flatten) ]
8082 rename : PhpRename ,
8183 flags : Option < Expr > ,
@@ -114,6 +116,10 @@ impl Property<'_> {
114116 . rename
115117 . rename ( self . ident . to_string ( ) , RenameRule :: Camel )
116118 }
119+
120+ pub fn is_static ( & self ) -> bool {
121+ self . attr . static_ . is_present ( )
122+ }
117123}
118124
119125/// Generates an implementation of `RegisteredClass` for struct `ident`.
@@ -130,9 +136,14 @@ fn generate_registered_class_impl(
130136) -> TokenStream {
131137 let modifier = modifier. option_tokens ( ) ;
132138
133- let fields = fields. iter ( ) . map ( |prop| {
139+ // Separate instance properties from static properties
140+ let ( instance_props, static_props) : ( Vec < _ > , Vec < _ > ) =
141+ fields. iter ( ) . partition ( |prop| !prop. is_static ( ) ) ;
142+
143+ // Generate instance properties (with Rust handlers)
144+ let instance_fields = instance_props. iter ( ) . map ( |prop| {
134145 let name = prop. name ( ) ;
135- let ident = prop. ident ;
146+ let field_ident = prop. ident ;
136147 let flags = prop
137148 . attr
138149 . flags
@@ -143,13 +154,33 @@ fn generate_registered_class_impl(
143154
144155 quote ! {
145156 ( #name, :: ext_php_rs:: internal:: property:: PropertyInfo {
146- prop: :: ext_php_rs:: props:: Property :: field( |this: & mut Self | & mut this. #ident ) ,
157+ prop: :: ext_php_rs:: props:: Property :: field( |this: & mut Self | & mut this. #field_ident ) ,
147158 flags: #flags,
148159 docs: & [ #( #docs, ) * ]
149160 } )
150161 }
151162 } ) ;
152163
164+ // Generate static properties (PHP-managed, no Rust handlers)
165+ // We combine the base flags with Static flag using from_bits_retain which is const
166+ let static_fields = static_props. iter ( ) . map ( |prop| {
167+ let name = prop. name ( ) ;
168+ let base_flags = prop
169+ . attr
170+ . flags
171+ . as_ref ( )
172+ . map ( ToTokens :: to_token_stream)
173+ . unwrap_or ( quote ! { :: ext_php_rs:: flags:: PropertyFlags :: Public } ) ;
174+ let docs = & prop. docs ;
175+
176+ // Use from_bits_retain to combine flags in a const context
177+ quote ! {
178+ ( #name, :: ext_php_rs:: flags:: PropertyFlags :: from_bits_retain(
179+ ( #base_flags) . bits( ) | :: ext_php_rs:: flags:: PropertyFlags :: Static . bits( )
180+ ) , & [ #( #docs, ) * ] as & [ & str ] )
181+ }
182+ } ) ;
183+
153184 let flags = match flags {
154185 Some ( flags) => flags. to_token_stream ( ) ,
155186 None => quote ! { :: ext_php_rs:: flags:: ClassFlags :: empty( ) } . to_token_stream ( ) ,
@@ -204,10 +235,16 @@ fn generate_registered_class_impl(
204235 > {
205236 use :: std:: iter:: FromIterator ;
206237 :: std:: collections:: HashMap :: from_iter( [
207- #( #fields , ) *
238+ #( #instance_fields , ) *
208239 ] )
209240 }
210241
242+ #[ must_use]
243+ fn static_properties( ) -> & ' static [ ( & ' static str , :: ext_php_rs:: flags:: PropertyFlags , & ' static [ & ' static str ] ) ] {
244+ static STATIC_PROPS : & [ ( & str , :: ext_php_rs:: flags:: PropertyFlags , & [ & str ] ) ] = & [ #( #static_fields, ) * ] ;
245+ STATIC_PROPS
246+ }
247+
211248 #[ inline]
212249 fn method_builders( ) -> :: std:: vec:: Vec <
213250 ( :: ext_php_rs:: builders:: FunctionBuilder <' static >, :: ext_php_rs:: flags:: MethodFlags )
0 commit comments