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}