1use std::{ffi::c_void, mem::MaybeUninit, os::raw::c_int, ptr};
2
3use crate::{
4 class::RegisteredClass,
5 exception::PhpResult,
6 ffi::{
7 std_object_handlers, zend_is_true, zend_object_handlers, zend_object_std_dtor,
8 zend_std_get_properties, zend_std_has_property, zend_std_read_property,
9 zend_std_write_property,
10 },
11 flags::ZvalTypeFlags,
12 types::{ZendClassObject, ZendHashTable, ZendObject, ZendStr, Zval},
13};
14
15pub type ZendObjectHandlers = zend_object_handlers;
17
18impl ZendObjectHandlers {
19 pub fn new<T: RegisteredClass>() -> ZendObjectHandlers {
22 let mut this = MaybeUninit::uninit();
23
24 unsafe { Self::init::<T>(&mut *this.as_mut_ptr()) };
26
27 unsafe { this.assume_init() }
30 }
31
32 pub unsafe fn init<T: RegisteredClass>(ptr: *mut ZendObjectHandlers) {
44 std::ptr::copy_nonoverlapping(&std_object_handlers, ptr, 1);
45 let offset = ZendClassObject::<T>::std_offset();
46 (*ptr).offset = offset as _;
47 (*ptr).free_obj = Some(Self::free_obj::<T>);
48 (*ptr).read_property = Some(Self::read_property::<T>);
49 (*ptr).write_property = Some(Self::write_property::<T>);
50 (*ptr).get_properties = Some(Self::get_properties::<T>);
51 (*ptr).has_property = Some(Self::has_property::<T>);
52 }
53
54 unsafe extern "C" fn free_obj<T: RegisteredClass>(object: *mut ZendObject) {
55 let obj = object
56 .as_mut()
57 .and_then(|obj| ZendClassObject::<T>::from_zend_obj_mut(obj))
58 .expect("Invalid object pointer given for `free_obj`");
59
60 ptr::drop_in_place(&mut obj.obj);
62
63 zend_object_std_dtor(object)
64 }
65
66 unsafe extern "C" fn read_property<T: RegisteredClass>(
67 object: *mut ZendObject,
68 member: *mut ZendStr,
69 type_: c_int,
70 cache_slot: *mut *mut c_void,
71 rv: *mut Zval,
72 ) -> *mut Zval {
73 #[inline(always)]
74 unsafe fn internal<T: RegisteredClass>(
75 object: *mut ZendObject,
76 member: *mut ZendStr,
77 type_: c_int,
78 cache_slot: *mut *mut c_void,
79 rv: *mut Zval,
80 ) -> PhpResult<*mut Zval> {
81 let obj = object
82 .as_mut()
83 .and_then(|obj| ZendClassObject::<T>::from_zend_obj_mut(obj))
84 .ok_or("Invalid object pointer given")?;
85 let prop_name = member
86 .as_ref()
87 .ok_or("Invalid property name pointer given")?;
88 let self_ = &mut **obj;
89 let props = T::get_metadata().get_properties();
90 let prop = props.get(prop_name.as_str()?);
91
92 let rv_mut = rv.as_mut().ok_or("Invalid return zval given")?;
94 rv_mut.u1.type_info = ZvalTypeFlags::Null.bits();
95
96 Ok(match prop {
97 Some(prop) => {
98 prop.get(self_, rv_mut)?;
99 rv
100 }
101 None => zend_std_read_property(object, member, type_, cache_slot, rv),
102 })
103 }
104
105 match internal::<T>(object, member, type_, cache_slot, rv) {
106 Ok(rv) => rv,
107 Err(e) => {
108 let _ = e.throw();
109 (*rv).set_null();
110 rv
111 }
112 }
113 }
114
115 unsafe extern "C" fn write_property<T: RegisteredClass>(
116 object: *mut ZendObject,
117 member: *mut ZendStr,
118 value: *mut Zval,
119 cache_slot: *mut *mut c_void,
120 ) -> *mut Zval {
121 #[inline(always)]
122 unsafe fn internal<T: RegisteredClass>(
123 object: *mut ZendObject,
124 member: *mut ZendStr,
125 value: *mut Zval,
126 cache_slot: *mut *mut c_void,
127 ) -> PhpResult<*mut Zval> {
128 let obj = object
129 .as_mut()
130 .and_then(|obj| ZendClassObject::<T>::from_zend_obj_mut(obj))
131 .ok_or("Invalid object pointer given")?;
132 let prop_name = member
133 .as_ref()
134 .ok_or("Invalid property name pointer given")?;
135 let self_ = &mut **obj;
136 let props = T::get_metadata().get_properties();
137 let prop = props.get(prop_name.as_str()?);
138 let value_mut = value.as_mut().ok_or("Invalid return zval given")?;
139
140 Ok(match prop {
141 Some(prop) => {
142 prop.set(self_, value_mut)?;
143 value
144 }
145 None => zend_std_write_property(object, member, value, cache_slot),
146 })
147 }
148
149 match internal::<T>(object, member, value, cache_slot) {
150 Ok(rv) => rv,
151 Err(e) => {
152 let _ = e.throw();
153 value
154 }
155 }
156 }
157
158 unsafe extern "C" fn get_properties<T: RegisteredClass>(
159 object: *mut ZendObject,
160 ) -> *mut ZendHashTable {
161 #[inline(always)]
162 unsafe fn internal<T: RegisteredClass>(
163 object: *mut ZendObject,
164 props: &mut ZendHashTable,
165 ) -> PhpResult {
166 let obj = object
167 .as_mut()
168 .and_then(|obj| ZendClassObject::<T>::from_zend_obj_mut(obj))
169 .ok_or("Invalid object pointer given")?;
170 let self_ = &mut **obj;
171 let struct_props = T::get_metadata().get_properties();
172
173 for (name, val) in struct_props {
174 let mut zv = Zval::new();
175 if val.get(self_, &mut zv).is_err() {
176 continue;
177 }
178 props.insert(name, zv).map_err(|e| {
179 format!("Failed to insert value into properties hashtable: {e:?}")
180 })?;
181 }
182
183 Ok(())
184 }
185
186 let props = zend_std_get_properties(object)
187 .as_mut()
188 .or_else(|| Some(ZendHashTable::new().into_raw()))
189 .expect("Failed to get property hashtable");
190
191 if let Err(e) = internal::<T>(object, props) {
192 let _ = e.throw();
193 }
194
195 props
196 }
197
198 unsafe extern "C" fn has_property<T: RegisteredClass>(
199 object: *mut ZendObject,
200 member: *mut ZendStr,
201 has_set_exists: c_int,
202 cache_slot: *mut *mut c_void,
203 ) -> c_int {
204 #[inline(always)]
205 unsafe fn internal<T: RegisteredClass>(
206 object: *mut ZendObject,
207 member: *mut ZendStr,
208 has_set_exists: c_int,
209 cache_slot: *mut *mut c_void,
210 ) -> PhpResult<c_int> {
211 let obj = object
212 .as_mut()
213 .and_then(|obj| ZendClassObject::<T>::from_zend_obj_mut(obj))
214 .ok_or("Invalid object pointer given")?;
215 let prop_name = member
216 .as_ref()
217 .ok_or("Invalid property name pointer given")?;
218 let props = T::get_metadata().get_properties();
219 let prop = props.get(prop_name.as_str()?);
220 let self_ = &mut **obj;
221
222 match has_set_exists {
223 0 => {
226 if let Some(val) = prop {
227 let mut zv = Zval::new();
228 val.get(self_, &mut zv)?;
229 if !zv.is_null() {
230 return Ok(1);
231 }
232 }
233 }
234 1 => {
237 if let Some(val) = prop {
238 let mut zv = Zval::new();
239 val.get(self_, &mut zv)?;
240
241 cfg_if::cfg_if! {
242 if #[cfg(php84)] {
243 #[allow(clippy::unnecessary_mut_passed)]
244 if zend_is_true(&mut zv) {
245 return Ok(1);
246 }
247 } else {
248 #[allow(clippy::unnecessary_mut_passed)]
249 if zend_is_true(&mut zv) == 1 {
250 return Ok(1);
251 }
252 }
253 }
254 }
255 }
256 2 => {
259 if prop.is_some() {
260 return Ok(1);
261 }
262 }
263 _ => return Err(
264 "Invalid value given for `has_set_exists` in struct `has_property` function."
265 .into(),
266 ),
267 };
268
269 Ok(zend_std_has_property(
270 object,
271 member,
272 has_set_exists,
273 cache_slot,
274 ))
275 }
276
277 match internal::<T>(object, member, has_set_exists, cache_slot) {
278 Ok(rv) => rv,
279 Err(e) => {
280 let _ = e.throw();
281 0
282 }
283 }
284 }
285}