Skip to content

Commit 7518fa1

Browse files
committed
Add function and method call, and struct support for macro Throwable.
1 parent caa46c3 commit 7518fa1

File tree

15 files changed

+145
-54
lines changed

15 files changed

+145
-54
lines changed

phper-macros/src/derives.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,14 @@ fn parse_throwable_input(
127127
})
128128
.into())
129129
}
130-
Data::Struct(_) => Err(syn::Error::new_spanned(
131-
&input,
132-
"struct auto derive Throwable is not supported",
133-
)),
130+
Data::Struct(_) => Ok((quote! {
131+
impl #crate_ident::errors::Throwable for #input_ident {
132+
fn class_entry(&self) -> &#crate_ident::classes::StatelessClassEntry {
133+
ClassEntry::from_globals(#exception).unwrap()
134+
}
135+
}
136+
})
137+
.into()),
134138
Data::Union(_) => Err(syn::Error::new_spanned(
135139
&input,
136140
"union auto derive Throwable is not supported",

phper-macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub fn php_get_module(attr: TokenStream, input: TokenStream) -> TokenStream {
9090
/// }
9191
/// ```
9292
///
93-
/// TODO Support struct, attbiute `throwable` with `code` and `message`.
93+
/// TODO Support attribute `throwable` with `code` and `message`, integration tests.
9494
#[proc_macro_derive(Throwable, attributes(throwable, throwable_crate))]
9595
pub fn derive_throwable(input: TokenStream) -> TokenStream {
9696
let input = parse_macro_input!(input as DeriveInput);

phper-sys/php_wrapper.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,8 @@ void *phper_zend_object_alloc(size_t obj_size, zend_class_entry *ce) {
147147
zend_object* (**phper_get_create_object(zend_class_entry *ce))(zend_class_entry *class_type) {
148148
return &ce->create_object;
149149
}
150+
151+
bool phper_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]) {
152+
function_table = function_table;
153+
return call_user_function(function_table, object, function_name, retval_ptr, param_count, params) == SUCCESS;
154+
}

phper-sys/php_wrapper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef PHPER_PHP_WRAPPER_H
22
#define PHPER_PHP_WRAPPER_H
33

4+
#include <stdbool.h>
45
#include <php.h>
56
#include <php_ini.h>
67
#include <ext/standard/info.h>
@@ -50,4 +51,6 @@ void *phper_zend_object_alloc(size_t obj_size, zend_class_entry *ce);
5051

5152
zend_object* (**phper_get_create_object(zend_class_entry *ce))(zend_class_entry *class_type);
5253

54+
bool phper_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]);
55+
5356
#endif //PHPER_PHP_WRAPPER_H

phper/src/classes.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::{
55
errors::{ClassNotFoundError, StateTypeError},
66
functions::{Argument, Function, FunctionEntity, FunctionEntry, Method},
77
objects::{ExtendObject, Object},
8+
strings::ZendString,
89
sys::*,
910
types::Scalar,
1011
values::{SetVal, Val},
@@ -233,6 +234,10 @@ impl<T: 'static> ClassEntry<T> {
233234
EBox::from_raw(object.cast())
234235
}
235236
}
237+
238+
pub fn get_name(&self) -> &ZendString {
239+
ZendString::from_ptr(self.inner.name)
240+
}
236241
}
237242

238243
fn find_global_class_entry_ptr(name: impl AsRef<str>) -> *mut zend_class_entry {

phper/src/errors.rs

Lines changed: 36 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
Error::Other,
77
};
88
use anyhow::anyhow;
9+
use derive_more::Constructor;
910
use std::{convert::Infallible, error, ffi::FromBytesWithNulError, io, str::Utf8Error};
1011

