Skip to content

Commit 19b48ef

Browse files
committed
Add arguments for new object, some call functions.
1 parent 7518fa1 commit 19b48ef

File tree

13 files changed

+290
-73
lines changed

13 files changed

+290
-73
lines changed

examples/http-client/src/client.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ pub fn make_client_builder_class() -> DynamicClass<ClientBuilder> {
4949
|this, _arguments| {
5050
let state = this.as_mut_state();
5151
let client = replace_and_get(state, ClientBuilder::new(), ClientBuilder::build)?;
52-
let mut object =
53-
ClassEntry::<Option<Client>>::from_globals(HTTP_CLIENT_CLASS_NAME)?.new_object();
52+
let mut object = ClassEntry::<Option<Client>>::from_globals(HTTP_CLIENT_CLASS_NAME)?
53+
.new_object_without_construct();
5454
*object.as_mut_state() = Some(client);
5555
Ok::<_, HttpClientError>(object)
5656
},
@@ -79,7 +79,7 @@ pub fn make_client_class() -> DynamicClass<Option<Client>> {
7979
let request_builder = client.get(url);
8080
let mut object =
8181
ClassEntry::<Option<RequestBuilder>>::from_globals(REQUEST_BUILDER_CLASS_NAME)?
82-
.new_object();
82+
.new_object_without_construct();
8383
*object.as_mut_state() = Some(request_builder);
8484
Ok::<_, HttpClientError>(object)
8585
},
@@ -95,7 +95,7 @@ pub fn make_client_class() -> DynamicClass<Option<Client>> {
9595
let request_builder = client.post(url);
9696
let mut object =
9797
ClassEntry::<Option<RequestBuilder>>::from_globals(REQUEST_BUILDER_CLASS_NAME)?
98-
.new_object();
98+
.new_object_without_construct();
9999
*object.as_mut_state() = Some(request_builder);
100100
Ok::<_, HttpClientError>(object)
101101
},

examples/http-client/src/request.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ pub fn make_request_builder_class() -> DynamicClass<Option<RequestBuilder>> {
2323
|this, _arguments| {
2424
let state = this.as_mut_state();
2525
let response = replace_and_get(state, None, |builder| builder.unwrap().send())?;
26-
let mut object =
27-
ClassEntry::<Option<Response>>::from_globals(RESPONSE_CLASS_NAME)?.new_object();
26+
let mut object = ClassEntry::<Option<Response>>::from_globals(RESPONSE_CLASS_NAME)?
27+
.new_object_without_construct();
2828
*object.as_mut_state() = Some(response);
2929
Ok::<_, HttpClientError>(object)
3030
},

phper-sys/php_wrapper.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,11 @@ bool phper_call_user_function(HashTable *function_table, zval *object, zval *fun
152152
function_table = function_table;
153153
return call_user_function(function_table, object, function_name, retval_ptr, param_count, params) == SUCCESS;
154154
}
155+
156+
bool phper_zend_hash_str_exists(const HashTable *ht, const char *str, size_t len) {
157+
return zend_hash_str_exists(ht, str, len) != 0;
158+
}
159+
160+
bool phper_zend_hash_index_exists(const HashTable *ht, zend_ulong h) {
161+
return zend_hash_index_exists(ht, h) != 0;
162+
}

phper-sys/php_wrapper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,7 @@ zend_object* (**phper_get_create_object(zend_class_entry *ce))(zend_class_entry
5353

5454
bool phper_call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]);
5555

56+
bool phper_zend_hash_str_exists(const HashTable *ht, const char *str, size_t len);
57+
bool phper_zend_hash_index_exists(const HashTable *ht, zend_ulong h);
58+
5659
#endif //PHPER_PHP_WRAPPER_H

phper/src/arrays.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,12 @@ impl Array {
5353
}
5454
}
5555

56-
pub(crate) unsafe fn from_mut_ptr<'a>(ptr: *mut zend_array) -> &'a mut Array {
56+
pub unsafe fn from_ptr<'a>(ptr: *const zend_array) -> &'a Array {
57+
let ptr = ptr as *const Array;
58+
ptr.as_ref().expect("ptr shouldn't be null")
59+
}
60+
61+
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zend_array) -> &'a mut Array {
5762
let ptr = ptr as *mut Array;
5863
ptr.as_mut().expect("ptr shouldn't be null")
5964
}
@@ -108,6 +113,20 @@ impl Array {
108113
unsafe { zend_array_count(&mut self.inner) as usize }
109114
}
110115

