Skip to content

Commit de326e3

Browse files
Added ability to get and set properties (#39)
* Fixed adding properties to classes * Removed unused function from wrapper header * Moved property functions into `ZendObject` * Introduced `ZendString` wrapper for raw pointer * Set refcount of Zval to 0 when setting string properties * Added ability to check if a property exists Made some lifetimes explicit, check if property exists before attempting to get. * Removed unused file * Removed unused lifetimes Clippy was complaining, they were added to ensure that a `'static` lifetime wasn't returned.
1 parent 4d364d9 commit de326e3

File tree

13 files changed

+399
-170
lines changed

13 files changed

+399
-170
lines changed

example/skel/src/lib.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ use ext_php_rs::{
99
enums::DataType,
1010
exceptions::throw,
1111
execution_data::ExecutionData,
12-
flags::MethodFlags,
12+
flags::{MethodFlags, PropertyFlags},
1313
function::FunctionBuilder,
1414
module::{ModuleBuilder, ModuleEntry},
1515
types::{
16-
array::ZendHashTable, long::ZendLong, object::ZendClassObject, string::ZendString,
16+
array::ZendHashTable,
17+
long::ZendLong,
18+
object::{PropertyQuery, ZendClassObject},
19+
string::ZendString,
1720
zval::Zval,
1821
},
1922
},
@@ -57,7 +60,14 @@ impl Test {
5760

5861
pub extern "C" fn get(execute_data: &mut ExecutionData, _retval: &mut Zval) {
5962
let x = ZendClassObject::<Test>::get(execute_data).unwrap();
60-
dbg!(x.a);
63+
//let param = execute_data.get_property("value");
64+
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!"));
6171
}
6272

6373
pub extern "C" fn call(execute_data: &mut ExecutionData, _retval: &mut Zval) {
@@ -107,7 +117,7 @@ pub extern "C" fn module_init(_type: i32, module_number: i32) -> i32 {
107117
.build(),
108118
MethodFlags::Public,
109119
)
110-
// .property("value", "world", PropertyFlags::Protected)
120+
.property("value", "world", PropertyFlags::Public)
111121
.constant("TEST", "Hello world")
112122
.object_override::<Test>()
113123
.build();

example/skel/test.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
include __DIR__.'/vendor/autoload.php';
44

5-
$x = pack('f*', 1234, 5678, 9012);
6-
var_dump(unpack('l*', skel_unpack($x)));
7-
dd($x);
5+
//$y = new \stdClass;
6+
//$y->hello = 'asdf';
7+
8+
$x = new TestClass();
9+
var_dump($x);
10+
$x->get();
11+
var_dump($x);
12+
$x->value = null;
13+
$x->get();

src/errors.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub type Result<T> = std::result::Result<T, Error>;
77

88
/// The main error type which is passed by the library inside the custom
99
/// [`Result`] type.
10-
#[derive(Debug, Clone)]
10+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
1111
#[non_exhaustive]
1212
pub enum Error {
1313
/// An incorrect number of arguments was given to a PHP function.
@@ -16,22 +16,26 @@ pub enum Error {
1616
/// number of arguments expected, and the second representing the number of
1717
/// arguments that were received.
1818
IncorrectArguments(u32, u32),
19-
2019
/// There was an error converting a Zval into a primitive type.
2120
///
2221
/// The enum carries the data type of the Zval.
2322
ZvalConversion(DataType),
24-
2523
/// The type of the Zval is unknown.
2624
///
2725
/// The enum carries the integer representation of the type of Zval.
28-
UnknownDatatype(u8),
29-
26+
UnknownDatatype(u32),
3027
/// Attempted to convert a [`ZvalTypeFlags`] struct to a [`DataType`].
3128
/// The flags did not contain a datatype.
3229
///
3330
/// The enum carries the flags that were attempted to be converted to a [`DataType`].
3431
InvalidTypeToDatatype(ZvalTypeFlags),
32+
/// The function called was called in an invalid scope (calling class-related functions
33+
/// inside of a non-class bound function).
34+
InvalidScope,
35+
/// The pointer inside a given type was invalid, either null or pointing to garbage.
36+
InvalidPointer,
37+
/// The given property name does not exist.
38+
InvalidProperty,
3539
}
3640

3741
impl Display for Error {
@@ -51,6 +55,9 @@ impl Display for Error {
5155
Error::InvalidTypeToDatatype(dt) => {
5256
write!(f, "Type flags did not contain a datatype: {:?}", dt)
5357
}
58+
Error::InvalidScope => write!(f, "Invalid scope."),
59+
Error::InvalidPointer => write!(f, "Invalid pointer."),
60+
Error::InvalidProperty => write!(f, "Property does not exist on object."),
5461
}
5562
}
5663
}

src/php/class.rs

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{mem, ptr};
55
use crate::{
66
bindings::{
77
ext_php_rs_zend_string_release, zend_class_entry, zend_declare_class_constant,
8-
zend_register_internal_class_ex,
8+
zend_declare_property, zend_register_internal_class_ex,
99
},
1010
functions::c_str,
1111
};
@@ -29,7 +29,7 @@ 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)>,
32+
properties: Vec<(&'a str, Zval, PropertyFlags)>,
3333
constants: Vec<(&'a str, Zval)>,
3434
}
3535

@@ -44,17 +44,21 @@ impl<'a> ClassBuilder<'a> {
4444
where
4545
N: AsRef<str>,
4646
{
47-
let ptr = unsafe { libc::calloc(1, mem::size_of::<ClassEntry>()) } as *mut ClassEntry;
48-
let self_ = Self {
49-
ptr: unsafe { ptr.as_mut() }.unwrap(),
47+
let ptr = unsafe {
48+
(libc::calloc(1, mem::size_of::<ClassEntry>()) as *mut ClassEntry)
49+
.as_mut()
50+
.unwrap()
51+
};
52+
ptr.name = ZendString::new_interned(name).release();
53+
54+
Self {
55+
ptr,
5056
extends: ptr::null_mut(),
5157
methods: vec![],
5258
object_override: None,
53-
// properties: vec![],
59+
properties: vec![],
5460
constants: vec![],
55-
};
56-
self_.ptr.name = ZendString::new_interned(name);
57-
self_
61+
}
5862
}
5963

6064
/// Sets the class builder to extend another class.
@@ -79,8 +83,8 @@ impl<'a> ClassBuilder<'a> {
7983
self
8084
}
8185

82-
/// Adds a property to the class.
83-
/// The type of the property is defined by the type of the given default.
86+
/// Adds a property to the class. The initial type of the property is given by the type
87+
/// of the given default. Note that the user can change the type.
8488
///
8589
/// # Parameters
8690
///
@@ -92,17 +96,16 @@ impl<'a> ClassBuilder<'a> {
9296
where
9397
T: Into<Zval>,
9498
{
95-
panic!("Properties are currently not supported. See #16");
96-
// let mut default = default.into();
99+
let mut default = default.into();
97100

98-
// if default.is_string() {
99-
// let val = default.string().unwrap();
100-
// unsafe { ext_php_rs_zend_string_release(default.value.str_) };
101-
// default.set_persistent_string(val);
102-
// }
101+
if default.is_string() {
102+
let val = default.string().unwrap();
103+
unsafe { ext_php_rs_zend_string_release(default.value.str_) };
104+
default.set_persistent_string(val);
105+
}
103106

104-
// self.properties.push((name, default, flags));
105-
// self
107+
self.properties.push((name, default, flags));
108+
self
106109
}
107110

108111
/// Adds a constant to the class.
@@ -146,7 +149,10 @@ impl<'a> ClassBuilder<'a> {
146149
/// * `T` - The type which will override the Zend object. Must implement [`ZendObjectOverride`]
147150
/// which can be derived through the [`ZendObjectHandler`](ext_php_rs_derive::ZendObjectHandler)
148151
/// derive macro.
149-
pub fn object_override<T: ZendObjectOverride>(mut self) -> Self {
152+
pub fn object_override<T>(mut self) -> Self
153+
where
154+
T: ZendObjectOverride,
155+
{
150156
self.object_override = Some(T::create_object);
151157
self
152158
}
@@ -165,18 +171,17 @@ impl<'a> ClassBuilder<'a> {
165171

166172
unsafe { libc::free((self.ptr as *mut ClassEntry) as *mut libc::c_void) };
167173

168-
// DC: Commented out for now. Bug with properties.
169-
// for (name, mut default, flags) in self.properties {
170-
// unsafe {
171-
// zend_declare_property(
172-
// class,
173-
// c_str(name),
174-
// name.len() as u64,
175-
// &mut default,
176-
// flags.bits() as i32,
177-
// );
178-
// }
179-
// }
174+
for (name, mut default, flags) in self.properties {
175+
unsafe {
176+
zend_declare_property(
177+
class,
178+
c_str(name),
179+
name.len() as _,
180+
&mut default,
181+
flags.bits() as _,
182+
);
183+
}
184+
}
180185

181186
for (name, value) in self.constants {
182187
let value = Box::into_raw(Box::new(value));

src/php/enums.rs

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
};
1313

1414
/// Valid data types for PHP.
15-
#[derive(Clone, Copy, Debug)]
15+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
1616
#[repr(u32)]
1717
pub enum DataType {
1818
Undef = IS_UNDEF,
@@ -33,6 +33,17 @@ pub enum DataType {
3333
Void = IS_VOID,
3434
}
3535

36+
// TODO: Ideally want something like this
37+
// pub struct Type {
38+
// data_type: DataType,
39+
// is_refcounted: bool,
40+
// is_collectable: bool,
41+
// is_immutable: bool,
42+
// is_persistent: bool,
43+
// }
44+
//
45+
// impl From<u32> for Type { ... }
46+
3647
impl TryFrom<ZvalTypeFlags> for DataType {
3748
type Error = Error;
3849

@@ -64,12 +75,37 @@ impl TryFrom<ZvalTypeFlags> for DataType {
6475
}
6576
}
6677

67-
impl TryFrom<u8> for DataType {
78+
impl TryFrom<u32> for DataType {
6879
type Error = Error;
6980

70-
fn try_from(value: u8) -> Result<Self> {
71-
let flags = ZvalTypeFlags::from_bits(value.into()).ok_or(Error::UnknownDatatype(value))?;
72-
DataType::try_from(flags)
81+
#[allow(clippy::bad_bit_mask)]
82+
fn try_from(value: u32) -> Result<Self> {
83+
macro_rules! contains {
84+
($c: ident, $t: ident) => {
85+
if (value & $c) == $c {
86+
return Ok(DataType::$t);
87+
}
88+
};
89+
}
90+
91+
contains!(IS_VOID, Void);
92+
contains!(IS_CALLABLE, Callable);
93+
contains!(IS_CONSTANT_AST, ConstantExpression);
94+
contains!(IS_CONSTANT_AST, ConstantExpression);
95+
contains!(IS_CONSTANT_AST, ConstantExpression);
96+
contains!(IS_REFERENCE, Reference);
97+
contains!(IS_RESOURCE, Resource);
98+
contains!(IS_OBJECT, Object);
99+
contains!(IS_ARRAY, Array);
100+
contains!(IS_STRING, String);
101+
contains!(IS_DOUBLE, Double);
102+
contains!(IS_LONG, Long);
103+
contains!(IS_TRUE, True);
104+
contains!(IS_FALSE, False);
105+
contains!(IS_NULL, Null);
106+
contains!(IS_UNDEF, Undef);
107+
108+
Err(Error::UnknownDatatype(value))
73109
}
74110
}
75111

@@ -93,3 +129,47 @@ impl Display for DataType {
93129
}
94130
}
95131
}
132+
133+
#[cfg(test)]
134+
mod tests {
135+
use super::DataType;
136+
use crate::bindings::{
137+
IS_ARRAY, IS_ARRAY_EX, IS_CALLABLE, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE,
138+
IS_FALSE, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_REFERENCE,
139+
IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, IS_STRING_EX, IS_TRUE, IS_UNDEF,
140+
IS_VOID,
141+
};
142+
use std::convert::TryFrom;
143+
144+
#[test]
145+
fn test_datatype() {
146+
macro_rules! test {
147+
($c: ident, $t: ident) => {
148+
assert_eq!(DataType::try_from($c), Ok(DataType::$t));
149+
};
150+
}
151+
152+
test!(IS_UNDEF, Undef);
153+
test!(IS_NULL, Null);
154+
test!(IS_FALSE, False);
155+
test!(IS_TRUE, True);
156+
test!(IS_LONG, Long);
157+
test!(IS_DOUBLE, Double);
158+
test!(IS_STRING, String);
159+
test!(IS_ARRAY, Array);
160+
test!(IS_OBJECT, Object);
161+
test!(IS_RESOURCE, Resource);
162+
test!(IS_REFERENCE, Reference);
163+
test!(IS_CONSTANT_AST, ConstantExpression);
164+
test!(IS_CALLABLE, Callable);
165+
test!(IS_VOID, Void);
166+
167+
test!(IS_INTERNED_STRING_EX, String);
168+
test!(IS_STRING_EX, String);
169+
test!(IS_ARRAY_EX, Array);
170+
test!(IS_OBJECT_EX, Object);
171+
test!(IS_RESOURCE_EX, Resource);
172+
test!(IS_REFERENCE_EX, Reference);
173+
test!(IS_CONSTANT_AST_EX, ConstantExpression);
174+
}
175+
}

0 commit comments

Comments
 (0)