1112
/// PHP Throwable, can cause throwing an exception when setting to [crate::values::Val].
@@ -64,6 +65,14 @@ pub enum Error {
6465
#[error(transparent)]
6566
#[throwable(transparent)]
6667
StateType(#[from] StateTypeError),
68+
69+
#[error(transparent)]
70+
#[throwable(transparent)]
71+
CallFunction(#[from] CallFunctionError),
72+
73+
#[error(transparent)]
74+
#[throwable(transparent)]
75+
CallMethod(#[from] CallMethodError),
6776
}
6877

6978
impl Error {
@@ -74,73 +83,39 @@ impl Error {
7483
}
7584
}
7685

77-
#[derive(thiserror::Error, Debug)]
86+
#[derive(Debug, thiserror::Error, crate::Throwable, Constructor)]
7887
#[error("type error: {message}")]
88+
#[throwable(class = "TypeError")]
89+
#[throwable_crate]
7990
pub struct TypeError {
8091
message: String,
8192
}
8293

83-
impl TypeError {
84-
pub fn new(message: String) -> Self {
85-
Self { message }
86-
}
87-
}
88-
89-
impl Throwable for TypeError {
90-
fn class_entry(&self) -> &ClassEntry<()> {
91-
ClassEntry::from_globals("TypeError").unwrap()
92-
}
93-
}
94-
95-
#[derive(thiserror::Error, Debug)]
94+
#[derive(Debug, thiserror::Error, crate::Throwable, Constructor)]
9695
#[error("Class '{class_name}' not found")]
96+
#[throwable(class = "Error")]
97+
#[throwable_crate]
9798
pub struct ClassNotFoundError {
9899
class_name: String,
99100
}
100101

101-
impl ClassNotFoundError {
102-
pub fn new(class_name: String) -> Self {
103-
Self { class_name }
104-
}
105-
}
106-
107-
impl Throwable for ClassNotFoundError {
108-
fn class_entry(&self) -> &StatelessClassEntry {
109-
ClassEntry::from_globals("Error").unwrap()
110-
}
111-
}
112-
113-
#[derive(thiserror::Error, Debug)]
102+
#[derive(Debug, thiserror::Error, crate::Throwable)]
114103
#[error(
115104
"Actual State type in generic type parameter isn't the state type registered in the class, \
116105
please confirm the real state type, or use StatelessClassEntry"
117106
)]
107+
#[throwable(class = "Error")]
108+
#[throwable_crate]
118109
pub struct StateTypeError;
119110

120-
impl Throwable for StateTypeError {
121-
fn class_entry(&self) -> &StatelessClassEntry {
122-
ClassEntry::from_globals("Error").unwrap()
123-
}
124-
}
125-
126-
#[derive(thiserror::Error, Debug)]
111+
#[derive(thiserror::Error, Debug, Constructor)]
127112
#[error("{function_name}(): expects at least {expect_count} parameter(s), {given_count} given")]
128113
pub struct ArgumentCountError {
129114
function_name: String,
130115
expect_count: usize,
131116
given_count: usize,
132117
}
133118

134-
impl ArgumentCountError {
135-
pub fn new(function_name: String, expect_count: usize, given_count: usize) -> Self {
136-
Self {
137-
function_name,
138-
expect_count,
139-
given_count,
140-
}
141-
}
142-
}
143-
144119
impl Throwable for ArgumentCountError {
145120
fn class_entry(&self) -> &StatelessClassEntry {
146121
let class_name = if PHP_VERSION_ID >= 70100 {
@@ -151,3 +126,20 @@ impl Throwable for ArgumentCountError {
151126
ClassEntry::from_globals(class_name).unwrap()
152127
}
153128
}
129+
130+
#[derive(Debug, thiserror::Error, crate::Throwable, Constructor)]
131+
#[error("Invalid call to {fn_name}")]
132+
#[throwable(class = "BadFunctionCallException")]
133+
#[throwable_crate]
134+
pub struct CallFunctionError {
135+
fn_name: String,
136+
}
137+
138+
#[derive(Debug, thiserror::Error, crate::Throwable, Constructor)]
139+
#[error("Invalid call to {class_name}::{method_name}")]
140+
#[throwable(class = "BadMethodCallException")]
141+
#[throwable_crate]
142+
pub struct CallMethodError {
143+
class_name: String,
144+
method_name: String,
145+
}

phper/src/functions.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
//! Apis relate to [crate::sys::zend_function_entry].
22
//!
33
//! TODO Add php function call.
4+
//! TODO Add lambda.
45
56
use std::{mem::zeroed, os::raw::c_char};
67

78
use crate::{
9+
alloc::EBox,
810
classes::Visibility,
9-
errors::ArgumentCountError,
11+
errors::{ArgumentCountError, CallFunctionError},
1012
objects::Object,
1113
strings::ZendString,
1214
sys::*,
1315
utils::ensure_end_with_zero,
1416
values::{ExecuteData, SetVal, Val},
1517
};
16-
use std::{marker::PhantomData, str::Utf8Error};
18+
use std::{marker::PhantomData, ptr::null_mut, str::Utf8Error};
1719

1820
pub(crate) trait Callable {
1921
fn call(&self, execute_data: &mut ExecuteData, arguments: &mut [Val], return_value: &mut Val);
@@ -284,7 +286,6 @@ pub(crate) const fn create_zend_arg_info(
284286
) -> zend_internal_arg_info {
285287
#[cfg(phper_php_version = "8.0")]
286288
{
287-
use std::ptr::null_mut;
288289
zend_internal_arg_info {
289290
name,
290291
type_: zend_type {
@@ -321,3 +322,22 @@ pub(crate) const fn create_zend_arg_info(
321322
}
322323
}
323324
}
325+
326+
pub fn call(fn_name: &str, arguments: &[Val]) -> Result<EBox<Val>, CallFunctionError> {
327+
let mut func = Val::new(fn_name);
328+
let mut ret = EBox::new(Val::null());
329+
unsafe {
330+
if phper_call_user_function(
331+
compiler_globals.function_table,
332+
null_mut(),
333+
func.as_mut_ptr(),
334+
ret.as_mut_ptr(),
335+
arguments.len() as u32,
336+
arguments.as_ptr() as *const Val as *mut Val as *mut zval,
337+
) {
338+
Ok(ret)
339+
} else {
340+
Err(CallFunctionError::new(fn_name.to_owned()))
341+
}
342+
}
343+
}

phper/src/objects.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::{
44
alloc::{EAllocatable, EBox},
55
classes::ClassEntry,
6+
errors::CallMethodError,
67
sys::*,
78
values::Val,
89
};
@@ -140,6 +141,30 @@ impl<T: 'static> Object<T> {
140141
pub fn get_class(&self) -> &ClassEntry<T> {
141142
ClassEntry::from_ptr(self.inner.ce)
142143
}
144+
145+
pub fn call(&self, method_name: &str, arguments: &[Val]) -> crate::Result<EBox<Val>> {
146+
let mut method = Val::new(method_name);
147+
let mut ret = EBox::new(Val::null());
148+
149+
unsafe {
150+
let mut object = std::mem::zeroed::<zval>();
151+
phper_zval_obj(&mut object, self.as_ptr() as *mut _);
152+
153+
if phper_call_user_function(
154+
null_mut(),
155+
&mut object,
156+
method.as_mut_ptr(),
157+
ret.as_mut_ptr(),
158+
arguments.len() as u32,
159+
arguments.as_ptr() as *const Val as *mut Val as *mut zval,
160+
) {
161+
Ok(ret)
162+
} else {
163+
let class_name = self.get_class().get_name().to_string()?;
164+
Err(CallMethodError::new(class_name, method_name.to_owned()).into())
165+
}
166+
}
167+
}
143168
}
144169

145170
impl Object<()> {

phper/src/values.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ impl Val {
224224
}
225225

226226
unsafe fn drop_value(&mut self) {
227+
// TODO Use zval_dtor.
227228
let t = self.get_type();
228229
if t.is_string() {
229230
ZendString::free(self.inner.value.str as *mut ZendString);

tests/integration/src/functions.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use phper::{arrays::Array, functions::call, modules::Module, values::Val};
2+
3+
pub fn integrate(module: &mut Module) {
4+
module.add_function(
5+
"integrate_functions_call",
6+
|_: &mut [Val]| -> phper::Result<()> {
7+
let mut arr = Array::new();
8+
arr.insert("a", Val::new(1));
9+
arr.insert("b", Val::new(2));
10+
let ret = call("json_encode", &[Val::new(arr)])?;
11+
assert_eq!(ret.as_string()?, r#"{"a":1,"b":2}"#);
12+
Ok(())
13+
},
14+
vec![],
15+
);
16+
}

0 commit comments

Comments
 (0)