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::ZendIterator;
8use crate::types::iterable::Iterable;
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(unsafe { 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}