@@ -42,18 +42,21 @@ pub struct ParsedMethod {
4242 pub tokens : TokenStream ,
4343 pub method : Method ,
4444 pub property : Option < ( String , PropAttrTy ) > ,
45+ pub constructor : bool ,
4546}
4647
4748impl ParsedMethod {
4849 pub fn new (
4950 tokens : TokenStream ,
5051 method : Method ,
5152 property : Option < ( String , PropAttrTy ) > ,
53+ constructor : bool ,
5254 ) -> Self {
5355 Self {
5456 tokens,
5557 method,
5658 property,
59+ constructor,
5760 }
5861 }
5962}
@@ -64,6 +67,7 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result<Par
6467 let mut visibility = Visibility :: Public ;
6568 let mut as_prop = None ;
6669 let mut identifier = None ;
70+ let mut is_constructor = false ;
6771
6872 for attr in input. attrs . iter ( ) {
6973 match parse_attribute ( attr) ? {
@@ -89,6 +93,7 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result<Par
8993 } ) ;
9094 as_prop = Some ( ( prop_name, ty) )
9195 }
96+ ParsedAttribute :: Constructor => is_constructor = true ,
9297 }
9398 }
9499
@@ -102,6 +107,20 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result<Par
102107 ..
103108 } = & sig;
104109
110+ let name = identifier. unwrap_or_else ( || rename_rule. rename ( ident. to_string ( ) ) ) ;
111+ if name == "__construct" {
112+ is_constructor = true ;
113+ }
114+
115+ if is_constructor && ( !matches ! ( visibility, Visibility :: Public ) || as_prop. is_some ( ) ) {
116+ bail ! ( "`#[constructor]` attribute cannot be combined with the visibility or getter/setter attributes." ) ;
117+ }
118+
119+ let bail = if is_constructor {
120+ quote ! { return ConstructorResult :: ArgError ; }
121+ } else {
122+ quote ! { return ; }
123+ } ;
105124 let internal_ident = Ident :: new ( & format ! ( "_internal_php_{}" , ident) , Span :: call_site ( ) ) ;
106125 let args = build_args ( inputs, & defaults) ?;
107126 let optional = function:: find_optional_parameter (
@@ -112,34 +131,55 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result<Par
112131 optional,
113132 ) ;
114133 let ( arg_definitions, is_static) = build_arg_definitions ( & args) ;
115- let arg_parser = build_arg_parser ( args. iter ( ) , & optional) ?;
116- let arg_accessors = build_arg_accessors ( & args) ;
134+ let arg_parser = build_arg_parser ( args. iter ( ) , & optional, & bail ) ?;
135+ let arg_accessors = build_arg_accessors ( & args, & bail ) ;
117136 let this = if is_static {
118137 quote ! { Self :: }
119138 } else {
120139 quote ! { this. }
121140 } ;
122141
123- let func = quote ! {
124- #input
142+ let func = if is_constructor {
143+ quote ! {
144+ #input
145+
146+ #[ doc( hidden) ]
147+ pub fn #internal_ident(
148+ ex: & mut :: ext_php_rs:: php:: execution_data:: ExecutionData
149+ ) -> :: ext_php_rs:: php:: types:: object:: ConstructorResult <Self > {
150+ use :: ext_php_rs:: php:: types:: zval:: IntoZval ;
151+ use :: ext_php_rs:: php:: types:: object:: ConstructorResult ;
152+
153+ #( #arg_definitions) *
154+ #arg_parser
155+
156+ Self :: #ident( #( #arg_accessors, ) * ) . into( )
157+ }
158+ }
159+ } else {
160+ quote ! {
161+ #input
125162
126- #[ doc( hidden) ]
127- pub extern "C" fn #internal_ident( ex: & mut :: ext_php_rs:: php:: execution_data:: ExecutionData , retval: & mut :: ext_php_rs:: php:: types:: zval:: Zval ) {
128- use :: ext_php_rs:: php:: types:: zval:: IntoZval ;
163+ #[ doc( hidden) ]
164+ pub extern "C" fn #internal_ident(
165+ ex: & mut :: ext_php_rs:: php:: execution_data:: ExecutionData ,
166+ retval: & mut :: ext_php_rs:: php:: types:: zval:: Zval
167+ ) {
168+ use :: ext_php_rs:: php:: types:: zval:: IntoZval ;
129169
130- #( #arg_definitions) *
131- #arg_parser
170+ #( #arg_definitions) *
171+ #arg_parser
132172
133- let result = #this #ident( #( #arg_accessors, ) * ) ;
173+ let result = #this #ident( #( #arg_accessors, ) * ) ;
134174
135- if let Err ( e) = result. set_zval( retval, false ) {
136- let e: :: ext_php_rs:: php:: exceptions:: PhpException = e. into( ) ;
137- e. throw( ) . expect( "Failed to throw exception" ) ;
175+ if let Err ( e) = result. set_zval( retval, false ) {
176+ let e: :: ext_php_rs:: php:: exceptions:: PhpException = e. into( ) ;
177+ e. throw( ) . expect( "Failed to throw exception" ) ;
178+ }
138179 }
139180 }
140181 } ;
141182
142- let name = identifier. unwrap_or_else ( || rename_rule. rename ( ident. to_string ( ) ) ) ;
143183 let method = Method {
144184 name,
145185 ident : internal_ident. to_string ( ) ,
@@ -151,7 +191,7 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result<Par
151191 visibility,
152192 } ;
153193
154- Ok ( ParsedMethod :: new ( func, method, as_prop) )
194+ Ok ( ParsedMethod :: new ( func, method, as_prop, is_constructor ) )
155195}
156196
157197fn build_args (
@@ -216,20 +256,22 @@ fn build_arg_definitions(args: &[Arg]) -> (Vec<TokenStream>, bool) {
216256fn build_arg_parser < ' a > (
217257 args : impl Iterator < Item = & ' a Arg > ,
218258 optional : & Option < String > ,
259+ ret : & TokenStream ,
219260) -> Result < TokenStream > {
220261 function:: build_arg_parser (
221262 args. filter_map ( |arg| match arg {
222263 Arg :: Typed ( arg) => Some ( arg) ,
223264 _ => None ,
224265 } ) ,
225266 optional,
267+ ret,
226268 )
227269}
228270
229- fn build_arg_accessors ( args : & [ Arg ] ) -> Vec < TokenStream > {
271+ fn build_arg_accessors ( args : & [ Arg ] , ret : & TokenStream ) -> Vec < TokenStream > {
230272 args. iter ( )
231273 . filter_map ( |arg| match arg {
232- Arg :: Typed ( arg) => Some ( arg. get_accessor ( ) ) ,
274+ Arg :: Typed ( arg) => Some ( arg. get_accessor ( ret ) ) ,
233275 _ => None ,
234276 } )
235277 . collect ( )
@@ -241,27 +283,27 @@ impl Method {
241283 Ident :: new ( & self . ident , Span :: call_site ( ) )
242284 }
243285
286+ pub fn get_arg_definitions ( & self ) -> impl Iterator < Item = TokenStream > + ' _ {
287+ self . args . iter ( ) . filter_map ( move |arg| match arg {
288+ Arg :: Typed ( arg) => {
289+ let def = arg. get_arg_definition ( ) ;
290+ let prelude = self . optional . as_ref ( ) . and_then ( |opt| {
291+ if opt. eq ( & arg. name ) {
292+ Some ( quote ! { . not_required( ) } )
293+ } else {
294+ None
295+ }
296+ } ) ;
297+ Some ( quote ! { #prelude. arg( #def) } )
298+ }
299+ _ => None ,
300+ } )
301+ }
302+
244303 pub fn get_builder ( & self , class_path : & Ident ) -> TokenStream {
245304 let name = & self . name ;
246305 let name_ident = self . get_name_ident ( ) ;
247- let args = self
248- . args
249- . iter ( )
250- . filter_map ( |arg| match arg {
251- Arg :: Typed ( arg) => {
252- let def = arg. get_arg_definition ( ) ;
253- let prelude = self . optional . as_ref ( ) . and_then ( |opt| {
254- if opt. eq ( & arg. name ) {
255- Some ( quote ! { . not_required( ) } )
256- } else {
257- None
258- }
259- } ) ;
260- Some ( quote ! { #prelude. arg( #def) } )
261- }
262- _ => None ,
263- } )
264- . collect :: < Vec < _ > > ( ) ;
306+ let args = self . get_arg_definitions ( ) ;
265307 let output = self . output . as_ref ( ) . map ( |( ty, nullable) | {
266308 let ty: Type = syn:: parse_str ( ty) . unwrap ( ) ;
267309
0 commit comments