Skip to content

Commit b23b678

Browse files
Better interactions with objects (#41)
* Object functions now return references to objects Added ability to get object class name as well as hashtable of properties * Return mutable reference to objects * Optimized `unwrap_or_else` calls
1 parent 4946613 commit b23b678

File tree

8 files changed

+116
-67
lines changed

8 files changed

+116
-67
lines changed

example/skel/src/lib.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,8 @@ impl Test {
6262
let x = ZendClassObject::<Test>::get(execute_data).unwrap();
6363
//let param = execute_data.get_property("value");
6464
let obj = execute_data.get_self().unwrap();
65-
dbg!(obj.get_property("value"));
66-
obj.set_property("value", "Hello");
67-
// dbg!(param);
68-
//execute_data.set_property("new_value", "New value...");
69-
//execute_data.set_property("value", "Hello, world!");
70-
// dbg!(execute_data.set_property("value", "Hello, world!"));
65+
obj.set_property("hello", "world");
66+
dbg!(obj);
7167
}
7268

7369
pub extern "C" fn call(execute_data: &mut ExecutionData, _retval: &mut Zval) {
@@ -86,6 +82,15 @@ impl Test {
8682

8783
println!("Ready for call!");
8884
}
85+
86+
pub extern "C" fn debug(execute_data: &mut ExecutionData, _retval: &mut Zval) {
87+
let mut val = Arg::new("val", DataType::Object);
88+
89+
parse_args!(execute_data, val);
90+
let obj = val.zval().unwrap().object().unwrap();
91+
obj.set_property("hello", "not irigianl");
92+
dbg!(val.zval().map(|zv| zv.object()));
93+
}
8994
}
9095

9196
impl Default for Test {
@@ -111,13 +116,20 @@ pub extern "C" fn module_init(_type: i32, module_number: i32) -> i32 {
111116
FunctionBuilder::new("get", Test::get).build(),
112117
MethodFlags::Public,
113118
)
119+
.method(
120+
FunctionBuilder::new("debug", Test::debug)
121+
.arg(Arg::new("val", DataType::Object))
122+
.build(),
123+
MethodFlags::Public,
124+
)
114125
.method(
115126
FunctionBuilder::new("call", Test::call)
116127
.arg(Arg::new("fn", DataType::Callable))
117128
.build(),
118129
MethodFlags::Public,
119130
)
120-
.property("value", "world", PropertyFlags::Public)
131+
.property("asdf", "world", PropertyFlags::Public)
132+
.property("jhki", 12345, PropertyFlags::Public)
121133
.constant("TEST", "Hello world")
122134
.object_override::<Test>()
123135
.build();

example/skel/test.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
$x = new TestClass();
99
var_dump($x);
1010
$x->get();
11+
$x->asdf = 10;
12+
$x->hello = 'asdf';
1113
var_dump($x);
12-
$x->value = null;
1314
$x->get();
15+
16+
$y = new \stdClass;
17+
$y->hello = 'world';
18+
$y->world = 'hello';
19+
20+
$x->debug($y);
21+
var_dump($y);
22+

src/php/args.rs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -200,27 +200,7 @@ impl<'a, 'b> ArgParser<'a, 'b> {
200200
}
201201

202202
for (i, arg) in self.args.iter_mut().enumerate() {
203-
let zval = unsafe { execute_data.zend_call_arg(i) };
204-
205-
if let Some(zval) = zval {
206-
// if !arg.allow_null && zval.is_null() {
207-
// unsafe {
208-
// zend_wrong_parameter_error(
209-
// ZPP_ERROR_WRONG_CLASS_OR_NULL as i32,
210-
// i as u32,
211-
// c_str(arg.name) as *mut i8,
212-
// _zend_expected_type::from(**arg),
213-
// &mut *zval,
214-
// );
215-
// }
216-
// return Err(format!(
217-
// "Argument at index {} was null but is non-nullable.",
218-
// i
219-
// ));
220-
// }
221-
222-
arg.zval = Some(zval);
223-
}
203+
arg.zval = unsafe { execute_data.zend_call_arg(i) };
224204
}
225205

226206
Ok(())

src/php/class.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ pub struct ClassBuilder<'a> {
2929
extends: *mut ClassEntry,
3030
methods: Vec<FunctionEntry>,
3131
object_override: Option<unsafe extern "C" fn(class_type: *mut ClassEntry) -> *mut ZendObject>,
32-
properties: Vec<(&'a str, Zval, PropertyFlags)>,
33-
constants: Vec<(&'a str, Zval)>,
32+
properties: Vec<(String, Zval, PropertyFlags)>,
33+
constants: Vec<(String, Zval)>,
3434
}
3535

3636
impl<'a> ClassBuilder<'a> {
@@ -91,11 +91,12 @@ impl<'a> ClassBuilder<'a> {
9191
/// * `name` - The name of the property to add to the class.
9292
/// * `default` - The default value of the property.
9393
/// * `flags` - Flags relating to the property. See [`PropertyFlags`].
94-
#[allow(unused_mut, unused_variables)]
95-
pub fn property<T>(mut self, name: &'a str, default: T, flags: PropertyFlags) -> Self
96-
where
97-
T: Into<Zval>,
98-
{
94+
pub fn property(
95+
mut self,
96+
name: impl AsRef<str>,
97+
default: impl Into<Zval>,
98+
flags: PropertyFlags,
99+
) -> Self {
99100
let mut default = default.into();
100101

101102
if default.is_string() {
@@ -104,7 +105,8 @@ impl<'a> ClassBuilder<'a> {
104105
default.set_persistent_string(val);
105106
}
106107

107-
self.properties.push((name, default, flags));
108+
self.properties
109+
.push((name.as_ref().to_string(), default, flags));
108110
self
109111
}
110112

@@ -115,10 +117,7 @@ impl<'a> ClassBuilder<'a> {
115117
///
116118
/// * `name` - The name of the constant to add to the class.
117119
/// * `value` - The value of the constant.
118-
pub fn constant<T>(mut self, name: &'a str, value: T) -> Self
119-
where
120-
T: Into<Zval>,
121-
{
120+
pub fn constant(mut self, name: impl AsRef<str>, value: impl Into<Zval>) -> Self {
122121
let mut value = value.into();
123122

124123
if value.is_string() {
@@ -127,7 +126,7 @@ impl<'a> ClassBuilder<'a> {
127126
value.set_persistent_string(val);
128127
}
129128

130-
self.constants.push((name, value));
129+
self.constants.push((name.as_ref().to_string(), value));
131130
self
132131
}
133132

@@ -175,7 +174,7 @@ impl<'a> ClassBuilder<'a> {
175174
unsafe {
176175
zend_declare_property(
177176
class,
178-
c_str(name),
177+
c_str(&name),
179178
name.len() as _,
180179
&mut default,
181180
flags.bits() as _,
@@ -185,7 +184,7 @@ impl<'a> ClassBuilder<'a> {
185184

186185
for (name, value) in self.constants {
187186
let value = Box::into_raw(Box::new(value));
188-
unsafe { zend_declare_class_constant(class, c_str(name), name.len() as u64, value) };
187+
unsafe { zend_declare_class_constant(class, c_str(&name), name.len() as u64, value) };
189188
}
190189

191190
if let Some(object_override) = self.object_override {

src/php/types/array.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::{
1515
zend_hash_next_index_insert, zend_hash_str_del, zend_hash_str_find, zend_hash_str_update,
1616
HT_MIN_SIZE,
1717
},
18+
errors::{Error, Result},
1819
functions::c_str,
1920
};
2021

@@ -50,8 +51,19 @@ impl ZendHashTable {
5051
/// # Parameters
5152
///
5253
/// * `ptr` - The pointer of the actual hash table.
53-
pub(crate) fn from_ptr(ptr: *mut HashTable) -> Self {
54-
Self { ptr, free: false }
54+
/// * `free` - Whether the pointer should be freed when the resulting [`ZendHashTable`]
55+
/// goes out of scope.
56+
///
57+
/// # Safety
58+
///
59+
/// As a raw pointer is given this function is unsafe, you must ensure that the pointer is valid when calling
60+
/// the function. A simple null check is done but this is not sufficient in most cases.
61+
pub unsafe fn from_ptr(ptr: *mut HashTable, free: bool) -> Result<Self> {
62+
if ptr.is_null() {
63+
return Err(Error::InvalidPointer);
64+
}
65+
66+
Ok(Self { ptr, free })
5567
}
5668

5769
/// Returns the current number of elements in the array.
@@ -290,8 +302,9 @@ impl<'a> Iterator for Iter<'a> {
290302
let result = if let Some(val) = unsafe { self.pos.as_ref() } {
291303
// SAFETY: We can ensure safety further by checking if it is null before
292304
// converting it to a reference (val.key.as_ref() returns None if ptr == null)
293-
let str_key =
294-
unsafe { ZendString::from_ptr(val.key, false) }.and_then(|s| s.try_into().ok());
305+
let str_key = unsafe { ZendString::from_ptr(val.key, false) }
306+
.and_then(|s| s.try_into())
307+
.ok();
295308

296309
Some((val.h, str_key, &val.val))
297310
} else {

src/php/types/object.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! allowing users to store Rust data inside a PHP object.
33
44
use std::{
5+
convert::TryInto,
56
fmt::Debug,
67
mem,
78
ops::{Deref, DerefMut},
@@ -17,7 +18,7 @@ use crate::{
1718
php::{class::ClassEntry, execution_data::ExecutionData, types::string::ZendString},
1819
};
1920

20-
use super::zval::Zval;
21+
use super::{array::ZendHashTable, zval::Zval};
2122

2223
pub type ZendObject = zend_object;
2324
pub type ZendObjectHandlers = zend_object_handlers;
@@ -35,6 +36,18 @@ pub enum PropertyQuery {
3536
}
3637

3738
impl ZendObject {
39+
/// Attempts to retrieve the class name of the object.
40+
pub fn get_class_name(&self) -> Result<String> {
41+
let name = unsafe {
42+
ZendString::from_ptr(
43+
self.handlers()?.get_class_name.ok_or(Error::InvalidScope)?(self),
44+
false,
45+
)
46+
}?;
47+
48+
name.try_into()
49+
}
50+
3851
/// Attempts to read a property from the Object. Returns a result returning an
3952
/// immutable reference to the [`Zval`] if the property exists and can be read,
4053
/// and an [`Error`] otherwise.
@@ -104,16 +117,24 @@ impl ZendObject {
104117
pub fn has_property(&self, name: impl AsRef<str>, query: PropertyQuery) -> Result<bool> {
105118
let name = ZendString::new(name.as_ref(), false);
106119

107-
let x = unsafe {
120+
Ok(unsafe {
108121
self.handlers()?.has_property.ok_or(Error::InvalidScope)?(
109122
self.mut_ptr(),
110123
name.borrow_ptr(),
111124
query as _,
112125
std::ptr::null_mut(),
113126
)
114-
};
127+
} > 0)
128+
}
115129

116-
Ok(x > 0)
130+
/// Attempts to retrieve the properties of the object. Returned inside a Zend Hashtable.
131+
pub fn get_properties(&self) -> Result<ZendHashTable> {
132+
unsafe {
133+
ZendHashTable::from_ptr(
134+
self.handlers()?.get_properties.ok_or(Error::InvalidScope)?(self.mut_ptr()),
135+
false,
136+
)
137+
}
117138
}
118139

119140
/// Attempts to retrieve a reference to the object handlers.
@@ -131,6 +152,24 @@ impl ZendObject {
131152
}
132153
}
133154

155+
impl Debug for ZendObject {
156+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157+
let mut dbg = f.debug_struct(
158+
self.get_class_name()
159+
.unwrap_or_else(|_| "ZendObject".to_string())
160+
.as_str(),
161+
);
162+
163+
if let Ok(props) = self.get_properties() {
164+
for (id, key, val) in props.into_iter() {
165+
dbg.field(key.unwrap_or_else(|| id.to_string()).as_str(), val);
166+
}
167+
}
168+
169+
dbg.finish()
170+
}
171+
}
172+
134173
/// Implemented by the [`ZendObjectHandler`](ext_php_rs_derive::ZendObjectHandler) macro on a type T
135174
/// which is used as the T type for [`ZendClassObject`].
136175
/// Implements a function `create_object` which is passed to a PHP class entry to instantiate the
@@ -193,15 +232,15 @@ impl<T: Default> ZendClassObject<T> {
193232
&mut obj.std
194233
}
195234

196-
/// Attempts to retrieve the zend class object container from the
235+
/// Attempts to retrieve the Zend class object container from the
197236
/// zend object contained in the execution data of a function.
198237
///
199238
/// # Parameters
200239
///
201240
/// * `ex` - The execution data of the function.
202241
pub fn get(ex: &ExecutionData) -> Option<&'static mut Self> {
203242
// cast to u8 to work in terms of bytes
204-
let ptr = ex.This.object()? as *mut u8;
243+
let ptr = (ex.This.object()? as *const ZendObject) as *mut u8;
205244
let offset = std::mem::size_of::<T>();
206245
unsafe {
207246
let ptr = ptr.offset(0 - offset as isize);

src/php/types/string.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ impl ZendString {
6565
///
6666
/// As a raw pointer is given this function is unsafe, you must ensure the pointer is valid when calling
6767
/// the function. A simple null check is done but this is not sufficient in most places.
68-
pub unsafe fn from_ptr(ptr: *mut zend_string, free: bool) -> Option<Self> {
68+
pub unsafe fn from_ptr(ptr: *mut zend_string, free: bool) -> Result<Self> {
6969
if ptr.is_null() {
70-
return None;
70+
return Err(Error::InvalidPointer);
7171
}
7272

73-
Some(Self { ptr, free })
73+
Ok(Self { ptr, free })
7474
}
7575

7676
/// Releases the Zend string, returning the raw pointer to the `zend_string` object

src/php/types/zval.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ use std::{convert::TryFrom, fmt::Debug, ptr};
77
use crate::{
88
bindings::{
99
_call_user_function_impl, _zval_struct__bindgen_ty_1, _zval_struct__bindgen_ty_2,
10-
ext_php_rs_zend_string_release, zend_is_callable, zend_object, zend_resource, zend_value,
11-
zval,
10+
ext_php_rs_zend_string_release, zend_is_callable, zend_resource, zend_value, zval,
1211
},
1312
errors::{Error, Result},
1413
php::pack::Pack,
@@ -20,7 +19,7 @@ use crate::php::{
2019
types::{long::ZendLong, string::ZendString},
2120
};
2221

23-
use super::array::ZendHashTable;
22+
use super::{array::ZendHashTable, object::ZendObject};
2423

2524
/// Zend value. Represents most data types that are in the Zend engine.
2625
pub type Zval = zval;
@@ -122,18 +121,16 @@ impl<'a> Zval {
122121
/// Returns the value of the zval if it is an array.
123122
pub fn array(&self) -> Option<ZendHashTable> {
124123
if self.is_array() {
125-
Some(ZendHashTable::from_ptr(unsafe { self.value.arr }))
124+
unsafe { ZendHashTable::from_ptr(self.value.arr, false) }.ok()
126125
} else {
127126
None
128127
}
129128
}
130129

131130
/// Returns the value of the zval if it is an object.
132-
pub fn object(&self) -> Option<*mut zend_object> {
133-
// TODO: Can we improve this function? I haven't done much research into
134-
// objects so I don't know if this is the optimal way to return this.
131+
pub fn object(&self) -> Option<&mut ZendObject> {
135132
if self.is_object() {
136-
Some(unsafe { self.value.obj })
133+
unsafe { self.value.obj.as_mut() }
137134
} else {
138135
None
139136
}
@@ -379,9 +376,9 @@ impl<'a> Zval {
379376
///
380377
/// * `val` - The value to set the zval as.
381378
/// * `copy` - Whether to copy the object or pass as a reference.
382-
pub fn set_object(&mut self, val: *mut zend_object, _copy: bool) {
379+
pub fn set_object(&mut self, val: &ZendObject, _copy: bool) {
383380
self.u1.type_info = ZvalTypeFlags::ObjectEx.bits();
384-
self.value.obj = val;
381+
self.value.obj = (val as *const ZendObject) as *mut ZendObject;
385382
}
386383

387384
/// Sets the value of the zval as an array.

0 commit comments

Comments
 (0)