ext_php_rs/types/zval.rs
1//! The base value in PHP. A Zval can contain any PHP type, and the type that it
2//! contains is determined by a property inside the struct. The content of the
3//! Zval is stored in a union.
4
5use std::{convert::TryInto, ffi::c_void, fmt::Debug, ptr};
6
7use crate::types::iterable::Iterable;
8use crate::types::ZendIterator;
9use crate::{
10    binary::Pack,
11    binary_slice::PackSlice,
12    boxed::ZBox,
13    convert::{FromZval, FromZvalMut, IntoZval, IntoZvalDyn},
14    error::{Error, Result},
15    ffi::{
16        _zval_struct__bindgen_ty_1, _zval_struct__bindgen_ty_2, zend_is_callable,
17        zend_is_identical, zend_is_iterable, zend_resource, zend_value, zval, zval_ptr_dtor,
18    },
19    flags::DataType,
20    flags::ZvalTypeFlags,
21    rc::PhpRc,
22    types::{ZendCallable, ZendHashTable, ZendLong, ZendObject, ZendStr},
23};
24
25/// A zend value. This is the primary storage container used throughout the Zend
26/// engine.
27///
28/// A zval can be thought of as a Rust enum, a type that can contain different
29/// values such as integers, strings, objects etc.
30pub type Zval = zval;
31
32// TODO(david): can we make zval send+sync? main problem is that refcounted
33// types do not have atomic refcounters, so technically two threads could
34// reference the same object and attempt to modify refcounter at the same time.
35// need to look into how ZTS works.
36
37// unsafe impl Send for Zval {}
38// unsafe impl Sync for Zval {}
39
40impl Zval {
41    /// Creates a new, empty zval.
42    #[must_use]
43    pub const fn new() -> Self {
44        Self {
45            value: zend_value {
46                ptr: ptr::null_mut(),
47            },
48            #[allow(clippy::used_underscore_items)]
49            u1: _zval_struct__bindgen_ty_1 {
50                type_info: DataType::Null.as_u32(),
51            },
52            #[allow(clippy::used_underscore_items)]
53            u2: _zval_struct__bindgen_ty_2 { next: 0 },
54        }
55    }
56
57    /// Creates a null zval
58    #[must_use]
59    pub fn null() -> Zval {
60        let mut zval = Zval::new();
61        zval.set_null();
62        zval
63    }
64
65    /// Dereference the zval, if it is a reference.
66    #[must_use]
67    pub fn dereference(&self) -> &Self {
68        self.reference().or_else(|| self.indirect()).unwrap_or(self)
69    }
70
71    /// Dereference the zval mutable, if it is a reference.
72    ///
73    /// # Panics
74    ///
75    /// Panics if a mutable reference to the zval is not possible.
76    pub fn dereference_mut(&mut self) -> &mut Self {
77        // TODO: probably more ZTS work is needed here
78        if self.is_reference() {
79            #[allow(clippy::unwrap_used)]
80            return self.reference_mut().unwrap();
81        }
82        if self.is_indirect() {
83            #[allow(clippy::unwrap_used)]
84            return self.indirect_mut().unwrap();
85        }
86        self
87    }
88
89    /// Returns the value of the zval if it is a long.
90    #[must_use]
91    pub fn long(&self) -> Option<ZendLong> {
92        if self.is_long() {
93            Some(unsafe { self.value.lval })
94        } else {
95            None
96        }
97    }
98
99    /// Returns the value of the zval if it is a bool.
100    #[must_use]
101    pub fn bool(&self) -> Option<bool> {
102        if self.is_true() {
103            Some(true)
104        } else if self.is_false() {
105            Some(false)
106        } else {
107            None
108        }
109    }
110
111    /// Returns the value of the zval if it is a double.
112    #[must_use]
113    pub fn double(&self) -> Option<f64> {
114        if self.is_double() {
115            Some(unsafe { self.value.dval })
116        } else {
117            None
118        }
119    }
120
121    /// Returns the value of the zval as a zend string, if it is a string.
122    ///
123    /// Note that this functions output will not be the same as
124    /// [`string()`](#method.string), as this function does not attempt to
125    /// convert other types into a [`String`].
126    #[must_use]
127    pub fn zend_str(&self) -> Option<&ZendStr> {
128        if self.is_string() {
129            unsafe { self.value.str_.as_ref() }
130        } else {
131            None
132        }
133    }
134
135    /// Returns the value of the zval if it is a string.
136    ///
137    /// [`str()`]: #method.str
138    pub fn string(&self) -> Option<String> {
139        self.str().map(ToString::to_string)
140    }
141
142    /// Returns the value of the zval if it is a string.
143    ///
144    /// Note that this functions output will not be the same as
145    /// [`string()`](#method.string), as this function does not attempt to
146    /// convert other types into a [`String`], as it could not pass back a
147    /// [`&str`] in those cases.
148    #[must_use]
149    pub fn str(&self) -> Option<&str> {
150        self.zend_str().and_then(|zs| zs.as_str().ok())
151    }
152
153    /// Returns the value of the zval if it is a string and can be unpacked into
154    /// a vector of a given type. Similar to the [`unpack`] function in PHP,
155    /// except you can only unpack one type.
156    ///
157    /// # Safety
158    ///
159    /// There is no way to tell if the data stored in the string is actually of
160    /// the given type. The results of this function can also differ from
161    /// platform-to-platform due to the different representation of some
162    /// types on different platforms. Consult the [`pack`] function
163    /// documentation for more details.
164    ///
165    /// [`pack`]: https://www.php.net/manual/en/function.pack.php
166    /// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
167    pub fn binary<T: Pack>(&self) -> Option<Vec<T>> {
168        self.zend_str().map(T::unpack_into)
169    }
170
171    /// Returns the value of the zval if it is a string and can be unpacked into
172    /// a slice of a given type. Similar to the [`unpack`] function in PHP,
173    /// except you can only unpack one type.
174    ///
175    /// This function is similar to [`Zval::binary`] except that a slice is
176    /// returned instead of a vector, meaning the contents of the string is
177    /// not copied.
178    ///
179    /// # Safety
180    ///
181    /// There is no way to tell if the data stored in the string is actually of
182    /// the given type. The results of this function can also differ from
183    /// platform-to-platform due to the different representation of some
184    /// types on different platforms. Consult the [`pack`] function
185    /// documentation for more details.
186    ///
187    /// [`pack`]: https://www.php.net/manual/en/function.pack.php
188    /// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
189    pub fn binary_slice<T: PackSlice>(&self) -> Option<&[T]> {
190        self.zend_str().map(T::unpack_into)
191    }
192
193    /// Returns the value of the zval if it is a resource.
194    #[must_use]
195    pub fn resource(&self) -> Option<*mut zend_resource> {
196        // TODO: Can we improve this function? I haven't done much research into
197        // resources so I don't know if this is the optimal way to return this.
198        if self.is_resource() {
199            Some(unsafe { self.value.res })
200        } else {
201            None
202        }
203    }
204
205    /// Returns an immutable reference to the underlying zval hashtable if the
206    /// zval contains an array.
207    #[must_use]
208    pub fn array(&self) -> Option<&ZendHashTable> {
209        if self.is_array() {
210            unsafe { self.value.arr.as_ref() }
211        } else {
212            None
213        }
214    }
215
216    /// Returns a mutable reference to the underlying zval hashtable if the zval
217    /// contains an array.
218    pub fn array_mut(&mut self) -> Option<&mut ZendHashTable> {
219        if self.is_array() {
220            unsafe { self.value.arr.as_mut() }
221        } else {
222            None
223        }
224    }
225
226    /// Returns the value of the zval if it is an object.
227    #[must_use]
228    pub fn object(&self) -> Option<&ZendObject> {
229        if self.is_object() {
230            unsafe { self.value.obj.as_ref() }
231        } else {
232            None
233        }
234    }
235
236    /// Returns a mutable reference to the object contained in the [`Zval`], if
237    /// any.
238    pub fn object_mut(&mut self) -> Option<&mut ZendObject> {
239        if self.is_object() {
240            unsafe { self.value.obj.as_mut() }
241        } else {
242            None
243        }
244    }
245
246    /// Attempts to call a method on the object contained in the zval.
247    ///
248    /// # Errors
249    ///
250    /// * Returns an error if the [`Zval`] is not an object.
251    // TODO: Measure this
252    #[allow(clippy::inline_always)]
253    #[inline(always)]
254    pub fn try_call_method(&self, name: &str, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
255        self.object()
256            .ok_or(Error::Object)?
257            .try_call_method(name, params)
258    }
259
260    /// Returns the value of the zval if it is an internal indirect reference.
261    #[must_use]
262    pub fn indirect(&self) -> Option<&Zval> {
263        if self.is_indirect() {
264            Some(unsafe { &*(self.value.zv.cast::<Zval>()) })
265        } else {
266            None
267        }
268    }
269
270    /// Returns a mutable reference to the zval if it is an internal indirect
271    /// reference.
272    // TODO: Verify if this is safe to use, as it allows mutating the
273    // hashtable while only having a reference to it. #461
274    #[allow(clippy::mut_from_ref)]
275    #[must_use]
276    pub fn indirect_mut(&self) -> Option<&mut Zval> {
277        if self.is_indirect() {
278            Some(unsafe { &mut *(self.value.zv.cast::<Zval>()) })
279        } else {
280            None
281        }
282    }
283
284    /// Returns the value of the zval if it is a reference.
285    #[must_use]
286    pub fn reference(&self) -> Option<&Zval> {
287        if self.is_reference() {
288            Some(&unsafe { self.value.ref_.as_ref() }?.val)
289        } else {
290            None
291        }
292    }
293
294    /// Returns a mutable reference to the underlying zval if it is a reference.
295    pub fn reference_mut(&mut self) -> Option<&mut Zval> {
296        if self.is_reference() {
297            Some(&mut unsafe { self.value.ref_.as_mut() }?.val)
298        } else {
299            None
300        }
301    }
302
303    /// Returns the value of the zval if it is callable.
304    #[must_use]
305    pub fn callable(&self) -> Option<ZendCallable<'_>> {
306        // The Zval is checked if it is callable in the `new` function.
307        ZendCallable::new(self).ok()
308    }
309
310    /// Returns an iterator over the zval if it is traversable.
311    #[must_use]
312    pub fn traversable(&self) -> Option<&mut ZendIterator> {
313        if self.is_traversable() {
314            self.object()?.get_class_entry().get_iterator(self, false)
315        } else {
316            None
317        }
318    }
319
320    /// Returns an iterable over the zval if it is an array or traversable. (is
321    /// iterable)
322    #[must_use]
323    pub fn iterable(&self) -> Option<Iterable<'_>> {
324        if self.is_iterable() {
325            Iterable::from_zval(self)
326        } else {
327            None
328        }
329    }
330
331    /// Returns the value of the zval if it is a pointer.
332    ///
333    /// # Safety
334    ///
335    /// The caller must ensure that the pointer contained in the zval is in fact
336    /// a pointer to an instance of `T`, as the zval has no way of defining
337    /// the type of pointer.
338    #[must_use]
339    pub unsafe fn ptr<T>(&self) -> Option<*mut T> {
340        if self.is_ptr() {
341            Some(self.value.ptr.cast::<T>())
342        } else {
343            None
344        }
345    }
346
347    /// Attempts to call the zval as a callable with a list of arguments to pass
348    /// to the function. Note that a thrown exception inside the callable is
349    /// not detectable, therefore you should check if the return value is
350    /// valid rather than unwrapping. Returns a result containing the return
351    /// value of the function, or an error.
352    ///
353    /// You should not call this function directly, rather through the
354    /// [`call_user_func`] macro.
355    ///
356    /// # Parameters
357    ///
358    /// * `params` - A list of parameters to call the function with.
359    ///
360    /// # Errors
361    ///
362    /// * Returns an error if the [`Zval`] is not callable.
363    // TODO: Measure this
364    #[allow(clippy::inline_always)]
365    #[inline(always)]
366    pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
367        self.callable().ok_or(Error::Callable)?.try_call(params)
368    }
369
370    /// Returns the type of the Zval.
371    #[must_use]
372    pub fn get_type(&self) -> DataType {
373        DataType::from(u32::from(unsafe { self.u1.v.type_ }))
374    }
375
376    /// Returns true if the zval is a long, false otherwise.
377    #[must_use]
378    pub fn is_long(&self) -> bool {
379        self.get_type() == DataType::Long
380    }
381
382    /// Returns true if the zval is null, false otherwise.
383    #[must_use]
384    pub fn is_null(&self) -> bool {
385        self.get_type() == DataType::Null
386    }
387
388    /// Returns true if the zval is true, false otherwise.
389    #[must_use]
390    pub fn is_true(&self) -> bool {
391        self.get_type() == DataType::True
392    }
393
394    /// Returns true if the zval is false, false otherwise.
395    #[must_use]
396    pub fn is_false(&self) -> bool {
397        self.get_type() == DataType::False
398    }
399
400    /// Returns true if the zval is a bool, false otherwise.
401    #[must_use]
402    pub fn is_bool(&self) -> bool {
403        self.is_true() || self.is_false()
404    }
405
406    /// Returns true if the zval is a double, false otherwise.
407    #[must_use]
408    pub fn is_double(&self) -> bool {
409        self.get_type() == DataType::Double
410    }
411
412    /// Returns true if the zval is a string, false otherwise.
413    #[must_use]
414    pub fn is_string(&self) -> bool {
415        self.get_type() == DataType::String
416    }
417
418    /// Returns true if the zval is a resource, false otherwise.
419    #[must_use]
420    pub fn is_resource(&self) -> bool {
421        self.get_type() == DataType::Resource
422    }
423
424    /// Returns true if the zval is an array, false otherwise.
425    #[must_use]
426    pub fn is_array(&self) -> bool {
427        self.get_type() == DataType::Array
428    }
429
430    /// Returns true if the zval is an object, false otherwise.
431    #[must_use]
432    pub fn is_object(&self) -> bool {
433        matches!(self.get_type(), DataType::Object(_))
434    }
435
436    /// Returns true if the zval is a reference, false otherwise.
437    #[must_use]
438    pub fn is_reference(&self) -> bool {
439        self.get_type() == DataType::Reference
440    }
441
442    /// Returns true if the zval is a reference, false otherwise.
443    #[must_use]
444    pub fn is_indirect(&self) -> bool {
445        self.get_type() == DataType::Indirect
446    }
447
448    /// Returns true if the zval is callable, false otherwise.
449    #[must_use]
450    pub fn is_callable(&self) -> bool {
451        let ptr: *const Self = self;
452        unsafe { zend_is_callable(ptr.cast_mut(), 0, std::ptr::null_mut()) }
453    }
454
455    /// Checks if the zval is identical to another one.
456    /// This works like `===` in php.
457    ///
458    /// # Parameters
459    ///
460    /// * `other` - The the zval to check identity against.
461    #[must_use]
462    pub fn is_identical(&self, other: &Self) -> bool {
463        let self_p: *const Self = self;
464        let other_p: *const Self = other;
465        unsafe { zend_is_identical(self_p.cast_mut(), other_p.cast_mut()) }
466    }
467
468    /// Returns true if the zval is traversable, false otherwise.
469    #[must_use]
470    pub fn is_traversable(&self) -> bool {
471        match self.object() {
472            None => false,
473            Some(obj) => obj.is_traversable(),
474        }
475    }
476
477    /// Returns true if the zval is iterable (array or traversable), false
478    /// otherwise.
479    #[must_use]
480    pub fn is_iterable(&self) -> bool {
481        let ptr: *const Self = self;
482        unsafe { zend_is_iterable(ptr.cast_mut()) }
483    }
484
485    /// Returns true if the zval contains a pointer, false otherwise.
486    #[must_use]
487    pub fn is_ptr(&self) -> bool {
488        self.get_type() == DataType::Ptr
489    }
490
491    /// Sets the value of the zval as a string. Returns nothing in a result when
492    /// successful.
493    ///
494    /// # Parameters
495    ///
496    /// * `val` - The value to set the zval as.
497    /// * `persistent` - Whether the string should persist between requests.
498    ///
499    /// # Errors
500    ///
501    /// Never returns an error.
502    // TODO: Check if we can drop the result here.
503    pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()> {
504        self.set_zend_string(ZendStr::new(val, persistent));
505        Ok(())
506    }
507
508    /// Sets the value of the zval as a Zend string.
509    ///
510    /// # Parameters
511    ///
512    /// * `val` - String content.
513    pub fn set_zend_string(&mut self, val: ZBox<ZendStr>) {
514        self.change_type(ZvalTypeFlags::StringEx);
515        self.value.str_ = val.into_raw();
516    }
517
518    /// Sets the value of the zval as a binary string, which is represented in
519    /// Rust as a vector.
520    ///
521    /// # Parameters
522    ///
523    /// * `val` - The value to set the zval as.
524    pub fn set_binary<T: Pack>(&mut self, val: Vec<T>) {
525        self.change_type(ZvalTypeFlags::StringEx);
526        let ptr = T::pack_into(val);
527        self.value.str_ = ptr;
528    }
529
530    /// Sets the value of the zval as a interned string. Returns nothing in a
531    /// result when successful.
532    ///
533    /// # Parameters
534    ///
535    /// * `val` - The value to set the zval as.
536    /// * `persistent` - Whether the string should persist between requests.
537    ///
538    /// # Errors
539    ///
540    /// Never returns an error.
541    // TODO: Check if we can drop the result here.
542    pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()> {
543        self.set_zend_string(ZendStr::new_interned(val, persistent));
544        Ok(())
545    }
546
547    /// Sets the value of the zval as a long.
548    ///
549    /// # Parameters
550    ///
551    /// * `val` - The value to set the zval as.
552    pub fn set_long<T: Into<ZendLong>>(&mut self, val: T) {
553        self.internal_set_long(val.into());
554    }
555
556    fn internal_set_long(&mut self, val: ZendLong) {
557        self.change_type(ZvalTypeFlags::Long);
558        self.value.lval = val;
559    }
560
561    /// Sets the value of the zval as a double.
562    ///
563    /// # Parameters
564    ///
565    /// * `val` - The value to set the zval as.
566    pub fn set_double<T: Into<f64>>(&mut self, val: T) {
567        self.internal_set_double(val.into());
568    }
569
570    fn internal_set_double(&mut self, val: f64) {
571        self.change_type(ZvalTypeFlags::Double);
572        self.value.dval = val;
573    }
574
575    /// Sets the value of the zval as a boolean.
576    ///
577    /// # Parameters
578    ///
579    /// * `val` - The value to set the zval as.
580    pub fn set_bool<T: Into<bool>>(&mut self, val: T) {
581        self.internal_set_bool(val.into());
582    }
583
584    fn internal_set_bool(&mut self, val: bool) {
585        self.change_type(if val {
586            ZvalTypeFlags::True
587        } else {
588            ZvalTypeFlags::False
589        });
590    }
591
592    /// Sets the value of the zval as null.
593    ///
594    /// This is the default of a zval.
595    pub fn set_null(&mut self) {
596        self.change_type(ZvalTypeFlags::Null);
597    }
598
599    /// Sets the value of the zval as a resource.
600    ///
601    /// # Parameters
602    ///
603    /// * `val` - The value to set the zval as.
604    pub fn set_resource(&mut self, val: *mut zend_resource) {
605        self.change_type(ZvalTypeFlags::ResourceEx);
606        self.value.res = val;
607    }
608
609    /// Sets the value of the zval as a reference to an object.
610    ///
611    /// # Parameters
612    ///
613    /// * `val` - The value to set the zval as.
614    pub fn set_object(&mut self, val: &mut ZendObject) {
615        self.change_type(ZvalTypeFlags::ObjectEx);
616        val.inc_count(); // TODO(david): not sure if this is needed :/
617        self.value.obj = ptr::from_ref(val).cast_mut();
618    }
619
620    /// Sets the value of the zval as an array. Returns nothing in a result on
621    /// success.
622    ///
623    /// # Parameters
624    ///
625    /// * `val` - The value to set the zval as.
626    ///
627    /// # Errors
628    ///
629    /// * Returns an error if the conversion to a hashtable fails.
630    pub fn set_array<T: TryInto<ZBox<ZendHashTable>, Error = Error>>(
631        &mut self,
632        val: T,
633    ) -> Result<()> {
634        self.set_hashtable(val.try_into()?);
635        Ok(())
636    }
637
638    /// Sets the value of the zval as an array. Returns nothing in a result on
639    /// success.
640    ///
641    /// # Parameters
642    ///
643    /// * `val` - The value to set the zval as.
644    pub fn set_hashtable(&mut self, val: ZBox<ZendHashTable>) {
645        self.change_type(ZvalTypeFlags::ArrayEx);
646        self.value.arr = val.into_raw();
647    }
648
649    /// Sets the value of the zval as a pointer.
650    ///
651    /// # Parameters
652    ///
653    /// * `ptr` - The pointer to set the zval as.
654    pub fn set_ptr<T>(&mut self, ptr: *mut T) {
655        self.u1.type_info = ZvalTypeFlags::Ptr.bits();
656        self.value.ptr = ptr.cast::<c_void>();
657    }
658
659    /// Used to drop the Zval but keep the value of the zval intact.
660    ///
661    /// This is important when copying the value of the zval, as the actual
662    /// value will not be copied, but the pointer to the value (string for
663    /// example) will be copied.
664    pub(crate) fn release(mut self) {
665        // NOTE(david): don't use `change_type` here as we are wanting to keep the
666        // contents intact.
667        self.u1.type_info = ZvalTypeFlags::Null.bits();
668    }
669
670    /// Changes the type of the zval, freeing the current contents when
671    /// applicable.
672    ///
673    /// # Parameters
674    ///
675    /// * `ty` - The new type of the zval.
676    fn change_type(&mut self, ty: ZvalTypeFlags) {
677        // SAFETY: we have exclusive mutable access to this zval so can free the
678        // contents.
679        unsafe { zval_ptr_dtor(self) };
680        self.u1.type_info = ty.bits();
681    }
682
683    /// Extracts some type from a `Zval`.
684    ///
685    /// This is a wrapper function around `TryFrom`.
686    #[must_use]
687    pub fn extract<'a, T>(&'a self) -> Option<T>
688    where
689        T: FromZval<'a>,
690    {
691        FromZval::from_zval(self)
692    }
693
694    /// Creates a shallow clone of the [`Zval`].
695    ///
696    /// This copies the contents of the [`Zval`], and increments the reference
697    /// counter of the underlying value (if it is reference counted).
698    ///
699    /// For example, if the zval contains a long, it will simply copy the value.
700    /// However, if the zval contains an object, the new zval will point to the
701    /// same object, and the objects reference counter will be incremented.
702    ///
703    /// # Returns
704    ///
705    /// The cloned zval.
706    #[must_use]
707    pub fn shallow_clone(&self) -> Zval {
708        let mut new = Zval::new();
709        new.u1 = self.u1;
710        new.value = self.value;
711
712        // SAFETY: `u1` union is only used for easier bitmasking. It is valid to read
713        // from either of the variants.
714        //
715        // SAFETY: If the value if refcounted (`self.u1.type_info & Z_TYPE_FLAGS_MASK`)
716        // then it is valid to dereference `self.value.counted`.
717        unsafe {
718            let flags = ZvalTypeFlags::from_bits_retain(self.u1.type_info);
719            if flags.contains(ZvalTypeFlags::RefCounted) {
720                (*self.value.counted).gc.refcount += 1;
721            }
722        }
723
724        new
725    }
726}
727
728impl Debug for Zval {
729    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
730        let mut dbg = f.debug_struct("Zval");
731        let ty = self.get_type();
732        dbg.field("type", &ty);
733
734        macro_rules! field {
735            ($value: expr) => {
736                dbg.field("val", &$value)
737            };
738        }
739
740        match ty {
741            DataType::Undef | DataType::Null | DataType::ConstantExpression | DataType::Void => {
742                field!(Option::<()>::None)
743            }
744            DataType::False => field!(false),
745            DataType::True => field!(true),
746            DataType::Long => field!(self.long()),
747            DataType::Double => field!(self.double()),
748            DataType::String | DataType::Mixed | DataType::Callable => field!(self.string()),
749            DataType::Array => field!(self.array()),
750            DataType::Object(_) => field!(self.object()),
751            DataType::Resource => field!(self.resource()),
752            DataType::Reference => field!(self.reference()),
753            DataType::Bool => field!(self.bool()),
754            DataType::Indirect => field!(self.indirect()),
755            DataType::Iterable => field!(self.iterable()),
756            // SAFETY: We are not accessing the pointer.
757            DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
758        };
759
760        dbg.finish()
761    }
762}
763
764impl Drop for Zval {
765    fn drop(&mut self) {
766        self.change_type(ZvalTypeFlags::Null);
767    }
768}
769
770impl Default for Zval {
771    fn default() -> Self {
772        Self::new()
773    }
774}
775
776impl IntoZval for Zval {
777    const TYPE: DataType = DataType::Mixed;
778    const NULLABLE: bool = true;
779
780    fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
781        *zv = self;
782        Ok(())
783    }
784}
785
786impl<'a> FromZval<'a> for &'a Zval {
787    const TYPE: DataType = DataType::Mixed;
788
789    fn from_zval(zval: &'a Zval) -> Option<Self> {
790        Some(zval)
791    }
792}
793
794impl<'a> FromZvalMut<'a> for &'a mut Zval {
795    const TYPE: DataType = DataType::Mixed;
796
797    fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
798        Some(zval)
799    }
800}
801
802#[cfg(test)]
803#[cfg(feature = "embed")]
804mod tests {
805    use super::*;
806    use crate::embed::Embed;
807
808    #[test]
809    fn test_zval_null() {
810        Embed::run(|| {
811            let zval = Zval::null();
812            assert!(zval.is_null());
813        });
814    }
815}