116+
pub fn exists<'a>(&self, key: impl Into<Key<'a>>) -> bool {
117+
let key = key.into();
118+
unsafe {
119+
match key {
120+
Key::Index(i) => phper_zend_hash_index_exists(&self.inner, i),
121+
Key::Str(s) => phper_zend_hash_str_exists(
122+
&self.inner,
123+
s.as_ref().as_ptr().cast(),
124+
s.as_ref().len(),
125+
),
126+
}
127+
}
128+
}
129+
111130
pub fn clone(&self) -> EBox<Self> {
112131
let mut other = Self::new();
113132
unsafe {

phper/src/classes.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use crate::{
44
alloc::EBox,
5+
arrays::Array,
56
errors::{ClassNotFoundError, StateTypeError},
67
functions::{Argument, Function, FunctionEntity, FunctionEntry, Method},
78
objects::{ExtendObject, Object},
@@ -226,18 +227,40 @@ impl<T: 'static> ClassEntry<T> {
226227
&mut self.inner
227228
}
228229

229-
pub fn new_object(&self) -> EBox<Object<T>> {
230+
/// Create the object from class and call `__construct` with arguments.
231+
pub fn new_object(&self, arguments: &mut [Val]) -> crate::Result<EBox<Object<T>>> {
230232
unsafe {
231233
let ptr = self.as_ptr() as *mut _;
232234
let f = (*phper_get_create_object(ptr)).unwrap_or(zend_objects_new);
233235
let object = f(ptr);
234-
EBox::from_raw(object.cast())
236+
let mut object: EBox<Object<T>> = EBox::from_raw(object.cast());
237+
let _ = object.call_construct(arguments)?;
238+
Ok(object)
239+
}
240+
}
241+
242+
/// Create the object from class, without calling `__construct`, be careful when `__construct`
243+
/// is necessary.
244+
pub fn new_object_without_construct(&self) -> EBox<Object<T>> {
245+
unsafe {
246+
let ptr = self.as_ptr() as *mut _;
247+
let f = (*phper_get_create_object(ptr)).unwrap_or(zend_objects_new);
248+
let object = f(ptr);
249+
let object: EBox<Object<T>> = EBox::from_raw(object.cast());
250+
object
235251
}
236252
}
237253

238254
pub fn get_name(&self) -> &ZendString {
239255
ZendString::from_ptr(self.inner.name)
240256
}
257+
258+
pub fn has_method(&self, method_name: &str) -> bool {
259+
unsafe {
260+
let function_table = Array::from_ptr(&self.inner.function_table);
261+
function_table.exists(method_name)
262+
}
263+
}
241264
}
242265

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

phper/src/functions.rs

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
//! Apis relate to [crate::sys::zend_function_entry].
22
//!
3-
//! TODO Add php function call.
43
//! TODO Add lambda.
54
65
use std::{mem::zeroed, os::raw::c_char};
76

87
use crate::{
98
alloc::EBox,
109
classes::Visibility,
11-
errors::{ArgumentCountError, CallFunctionError},
10+
errors::{ArgumentCountError, CallFunctionError, CallMethodError},
1211
objects::Object,
1312
strings::ZendString,
1413
sys::*,
1514
utils::ensure_end_with_zero,
1615
values::{ExecuteData, SetVal, Val},
1716
};
18-
use std::{marker::PhantomData, ptr::null_mut, str::Utf8Error};
17+
use std::{marker::PhantomData, mem::size_of, ptr::null_mut};
1918

2019
pub(crate) trait Callable {
2120
fn call(&self, execute_data: &mut ExecuteData, arguments: &mut [Val], return_value: &mut Val);
@@ -225,10 +224,73 @@ impl ZendFunction {
225224
&mut self.inner
226225
}
227226

228-
pub fn get_name(&self) -> Result<String, Utf8Error> {
227+
pub fn get_name(&self) -> EBox<ZendString> {
229228
unsafe {
230229
let s = phper_get_function_or_method_name(self.as_ptr());
231-
ZendString::from_raw(s).to_string()
230+
ZendString::from_raw(s)
231+
}
232+
}
233+
234+
pub(crate) fn call_method<T: 'static>(
235+
&mut self,
236+
object: &mut Object<T>,
237+
arguments: &mut [Val],
238+
) -> crate::Result<EBox<Val>> {
239+
let mut ret_val = EBox::new(Val::undef());
240+
241+
let mut fci = zend_fcall_info {
242+
size: size_of::<zend_fcall_info>(),
243+
function_name: Val::undef().into_inner(),
244+
retval: ret_val.as_mut_ptr(),
245+
params: arguments.as_mut_ptr().cast(),
246+
object: object.as_mut_ptr(),
247+
param_count: arguments.len() as u32,
248+
#[cfg(phper_major_version = "8")]
249+
named_params: null_mut(),
250+
#[cfg(phper_major_version = "7")]
251+
no_separation: 1,
252+
#[cfg(all(phper_major_version = "7", phper_minor_version = "0"))]
253+
function_table: null_mut(),
254+
#[cfg(all(phper_major_version = "7", phper_minor_version = "0"))]
255+
symbol_table: null_mut(),
256+
};
257+
258+
let called_scope = unsafe {
259+
let mut called_scope = object.get_class().as_ptr() as *mut zend_class_entry;
260+
if called_scope.is_null() {
261+
called_scope = self.inner.common.scope;
262+
}
263+
called_scope
264+
};
265+
266+
let mut fcc = zend_fcall_info_cache {
267+
function_handler: self.as_mut_ptr(),
268+
calling_scope: null_mut(),
269+
called_scope,
270+
object: object.as_mut_ptr(),
271+
#[cfg(all(
272+
phper_major_version = "7",
273+
any(
274+
phper_minor_version = "0",
275+
phper_minor_version = "1",
276+
phper_minor_version = "2",
277+
)
278+
))]
279+
initialized: 1,
280+
};
281+
282+
unsafe {
283+
if zend_call_function(&mut fci, &mut fcc) != ZEND_RESULT_CODE_SUCCESS
284+
|| ret_val.get_type().is_undef()
285+
{
286+
Err(CallMethodError::new(
287+
object.get_class().get_name().to_string()?,
288+
self.get_name().to_string()?,
289+
)
290+
.into())
291+
} else {
292+
Ok(ret_val)
293+
}
232294
}
233295
}
234296
}
@@ -262,6 +324,7 @@ unsafe extern "C" fn invoke(execute_data: *mut zend_execute_data, return_value:
262324
if num_args < required_num_args {
263325
let func_name = execute_data.func().get_name();
264326
let result = func_name
327+
.to_string()
265328
.map(|func_name| {
266329
Err::<(), _>(ArgumentCountError::new(
267330
func_name,
@@ -323,7 +386,23 @@ pub(crate) const fn create_zend_arg_info(
323386
}
324387
}
325388

326-
pub fn call(fn_name: &str, arguments: &[Val]) -> Result<EBox<Val>, CallFunctionError> {
389+
/// Call user function by name.
390+
///
391+
/// # Examples
392+
///
393+
/// ```
394+
/// use phper::{arrays::Array, functions::call, values::Val};
395+
///
396+
/// fn example() -> phper::Result<()> {
397+
/// let mut arr = Array::new();
398+
/// arr.insert("a", Val::new(1));
399+
/// arr.insert("b", Val::new(2));
400+
/// let ret = call("json_encode", &mut [Val::new(arr)])?;
401+
/// assert_eq!(ret.as_string()?, r#"{"a":1,"b":2}"#);
402+
/// Ok(())
403+
/// }
404+
/// ```
405+
pub fn call(fn_name: &str, arguments: &mut [Val]) -> Result<EBox<Val>, CallFunctionError> {
327406
let mut func = Val::new(fn_name);
328407
let mut ret = EBox::new(Val::null());
329408
unsafe {
@@ -333,8 +412,9 @@ pub fn call(fn_name: &str, arguments: &[Val]) -> Result<EBox<Val>, CallFunctionE
333412
func.as_mut_ptr(),
334413
ret.as_mut_ptr(),
335414
arguments.len() as u32,
336-
arguments.as_ptr() as *const Val as *mut Val as *mut zval,
337-
) {
415+
arguments.as_mut_ptr().cast(),
416+
) && !ret.get_type().is_undef()
417+
{
338418
Ok(ret)
339419
} else {
340420
Err(CallFunctionError::new(fn_name.to_owned()))

phper/src/objects.rs

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
alloc::{EAllocatable, EBox},
55
classes::ClassEntry,
66
errors::CallMethodError,
7+
functions::ZendFunction,
78
sys::*,
89
values::Val,
910
};
@@ -26,13 +27,17 @@ pub struct Object<T: 'static> {
2627
}
2728

2829
impl<T: 'static> Object<T> {
29-
pub fn new(class_entry: &ClassEntry<T>) -> EBox<Self> {
30-
class_entry.new_object()
30+
/// Another way to new object like [crate::classes::ClassEntry::new_object].
31+
pub fn new(class_entry: &ClassEntry<T>, arguments: &mut [Val]) -> crate::Result<EBox<Self>> {
32+
class_entry.new_object(arguments)
3133
}
3234

33-
pub fn new_by_class_name(class_name: impl AsRef<str>) -> crate::Result<EBox<Self>> {
35+
pub fn new_by_class_name(
36+
class_name: impl AsRef<str>,
37+
arguments: &mut [Val],
38+
) -> crate::Result<EBox<Self>> {
3439
let class_entry = ClassEntry::from_globals(class_name)?;
35-
Ok(Self::new(class_entry))
40+
Self::new(class_entry, arguments)
3641
}
3742

3843
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zend_object) -> &'a mut Self {
@@ -142,21 +147,34 @@ impl<T: 'static> Object<T> {
142147
ClassEntry::from_ptr(self.inner.ce)
143148
}
144149

145-
pub fn call(&self, method_name: &str, arguments: &[Val]) -> crate::Result<EBox<Val>> {
150+
/// Call the object method byn name.
151+
///
152+
/// # Examples
153+
///
154+
/// ```
155+
/// use phper::{alloc::EBox, classes::StatelessClassEntry, values::Val};
156+
///
157+
/// fn example() -> phper::Result<EBox<Val>> {
158+
/// let mut memcached = StatelessClassEntry::from_globals("Memcached")?.new_object(&mut [])?;
159+
/// memcached.call("addServer", &mut [Val::new("127.0.0.1"), Val::new(11211)])?;
160+
/// memcached.call("get", &mut [Val::new("hello")])
161+
/// }
162+
/// ```
163+
pub fn call(&mut self, method_name: &str, arguments: &mut [Val]) -> crate::Result<EBox<Val>> {
146164
let mut method = Val::new(method_name);
147165
let mut ret = EBox::new(Val::null());
148166

149167
unsafe {
150168
let mut object = std::mem::zeroed::<zval>();
151-
phper_zval_obj(&mut object, self.as_ptr() as *mut _);
169+
phper_zval_obj(&mut object, self.as_mut_ptr());
152170

153171
if phper_call_user_function(
154172
null_mut(),
155173
&mut object,
156174
method.as_mut_ptr(),
157175
ret.as_mut_ptr(),
158176
arguments.len() as u32,
159-
arguments.as_ptr() as *const Val as *mut Val as *mut zval,
177+
arguments.as_mut_ptr().cast(),
160178
) {
161179
Ok(ret)
162180
} else {
@@ -165,11 +183,28 @@ impl<T: 'static> Object<T> {
165183
}
166184
}
167185
}
186+
187+
pub(crate) fn call_construct(&mut self, arguments: &mut [Val]) -> crate::Result<Option<()>> {
188+
unsafe {
189+
(*self.inner.handlers)
190+
.get_constructor
191+
.and_then(|get_constructor| {
192+
let f = get_constructor(self.as_ptr() as *mut _);
193+
if f.is_null() {
194+
None
195+
} else {
196+
let zend_fn = ZendFunction::from_mut_ptr(f);
197+
Some(zend_fn.call_method(self, arguments).map(|_| ()))
198+
}
199+
})
200+
.transpose()
201+
}
202+
}
168203
}
169204

170205
impl Object<()> {
171206
pub fn new_by_std_class() -> EBox<Self> {
172-
Self::new_by_class_name("stdclass").unwrap()
207+
Self::new_by_class_name("stdclass", &mut []).unwrap()
173208
}
174209
}
175210

0 commit comments

Comments
 (0)