1use std::{convert::TryInto, fmt::Debug, ops::DerefMut, os::raw::c_char};
5
6use crate::{
7 boxed::{ZBox, ZBoxable},
8 class::RegisteredClass,
9 convert::{FromZendObject, FromZval, FromZvalMut, IntoZval, IntoZvalDyn},
10 error::{Error, Result},
11 ffi::{
12 ext_php_rs_zend_object_release, object_properties_init, zend_call_known_function,
13 zend_function, zend_hash_str_find_ptr_lc, zend_object, zend_objects_new, HashTable,
14 ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, ZEND_PROPERTY_ISSET,
15 },
16 flags::DataType,
17 rc::PhpRc,
18 types::{ZendClassObject, ZendStr, Zval},
19 zend::{ce, ClassEntry, ExecutorGlobals, ZendObjectHandlers},
20};
21
22pub type ZendObject = zend_object;
29
30impl ZendObject {
31 pub fn new(ce: &ClassEntry) -> ZBox<Self> {
42 unsafe {
45 let ptr = match ce.__bindgen_anon_2.create_object {
46 None => {
47 let ptr = zend_objects_new(ce as *const _ as *mut _);
48 if ptr.is_null() {
49 panic!("Failed to allocate memory for Zend object")
50 }
51 object_properties_init(ptr, ce as *const _ as *mut _);
52 ptr
53 }
54 Some(v) => v(ce as *const _ as *mut _),
55 };
56
57 ZBox::from_raw(
58 ptr.as_mut()
59 .expect("Failed to allocate memory for Zend object"),
60 )
61 }
62 }
63
64 pub fn new_stdclass() -> ZBox<Self> {
82 Self::new(ce::stdclass())
85 }
86
87 pub fn from_class_object<T: RegisteredClass>(obj: ZBox<ZendClassObject<T>>) -> ZBox<Self> {
90 let this = obj.into_raw();
91 unsafe { ZBox::from_raw(this.get_mut_zend_obj()) }
93 }
94
95 pub fn get_class_entry(&self) -> &'static ClassEntry {
101 unsafe { self.ce.as_ref() }.expect("Could not retrieve class entry.")
104 }
105
106 pub fn get_class_name(&self) -> Result<String> {
108 unsafe {
109 self.handlers()?
110 .get_class_name
111 .and_then(|f| f(self).as_ref())
112 .ok_or(Error::InvalidScope)
113 .and_then(|s| s.try_into())
114 }
115 }
116
117 pub fn instance_of(&self, ce: &ClassEntry) -> bool {
125 self.get_class_entry().instance_of(ce)
126 }
127
128 pub fn is_instance<T: RegisteredClass>(&self) -> bool {
133 (self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _))
134 }
135
136 pub fn is_traversable(&self) -> bool {
142 self.instance_of(ce::traversable())
143 }
144
145 #[inline(always)]
146 pub fn try_call_method(&self, name: &str, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
147 let mut retval = Zval::new();
148 let len = params.len();
149 let params = params
150 .into_iter()
151 .map(|val| val.as_zval(false))
152 .collect::<Result<Vec<_>>>()?;
153 let packed = params.into_boxed_slice();
154
155 unsafe {
156 let res = zend_hash_str_find_ptr_lc(
157 &(*self.ce).function_table,
158 name.as_ptr() as *const c_char,
159 name.len(),
160 ) as *mut zend_function;
161 if res.is_null() {
162 return Err(Error::Callable);
163 }
164 zend_call_known_function(
165 res,
166 self as *const _ as *mut _,
167 self.ce,
168 &mut retval,
169 len as _,
170 packed.as_ptr() as *mut _,
171 std::ptr::null_mut(),
172 )
173 };
174
175 Ok(retval)
176 }
177 pub fn get_property<'a, T>(&'a self, name: &str) -> Result<T>
186 where
187 T: FromZval<'a>,
188 {
189 if !self.has_property(name, PropertyQuery::Exists)? {
190 return Err(Error::InvalidProperty);
191 }
192
193 let mut name = ZendStr::new(name, false);
194 let mut rv = Zval::new();
195
196 let zv = unsafe {
197 self.handlers()?.read_property.ok_or(Error::InvalidScope)?(
198 self.mut_ptr(),
199 name.deref_mut(),
200 1,
201 std::ptr::null_mut(),
202 &mut rv,
203 )
204 .as_ref()
205 }
206 .ok_or(Error::InvalidScope)?;
207
208 T::from_zval(zv).ok_or_else(|| Error::ZvalConversion(zv.get_type()))
209 }
210
211 pub fn set_property(&mut self, name: &str, value: impl IntoZval) -> Result<()> {
218 let mut name = ZendStr::new(name, false);
219 let mut value = value.into_zval(false)?;
220
221 unsafe {
222 self.handlers()?.write_property.ok_or(Error::InvalidScope)?(
223 self,
224 name.deref_mut(),
225 &mut value,
226 std::ptr::null_mut(),
227 )
228 .as_ref()
229 }
230 .ok_or(Error::InvalidScope)?;
231 Ok(())
232 }
233
234 pub fn has_property(&self, name: &str, query: PropertyQuery) -> Result<bool> {
243 let mut name = ZendStr::new(name, false);
244
245 Ok(unsafe {
246 self.handlers()?.has_property.ok_or(Error::InvalidScope)?(
247 self.mut_ptr(),
248 name.deref_mut(),
249 query as _,
250 std::ptr::null_mut(),
251 )
252 } > 0)
253 }
254
255 pub fn get_properties(&self) -> Result<&HashTable> {
258 unsafe {
259 self.handlers()?
260 .get_properties
261 .and_then(|props| props(self.mut_ptr()).as_ref())
262 .ok_or(Error::InvalidScope)
263 }
264 }
265
266 pub fn extract<'a, T>(&'a self) -> Result<T>
270 where
271 T: FromZendObject<'a>,
272 {
273 T::from_zend_object(self)
274 }
275
276 #[inline]
284 pub fn get_id(&self) -> u32 {
285 self.handle
286 }
287
288 pub fn hash(&self) -> String {
296 format!("{:016x}0000000000000000", self.handle)
297 }
298
299 #[inline]
301 unsafe fn handlers(&self) -> Result<&ZendObjectHandlers> {
302 self.handlers.as_ref().ok_or(Error::InvalidScope)
303 }
304
305 #[inline]
309 fn mut_ptr(&self) -> *mut Self {
310 (self as *const Self) as *mut Self
311 }
312}
313
314unsafe impl ZBoxable for ZendObject {
315 fn free(&mut self) {
316 unsafe { ext_php_rs_zend_object_release(self) }
317 }
318}
319
320impl Debug for ZendObject {
321 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
322 let mut dbg = f.debug_struct(
323 self.get_class_name()
324 .unwrap_or_else(|_| "ZendObject".to_string())
325 .as_str(),
326 );
327
328 if let Ok(props) = self.get_properties() {
329 for (key, val) in props.iter() {
330 dbg.field(key.to_string().as_str(), val);
331 }
332 }
333
334 dbg.finish()
335 }
336}
337
338impl<'a> FromZval<'a> for &'a ZendObject {
339 const TYPE: DataType = DataType::Object(None);
340
341 fn from_zval(zval: &'a Zval) -> Option<Self> {
342 zval.object()
343 }
344}
345
346impl<'a> FromZvalMut<'a> for &'a mut ZendObject {
347 const TYPE: DataType = DataType::Object(None);
348
349 fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
350 zval.object_mut()
351 }
352}
353
354impl IntoZval for ZBox<ZendObject> {
355 const TYPE: DataType = DataType::Object(None);
356
357 #[inline]
358 fn set_zval(mut self, zv: &mut Zval, _: bool) -> Result<()> {
359 self.dec_count();
363 zv.set_object(self.into_raw());
364 Ok(())
365 }
366}
367
368impl IntoZval for &mut ZendObject {
369 const TYPE: DataType = DataType::Object(None);
370
371 #[inline]
372 fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
373 zv.set_object(self);
374 Ok(())
375 }
376}
377
378impl FromZendObject<'_> for String {
379 fn from_zend_object(obj: &ZendObject) -> Result<Self> {
380 let mut ret = Zval::new();
381 unsafe {
382 zend_call_known_function(
383 (*obj.ce).__tostring,
384 obj as *const _ as *mut _,
385 obj.ce,
386 &mut ret,
387 0,
388 std::ptr::null_mut(),
389 std::ptr::null_mut(),
390 );
391 }
392
393 if let Some(err) = ExecutorGlobals::take_exception() {
394 let class_name = obj.get_class_name();
396 panic!(
397 "Uncaught exception during call to {}::__toString(): {:?}",
398 class_name.expect("unable to determine class name"),
399 err
400 );
401 } else if let Some(output) = ret.extract() {
402 Ok(output)
403 } else {
404 let class_name = obj.get_class_name();
406 panic!(
407 "{}::__toString() must return a string",
408 class_name.expect("unable to determine class name"),
409 );
410 }
411 }
412}
413
414impl<T: RegisteredClass> From<ZBox<ZendClassObject<T>>> for ZBox<ZendObject> {
415 #[inline]
416 fn from(obj: ZBox<ZendClassObject<T>>) -> Self {
417 ZendObject::from_class_object(obj)
418 }
419}
420
421#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
423#[repr(u32)]
424pub enum PropertyQuery {
425 Isset = ZEND_PROPERTY_ISSET,
427 NotEmpty = ZEND_ISEMPTY,
429 Exists = ZEND_PROPERTY_EXISTS,
431}