diff --git a/crates/macros/src/impl_.rs b/crates/macros/src/impl_.rs index b43f7ea1ad..9fa5f16bdc 100644 --- a/crates/macros/src/impl_.rs +++ b/crates/macros/src/impl_.rs @@ -2,7 +2,7 @@ use darling::util::Flag; use darling::FromAttributes; use proc_macro2::TokenStream; use quote::quote; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use syn::{Ident, ItemImpl, Lit}; use crate::constant::PhpConstAttribute; @@ -124,6 +124,22 @@ struct ParsedImpl<'a> { constants: Vec>, } +#[derive(Debug, Eq, Hash, PartialEq)] +enum MethodModifier { + Abstract, + Static, +} + +impl quote::ToTokens for MethodModifier { + fn to_tokens(&self, tokens: &mut TokenStream) { + match *self { + Self::Abstract => quote! { ::ext_php_rs::flags::MethodFlags::Abstract }, + Self::Static => quote! { ::ext_php_rs::flags::MethodFlags::Static }, + } + .to_tokens(tokens); + } +} + #[derive(Debug)] struct FnBuilder { /// Tokens which represent the `FunctionBuilder` for this function. @@ -131,7 +147,7 @@ struct FnBuilder { /// The visibility of this method. pub vis: Visibility, /// Whether this method is abstract. - pub r#abstract: bool, + pub modifiers: HashSet, } #[derive(Debug)] @@ -190,6 +206,8 @@ impl<'a> ParsedImpl<'a> { let args = Args::parse_from_fnargs(method.sig.inputs.iter(), opts.defaults)?; let mut func = Function::new(&method.sig, opts.name, args, opts.optional, docs); + let mut modifiers: HashSet = HashSet::new(); + if matches!(opts.ty, MethodTy::Constructor) { if self.constructor.replace(func).is_some() { bail!(method => "Only one constructor can be provided per class."); @@ -211,15 +229,21 @@ impl<'a> ParsedImpl<'a> { func.args.typed.pop(); MethodReceiver::ZendClassObject } else { + modifiers.insert(MethodModifier::Static); // Static method MethodReceiver::Static }, }; + if matches!(opts.ty, MethodTy::Abstract) { + modifiers.insert(MethodModifier::Abstract); + } + let builder = func.function_builder(call_type); + self.functions.push(FnBuilder { builder, vis: opts.vis, - r#abstract: matches!(opts.ty, MethodTy::Abstract), + modifiers, }); } } @@ -284,9 +308,10 @@ impl quote::ToTokens for FnBuilder { Visibility::Protected => quote! { ::ext_php_rs::flags::MethodFlags::Protected }, Visibility::Private => quote! { ::ext_php_rs::flags::MethodFlags::Private }, }); - if self.r#abstract { - flags.push(quote! { ::ext_php_rs::flags::MethodFlags::Abstract }); + for flag in &self.modifiers { + flags.push(quote! { #flag }) } + quote! { (#builder, #(#flags)|*) } diff --git a/tests/src/integration/class.php b/tests/src/integration/class.php index 568cafbb17..af705a1132 100644 --- a/tests/src/integration/class.php +++ b/tests/src/integration/class.php @@ -19,3 +19,12 @@ assert($class->boolean); $class->boolean = false; assert($class->boolean === false); + +// Call regular from object +assert($class->staticCall('Php') === 'Hello Php'); + +// Call static from object +assert($class::staticCall('Php') === 'Hello Php'); + +// Call static from class +assert(TestClass::staticCall('Php') === 'Hello Php'); diff --git a/tests/src/lib.rs b/tests/src/lib.rs index c9cc15c81b..78ce9ae6b7 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -210,6 +210,10 @@ impl TestClass { pub fn set_number(&mut self, number: i32) { self.number = number; } + + pub fn static_call(name: String) -> String { + format!("Hello {name}") + } } #[php_function]