• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidcole1340 / ext-php-rs / 15788182852

20 Jun 2025 09:32PM UTC coverage: 22.034%. Remained the same
15788182852

Pull #460

github

web-flow
Merge d4fc01bb5 into 660f308c0
Pull Request #460: chore(clippy): flowing lifetimes warning and clippy::mut_from_ref

6 of 56 new or added lines in 13 files covered. (10.71%)

3 existing lines in 2 files now uncovered.

871 of 3953 relevant lines covered (22.03%)

2.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

28.38
/src/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

5
use std::{convert::TryInto, ffi::c_void, fmt::Debug, ptr};
6

7
use crate::types::iterable::Iterable;
8
use crate::types::ZendIterator;
9
use 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.
30
pub 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

40
impl Zval {
41
    /// Creates a new, empty zval.
42
    #[must_use]
43
    pub const fn new() -> Self {
32✔
44
        Self {
45
            value: zend_value {
32✔
46
                ptr: ptr::null_mut(),
47
            },
48
            #[allow(clippy::used_underscore_items)]
49
            u1: _zval_struct__bindgen_ty_1 {
32✔
50
                type_info: DataType::Null.as_u32(),
51
            },
52
            #[allow(clippy::used_underscore_items)]
53
            u2: _zval_struct__bindgen_ty_2 { next: 0 },
32✔
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 {
7✔
69
        // TODO: probably more ZTS work is needed here
70
        if self.is_reference() {
7✔
71
            #[allow(clippy::unwrap_used)]
72
            return self.reference_mut().unwrap();
×
73
        }
74
        if self.is_indirect() {
7✔
75
            #[allow(clippy::unwrap_used)]
76
            return self.indirect_mut().unwrap();
×
77
        }
78
        self
7✔
79
    }
80

81
    /// Returns the value of the zval if it is a long.
82
    #[must_use]
83
    pub fn long(&self) -> Option<ZendLong> {
15✔
84
        if self.is_long() {
15✔
85
            Some(unsafe { self.value.lval })
15✔
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> {
13✔
120
        if self.is_string() {
13✔
121
            unsafe { self.value.str_.as_ref() }
13✔
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> {
10✔
131
        self.str().map(ToString::to_string)
10✔
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> {
13✔
142
        self.zend_str().and_then(|zs| zs.as_str().ok())
39✔
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> {
7✔
221
        if self.is_object() {
7✔
222
            unsafe { self.value.obj.as_ref() }
7✔
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> {
2✔
231
        if self.is_object() {
2✔
232
            unsafe { self.value.obj.as_mut() }
×
233
        } else {
234
            None
2✔
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
    #[must_use]
265
    pub fn indirect_mut(&self) -> Option<&mut Zval> {
×
266
        if self.is_indirect() {
×
267
            Some(unsafe { &mut *(self.value.zv.cast::<Zval>()) })
×
268
        } else {
269
            None
×
270
        }
271
    }
272

273
    /// Returns the value of the zval if it is a reference.
274
    #[must_use]
275
    pub fn reference(&self) -> Option<&Zval> {
×
276
        if self.is_reference() {
×
277
            Some(&unsafe { self.value.ref_.as_ref() }?.val)
×
278
        } else {
279
            None
×
280
        }
281
    }
282

283
    /// Returns a mutable reference to the underlying zval if it is a reference.
284
    pub fn reference_mut(&mut self) -> Option<&mut Zval> {
×
285
        if self.is_reference() {
×
286
            Some(&mut unsafe { self.value.ref_.as_mut() }?.val)
×
287
        } else {
288
            None
×
289
        }
290
    }
291

292
    /// Returns the value of the zval if it is callable.
293
    #[must_use]
294
    pub fn callable(&self) -> Option<ZendCallable<'_>> {
1✔
295
        // The Zval is checked if it is callable in the `new` function.
296
        ZendCallable::new(self).ok()
1✔
297
    }
298

299
    /// Returns an iterator over the zval if it is traversable.
300
    #[must_use]
301
    pub fn traversable(&self) -> Option<&mut ZendIterator> {
2✔
302
        if self.is_traversable() {
2✔
303
            self.object()?.get_class_entry().get_iterator(self, false)
4✔
304
        } else {
305
            None
×
306
        }
307
    }
308

309
    /// Returns an iterable over the zval if it is an array or traversable. (is
310
    /// iterable)
311
    #[must_use]
NEW
312
    pub fn iterable(&self) -> Option<Iterable<'_>> {
×
313
        if self.is_iterable() {
×
314
            Iterable::from_zval(self)
×
315
        } else {
316
            None
×
317
        }
318
    }
319

320
    /// Returns the value of the zval if it is a pointer.
321
    ///
322
    /// # Safety
323
    ///
324
    /// The caller must ensure that the pointer contained in the zval is in fact
325
    /// a pointer to an instance of `T`, as the zval has no way of defining
326
    /// the type of pointer.
327
    #[must_use]
328
    pub unsafe fn ptr<T>(&self) -> Option<*mut T> {
×
329
        if self.is_ptr() {
×
330
            Some(self.value.ptr.cast::<T>())
×
331
        } else {
332
            None
×
333
        }
334
    }
335

336
    /// Attempts to call the zval as a callable with a list of arguments to pass
337
    /// to the function. Note that a thrown exception inside the callable is
338
    /// not detectable, therefore you should check if the return value is
339
    /// valid rather than unwrapping. Returns a result containing the return
340
    /// value of the function, or an error.
341
    ///
342
    /// You should not call this function directly, rather through the
343
    /// [`call_user_func`] macro.
344
    ///
345
    /// # Parameters
346
    ///
347
    /// * `params` - A list of parameters to call the function with.
348
    ///
349
    /// # Errors
350
    ///
351
    /// * Returns an error if the [`Zval`] is not callable.
352
    // TODO: Measure this
353
    #[allow(clippy::inline_always)]
354
    #[inline(always)]
355
    pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
1✔
356
        self.callable().ok_or(Error::Callable)?.try_call(params)
2✔
357
    }
358

359
    /// Returns the type of the Zval.
360
    #[must_use]
361
    pub fn get_type(&self) -> DataType {
72✔
362
        DataType::from(u32::from(unsafe { self.u1.v.type_ }))
72✔
363
    }
364

365
    /// Returns true if the zval is a long, false otherwise.
366
    #[must_use]
367
    pub fn is_long(&self) -> bool {
20✔
368
        self.get_type() == DataType::Long
20✔
369
    }
370

371
    /// Returns true if the zval is null, false otherwise.
372
    #[must_use]
373
    pub fn is_null(&self) -> bool {
×
374
        self.get_type() == DataType::Null
×
375
    }
376

377
    /// Returns true if the zval is true, false otherwise.
378
    #[must_use]
379
    pub fn is_true(&self) -> bool {
×
380
        self.get_type() == DataType::True
×
381
    }
382

383
    /// Returns true if the zval is false, false otherwise.
384
    #[must_use]
385
    pub fn is_false(&self) -> bool {
×
386
        self.get_type() == DataType::False
×
387
    }
388

389
    /// Returns true if the zval is a bool, false otherwise.
390
    #[must_use]
391
    pub fn is_bool(&self) -> bool {
×
392
        self.is_true() || self.is_false()
×
393
    }
394

395
    /// Returns true if the zval is a double, false otherwise.
396
    #[must_use]
397
    pub fn is_double(&self) -> bool {
×
398
        self.get_type() == DataType::Double
×
399
    }
400

401
    /// Returns true if the zval is a string, false otherwise.
402
    #[must_use]
403
    pub fn is_string(&self) -> bool {
22✔
404
        self.get_type() == DataType::String
22✔
405
    }
406

407
    /// Returns true if the zval is a resource, false otherwise.
408
    #[must_use]
409
    pub fn is_resource(&self) -> bool {
×
410
        self.get_type() == DataType::Resource
×
411
    }
412

413
    /// Returns true if the zval is an array, false otherwise.
414
    #[must_use]
415
    pub fn is_array(&self) -> bool {
×
416
        self.get_type() == DataType::Array
×
417
    }
418

419
    /// Returns true if the zval is an object, false otherwise.
420
    #[must_use]
421
    pub fn is_object(&self) -> bool {
16✔
422
        matches!(self.get_type(), DataType::Object(_))
18✔
423
    }
424

425
    /// Returns true if the zval is a reference, false otherwise.
426
    #[must_use]
427
    pub fn is_reference(&self) -> bool {
7✔
428
        self.get_type() == DataType::Reference
7✔
429
    }
430

431
    /// Returns true if the zval is a reference, false otherwise.
432
    #[must_use]
433
    pub fn is_indirect(&self) -> bool {
7✔
434
        self.get_type() == DataType::Indirect
7✔
435
    }
436

437
    /// Returns true if the zval is callable, false otherwise.
438
    #[must_use]
439
    pub fn is_callable(&self) -> bool {
1✔
440
        let ptr: *const Self = self;
1✔
441
        unsafe { zend_is_callable(ptr.cast_mut(), 0, std::ptr::null_mut()) }
1✔
442
    }
443

444
    /// Checks if the zval is identical to another one.
445
    /// This works like `===` in php.
446
    ///
447
    /// # Parameters
448
    ///
449
    /// * `other` - The the zval to check identity against.
450
    #[must_use]
451
    pub fn is_identical(&self, other: &Self) -> bool {
×
452
        let self_p: *const Self = self;
×
453
        let other_p: *const Self = other;
×
454
        unsafe { zend_is_identical(self_p.cast_mut(), other_p.cast_mut()) }
×
455
    }
456

457
    /// Returns true if the zval is traversable, false otherwise.
458
    #[must_use]
459
    pub fn is_traversable(&self) -> bool {
4✔
460
        match self.object() {
4✔
461
            None => false,
×
462
            Some(obj) => obj.is_traversable(),
4✔
463
        }
464
    }
465

466
    /// Returns true if the zval is iterable (array or traversable), false
467
    /// otherwise.
468
    #[must_use]
469
    pub fn is_iterable(&self) -> bool {
×
470
        let ptr: *const Self = self;
×
471
        unsafe { zend_is_iterable(ptr.cast_mut()) }
×
472
    }
473

474
    /// Returns true if the zval contains a pointer, false otherwise.
475
    #[must_use]
476
    pub fn is_ptr(&self) -> bool {
×
477
        self.get_type() == DataType::Ptr
×
478
    }
479

480
    /// Sets the value of the zval as a string. Returns nothing in a result when
481
    /// successful.
482
    ///
483
    /// # Parameters
484
    ///
485
    /// * `val` - The value to set the zval as.
486
    /// * `persistent` - Whether the string should persist between requests.
487
    ///
488
    /// # Errors
489
    ///
490
    /// Never returns an error.
491
    // TODO: Check if we can drop the result here.
492
    pub fn set_string(&mut self, val: &str, persistent: bool) -> Result<()> {
2✔
493
        self.set_zend_string(ZendStr::new(val, persistent));
2✔
494
        Ok(())
2✔
495
    }
496

497
    /// Sets the value of the zval as a Zend string.
498
    ///
499
    /// # Parameters
500
    ///
501
    /// * `val` - String content.
502
    pub fn set_zend_string(&mut self, val: ZBox<ZendStr>) {
2✔
503
        self.change_type(ZvalTypeFlags::StringEx);
2✔
504
        self.value.str_ = val.into_raw();
2✔
505
    }
506

507
    /// Sets the value of the zval as a binary string, which is represented in
508
    /// Rust as a vector.
509
    ///
510
    /// # Parameters
511
    ///
512
    /// * `val` - The value to set the zval as.
513
    pub fn set_binary<T: Pack>(&mut self, val: Vec<T>) {
×
514
        self.change_type(ZvalTypeFlags::StringEx);
×
515
        let ptr = T::pack_into(val);
×
516
        self.value.str_ = ptr;
×
517
    }
518

519
    /// Sets the value of the zval as a interned string. Returns nothing in a
520
    /// result when successful.
521
    ///
522
    /// # Parameters
523
    ///
524
    /// * `val` - The value to set the zval as.
525
    /// * `persistent` - Whether the string should persist between requests.
526
    ///
527
    /// # Errors
528
    ///
529
    /// Never returns an error.
530
    // TODO: Check if we can drop the result here.
531
    pub fn set_interned_string(&mut self, val: &str, persistent: bool) -> Result<()> {
×
532
        self.set_zend_string(ZendStr::new_interned(val, persistent));
×
533
        Ok(())
×
534
    }
535

536
    /// Sets the value of the zval as a long.
537
    ///
538
    /// # Parameters
539
    ///
540
    /// * `val` - The value to set the zval as.
541
    pub fn set_long<T: Into<ZendLong>>(&mut self, val: T) {
6✔
542
        self.internal_set_long(val.into());
6✔
543
    }
544

545
    fn internal_set_long(&mut self, val: ZendLong) {
6✔
546
        self.change_type(ZvalTypeFlags::Long);
6✔
547
        self.value.lval = val;
6✔
548
    }
549

550
    /// Sets the value of the zval as a double.
551
    ///
552
    /// # Parameters
553
    ///
554
    /// * `val` - The value to set the zval as.
555
    pub fn set_double<T: Into<f64>>(&mut self, val: T) {
×
556
        self.internal_set_double(val.into());
×
557
    }
558

559
    fn internal_set_double(&mut self, val: f64) {
×
560
        self.change_type(ZvalTypeFlags::Double);
×
561
        self.value.dval = val;
×
562
    }
563

564
    /// Sets the value of the zval as a boolean.
565
    ///
566
    /// # Parameters
567
    ///
568
    /// * `val` - The value to set the zval as.
569
    pub fn set_bool<T: Into<bool>>(&mut self, val: T) {
×
570
        self.internal_set_bool(val.into());
×
571
    }
572

573
    fn internal_set_bool(&mut self, val: bool) {
×
574
        self.change_type(if val {
×
575
            ZvalTypeFlags::True
×
576
        } else {
577
            ZvalTypeFlags::False
×
578
        });
579
    }
580

581
    /// Sets the value of the zval as null.
582
    ///
583
    /// This is the default of a zval.
584
    pub fn set_null(&mut self) {
×
585
        self.change_type(ZvalTypeFlags::Null);
×
586
    }
587

588
    /// Sets the value of the zval as a resource.
589
    ///
590
    /// # Parameters
591
    ///
592
    /// * `val` - The value to set the zval as.
593
    pub fn set_resource(&mut self, val: *mut zend_resource) {
×
594
        self.change_type(ZvalTypeFlags::ResourceEx);
×
595
        self.value.res = val;
×
596
    }
597

598
    /// Sets the value of the zval as a reference to an object.
599
    ///
600
    /// # Parameters
601
    ///
602
    /// * `val` - The value to set the zval as.
603
    pub fn set_object(&mut self, val: &mut ZendObject) {
×
604
        self.change_type(ZvalTypeFlags::ObjectEx);
×
605
        val.inc_count(); // TODO(david): not sure if this is needed :/
×
606
        self.value.obj = ptr::from_ref(val).cast_mut();
×
607
    }
608

609
    /// Sets the value of the zval as an array. Returns nothing in a result on
610
    /// success.
611
    ///
612
    /// # Parameters
613
    ///
614
    /// * `val` - The value to set the zval as.
615
    ///
616
    /// # Errors
617
    ///
618
    /// * Returns an error if the conversion to a hashtable fails.
619
    pub fn set_array<T: TryInto<ZBox<ZendHashTable>, Error = Error>>(
×
620
        &mut self,
621
        val: T,
622
    ) -> Result<()> {
623
        self.set_hashtable(val.try_into()?);
×
624
        Ok(())
×
625
    }
626

627
    /// Sets the value of the zval as an array. Returns nothing in a result on
628
    /// success.
629
    ///
630
    /// # Parameters
631
    ///
632
    /// * `val` - The value to set the zval as.
633
    pub fn set_hashtable(&mut self, val: ZBox<ZendHashTable>) {
×
634
        self.change_type(ZvalTypeFlags::ArrayEx);
×
635
        self.value.arr = val.into_raw();
×
636
    }
637

638
    /// Sets the value of the zval as a pointer.
639
    ///
640
    /// # Parameters
641
    ///
642
    /// * `ptr` - The pointer to set the zval as.
643
    pub fn set_ptr<T>(&mut self, ptr: *mut T) {
×
644
        self.u1.type_info = ZvalTypeFlags::Ptr.bits();
×
645
        self.value.ptr = ptr.cast::<c_void>();
×
646
    }
647

648
    /// Used to drop the Zval but keep the value of the zval intact.
649
    ///
650
    /// This is important when copying the value of the zval, as the actual
651
    /// value will not be copied, but the pointer to the value (string for
652
    /// example) will be copied.
653
    pub(crate) fn release(mut self) {
×
654
        // NOTE(david): don't use `change_type` here as we are wanting to keep the
655
        // contents intact.
656
        self.u1.type_info = ZvalTypeFlags::Null.bits();
×
657
    }
658

659
    /// Changes the type of the zval, freeing the current contents when
660
    /// applicable.
661
    ///
662
    /// # Parameters
663
    ///
664
    /// * `ty` - The new type of the zval.
665
    fn change_type(&mut self, ty: ZvalTypeFlags) {
38✔
666
        // SAFETY: we have exclusive mutable access to this zval so can free the
667
        // contents.
668
        unsafe { zval_ptr_dtor(self) };
38✔
669
        self.u1.type_info = ty.bits();
38✔
670
    }
671

672
    /// Extracts some type from a `Zval`.
673
    ///
674
    /// This is a wrapper function around `TryFrom`.
675
    #[must_use]
676
    pub fn extract<'a, T>(&'a self) -> Option<T>
×
677
    where
678
        T: FromZval<'a>,
679
    {
680
        FromZval::from_zval(self)
×
681
    }
682

683
    /// Creates a shallow clone of the [`Zval`].
684
    ///
685
    /// This copies the contents of the [`Zval`], and increments the reference
686
    /// counter of the underlying value (if it is reference counted).
687
    ///
688
    /// For example, if the zval contains a long, it will simply copy the value.
689
    /// However, if the zval contains an object, the new zval will point to the
690
    /// same object, and the objects reference counter will be incremented.
691
    ///
692
    /// # Returns
693
    ///
694
    /// The cloned zval.
695
    #[must_use]
696
    pub fn shallow_clone(&self) -> Zval {
×
697
        let mut new = Zval::new();
×
698
        new.u1 = self.u1;
×
699
        new.value = self.value;
×
700

701
        // SAFETY: `u1` union is only used for easier bitmasking. It is valid to read
702
        // from either of the variants.
703
        //
704
        // SAFETY: If the value if refcounted (`self.u1.type_info & Z_TYPE_FLAGS_MASK`)
705
        // then it is valid to dereference `self.value.counted`.
706
        unsafe {
707
            let flags = ZvalTypeFlags::from_bits_retain(self.u1.type_info);
×
708
            if flags.contains(ZvalTypeFlags::RefCounted) {
×
709
                (*self.value.counted).gc.refcount += 1;
×
710
            }
711
        }
712

713
        new
×
714
    }
715
}
716

717
impl Debug for Zval {
718
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
719
        let mut dbg = f.debug_struct("Zval");
×
720
        let ty = self.get_type();
×
721
        dbg.field("type", &ty);
×
722

723
        macro_rules! field {
724
            ($value: expr) => {
725
                dbg.field("val", &$value)
726
            };
727
        }
728

729
        match ty {
×
730
            DataType::Undef | DataType::Null | DataType::ConstantExpression | DataType::Void => {
731
                field!(Option::<()>::None)
×
732
            }
733
            DataType::False => field!(false),
×
734
            DataType::True => field!(true),
×
735
            DataType::Long => field!(self.long()),
×
736
            DataType::Double => field!(self.double()),
×
737
            DataType::String | DataType::Mixed | DataType::Callable => field!(self.string()),
×
738
            DataType::Array => field!(self.array()),
×
739
            DataType::Object(_) => field!(self.object()),
×
740
            DataType::Resource => field!(self.resource()),
×
741
            DataType::Reference => field!(self.reference()),
×
742
            DataType::Bool => field!(self.bool()),
×
743
            DataType::Indirect => field!(self.indirect()),
×
744
            DataType::Iterable => field!(self.iterable()),
×
745
            // SAFETY: We are not accessing the pointer.
746
            DataType::Ptr => field!(unsafe { self.ptr::<c_void>() }),
×
747
        };
748

749
        dbg.finish()
×
750
    }
751
}
752

753
impl Drop for Zval {
754
    fn drop(&mut self) {
30✔
755
        self.change_type(ZvalTypeFlags::Null);
30✔
756
    }
757
}
758

759
impl Default for Zval {
760
    fn default() -> Self {
×
761
        Self::new()
×
762
    }
763
}
764

765
impl IntoZval for Zval {
766
    const TYPE: DataType = DataType::Mixed;
767
    const NULLABLE: bool = true;
768

769
    fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
×
770
        *zv = self;
×
771
        Ok(())
×
772
    }
773
}
774

775
impl<'a> FromZval<'a> for &'a Zval {
776
    const TYPE: DataType = DataType::Mixed;
777

778
    fn from_zval(zval: &'a Zval) -> Option<Self> {
×
779
        Some(zval)
×
780
    }
781
}
782

783
impl<'a> FromZvalMut<'a> for &'a mut Zval {
784
    const TYPE: DataType = DataType::Mixed;
785

786
    fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
×
787
        Some(zval)
×
788
    }
789
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc