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