Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion ext-php-rs-derive/src/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>

state.classes.insert(ident.to_string(), class);

Ok(quote! { #input })
Ok(quote! {
#input

::ext_php_rs::class_derives!(#ident);
})
}

#[derive(Debug)]
Expand Down
52 changes: 41 additions & 11 deletions ext-php-rs-derive/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi
let args = build_args(inputs, &attr_args.defaults)?;
let optional = find_optional_parameter(args.iter(), attr_args.optional);
let arg_definitions = build_arg_definitions(&args);
let arg_parser = build_arg_parser(args.iter(), &optional, &quote! { return; })?;
let arg_parser = build_arg_parser(
args.iter(),
&optional,
&quote! { return; },
ParserType::Function,
)?;
let arg_accessors = build_arg_accessors(&args);

let return_type = get_return_type(output)?;
Expand Down Expand Up @@ -154,26 +159,29 @@ pub fn find_optional_parameter<'a>(
optional
}

pub enum ParserType {
Function,
Method,
StaticMethod,
}

pub fn build_arg_parser<'a>(
args: impl Iterator<Item = &'a Arg>,
optional: &Option<String>,
ret: &TokenStream,
ty: ParserType,
) -> Result<TokenStream> {
let mut rest_optional = false;

let args = args
.map(|arg| {
let name = arg.get_name_ident();
let prelude = if let Some(optional) = optional {
if *optional == arg.name {
rest_optional = true;
quote! { .not_required() }
} else {
quote! {}
}
let prelude = optional.as_ref().and_then(|opt| if *opt == arg.name {
rest_optional = true;
Some(quote! { .not_required() })
} else {
quote! {}
};
None
});

if rest_optional && !arg.nullable && arg.default.is_none() {
bail!(
Expand All @@ -188,15 +196,37 @@ pub fn build_arg_parser<'a>(
}
})
.collect::<Result<Vec<_>>>()?;
let (parser, this) = match ty {
ParserType::Function | ParserType::StaticMethod => {
(quote! { let parser = ex.parser(); }, None)
}
ParserType::Method => (
quote! { let (parser, this) = ex.parser_method::<Self>(); },
Some(quote! {
let this = match this {
Some(this) => this,
None => {
::ext_php_rs::php::exceptions::PhpException::default("Failed to retrieve reference to `$this`".into())
.throw()
.unwrap();
return;
},
};
}),
),
};

Ok(quote! {
let parser = ::ext_php_rs::php::args::ArgParser::new(ex)
#parser
let parser = parser
#(#args)*
.parse();

if parser.is_err() {
#ret
}

#this
})
}

Expand Down
56 changes: 30 additions & 26 deletions ext-php-rs-derive/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use quote::ToTokens;
use std::collections::HashMap;

use crate::{
function,
function::{self, ParserType},
impl_::{parse_attribute, ParsedAttribute, PropAttrTy, RenameRule, Visibility},
};
use proc_macro2::{Ident, Span, TokenStream};
Expand Down Expand Up @@ -131,7 +131,16 @@ pub fn parser(input: &mut ImplItemMethod, rename_rule: RenameRule) -> Result<Par
optional,
);
let (arg_definitions, is_static) = build_arg_definitions(&args);
let arg_parser = build_arg_parser(args.iter(), &optional, &bail)?;
let arg_parser = build_arg_parser(
args.iter(),
&optional,
&bail,
if is_static {
ParserType::StaticMethod
} else {
ParserType::Method
},
)?;
let arg_accessors = build_arg_accessors(&args, &bail);
let this = if is_static {
quote! { Self:: }
Expand Down Expand Up @@ -225,38 +234,32 @@ fn build_args(
fn build_arg_definitions(args: &[Arg]) -> (Vec<TokenStream>, bool) {
let mut _static = true;

(args.iter()
.map(|ty| match ty {
Arg::Receiver(mutability) => {
let mutability = mutability.then(|| quote! { mut });
_static = false;

quote! {
// SAFETY: We are calling this on an execution data from a class method.
let #mutability this = match unsafe { ex.get_object::<Self>() } {
Some(this) => this,
None => return ::ext_php_rs::php::exceptions::throw(
::ext_php_rs::php::class::ClassEntry::exception(),
"Failed to retrieve reference to object function was called on."
).expect("Failed to throw exception: Failed to retrieve reference to object function was called on."),
};
(
args.iter()
.filter_map(|ty| match ty {
Arg::Receiver(_) => {
_static = false;

None
}
}
Arg::Typed(arg) => {
let ident = arg.get_name_ident();
let definition = arg.get_arg_definition();
quote! {
let mut #ident = #definition;
Arg::Typed(arg) => {
let ident = arg.get_name_ident();
let definition = arg.get_arg_definition();
Some(quote! {
let mut #ident = #definition;
})
}
},
})
.collect(), _static)
})
.collect(),
_static,
)
}

fn build_arg_parser<'a>(
args: impl Iterator<Item = &'a Arg>,
optional: &Option<String>,
ret: &TokenStream,
ty: ParserType,
) -> Result<TokenStream> {
function::build_arg_parser(
args.filter_map(|arg| match arg {
Expand All @@ -265,6 +268,7 @@ fn build_arg_parser<'a>(
}),
optional,
ret,
ty,
)
}

Expand Down
2 changes: 1 addition & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub enum Error {
/// The enum carries two integers - the first representing the minimum
/// number of arguments expected, and the second representing the number of
/// arguments that were received.
IncorrectArguments(u32, u32),
IncorrectArguments(usize, usize),
/// There was an error converting a Zval into a primitive type.
///
/// The enum carries the data type of the Zval.
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ pub use ext_php_rs_derive::php_extern;
///
/// pub extern "C" fn _internal_php_hello(ex: &mut ExecutionData, retval: &mut Zval) {
/// let mut name = Arg::new("name", <String as FromZval>::TYPE);
/// let parser = ArgParser::new(ex)
/// let parser = ex.parser()
/// .arg(&mut name)
/// .parse();
///
Expand Down
150 changes: 146 additions & 4 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ macro_rules! call_user_func {
#[macro_export]
macro_rules! parse_args {
($ed: expr, $($arg: expr),*) => {{
use $crate::php::args::ArgParser;

let parser = ArgParser::new($ed)
let parser = $ed.parser()
$(.arg(&mut $arg))*
.parse();
if parser.is_err() {
Expand All @@ -125,7 +123,7 @@ macro_rules! parse_args {
($ed: expr, $($arg: expr),* ; $($opt: expr),*) => {{
use $crate::php::args::ArgParser;

let parser = ArgParser::new($ed)
let parser = $ed.parser()
$(.arg(&mut $arg))*
.not_required()
$(.arg(&mut $opt))*
Expand Down Expand Up @@ -163,3 +161,147 @@ macro_rules! throw {
return;
};
}

/// Implements a set of traits required to convert types that implement [`RegisteredClass`] to and
/// from [`ZendObject`]s and [`Zval`]s. Generally, this macro should not be called directly, as it
/// is called on any type that uses the [`#[php_class]`] macro.
///
/// The following traits are implemented:
///
/// * `FromZendObject for &'a T`
/// * `FromZendObjectMut for &'a mut T`
/// * `FromZval for &'a T`
/// * `FromZvalMut for &'a mut T`
/// * `IntoZendObject for T`
/// * `IntoZval for T`
///
/// These implementations are required while we wait on the stabilisation of specialisation.
///
/// # Examples
///
/// ```
/// # use ext_php_rs::{php::types::{zval::{Zval, IntoZval, FromZval}, object::{ZendObject, RegisteredClass}}};
/// use ext_php_rs::class_derives;
///
/// struct Test {
/// a: i32,
/// b: i64
/// }
///
/// impl RegisteredClass for Test {
/// const CLASS_NAME: &'static str = "Test";
///
/// const CONSTRUCTOR: Option<ext_php_rs::php::types::object::ConstructorMeta<Self>> = None;
///
/// fn get_metadata() -> &'static ext_php_rs::php::types::object::ClassMetadata<Self> {
/// todo!()
/// }
///
/// fn get_properties<'a>(
/// ) -> std::collections::HashMap<&'static str, ext_php_rs::php::types::props::Property<'a, Self>>
/// {
/// todo!()
/// }
/// }
///
/// class_derives!(Test);
///
/// fn into_zval_test() -> Zval {
/// let x = Test { a: 5, b: 10 };
/// x.into_zval(false).unwrap()
/// }
///
/// fn from_zval_test<'a>(zv: &'a Zval) -> &'a Test {
/// <&Test>::from_zval(zv).unwrap()
/// }
/// ```
///
/// [`RegisteredClass`]: crate::php::types::object::RegisteredClass
/// [`ZendObject`]: crate::php::types::object::ZendObject
/// [`Zval`]: crate::php::types::zval::Zval
/// [`#[php_class]`]: crate::php_class
#[macro_export]
macro_rules! class_derives {
($type: ty) => {
impl<'a> $crate::php::types::object::FromZendObject<'a> for &'a $type {
#[inline]
fn from_zend_object(
obj: &'a $crate::php::types::object::ZendObject,
) -> $crate::errors::Result<Self> {
let obj = $crate::php::types::object::ZendClassObject::<$type>::from_zend_obj(obj)
.ok_or($crate::errors::Error::InvalidScope)?;
Ok(&**obj)
}
}

impl<'a> $crate::php::types::object::FromZendObjectMut<'a> for &'a mut $type {
#[inline]
fn from_zend_object_mut(
obj: &'a mut $crate::php::types::object::ZendObject,
) -> $crate::errors::Result<Self> {
let obj =
$crate::php::types::object::ZendClassObject::<$type>::from_zend_obj_mut(obj)
.ok_or($crate::errors::Error::InvalidScope)?;
Ok(&mut **obj)
}
}

impl<'a> $crate::php::types::zval::FromZval<'a> for &'a $type {
const TYPE: $crate::php::enums::DataType = $crate::php::enums::DataType::Object(Some(
<$type as $crate::php::types::object::RegisteredClass>::CLASS_NAME,
));

#[inline]
fn from_zval(zval: &'a $crate::php::types::zval::Zval) -> ::std::option::Option<Self> {
<Self as $crate::php::types::object::FromZendObject>::from_zend_object(
zval.object()?,
)
.ok()
}
}

impl<'a> $crate::php::types::zval::FromZvalMut<'a> for &'a mut $type {
const TYPE: $crate::php::enums::DataType = $crate::php::enums::DataType::Object(Some(
<$type as $crate::php::types::object::RegisteredClass>::CLASS_NAME,
));

#[inline]
fn from_zval_mut(
zval: &'a mut $crate::php::types::zval::Zval,
) -> ::std::option::Option<Self> {
<Self as $crate::php::types::object::FromZendObjectMut>::from_zend_object_mut(
zval.object_mut()?,
)
.ok()
}
}

impl $crate::php::types::object::IntoZendObject for $type {
#[inline]
fn into_zend_object(
self,
) -> $crate::errors::Result<
$crate::php::boxed::ZBox<$crate::php::types::object::ZendObject>,
> {
Ok($crate::php::types::object::ZendClassObject::new(self).into())
}
}

impl $crate::php::types::zval::IntoZval for $type {
const TYPE: $crate::php::enums::DataType = $crate::php::enums::DataType::Object(Some(
<$type as $crate::php::types::object::RegisteredClass>::CLASS_NAME,
));

#[inline]
fn set_zval(
self,
zv: &mut $crate::php::types::zval::Zval,
persistent: bool,
) -> $crate::errors::Result<()> {
use $crate::php::types::object::IntoZendObject;

self.into_zend_object()?.set_zval(zv, persistent)
}
}
};
}
Loading