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

extphprs / ext-php-rs / 20436469378

22 Dec 2025 03:33PM UTC coverage: 34.867% (-0.4%) from 35.249%
20436469378

Pull #633

github

web-flow
Merge a5bc45832 into 78d2857dc
Pull Request #633: fix: property visibility checks #375

1 of 56 new or added lines in 2 files covered. (1.79%)

1 existing line in 1 file now uncovered.

1705 of 4890 relevant lines covered (34.87%)

12.14 hits per line

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

23.96
/src/types/array/mod.rs
1
//! Represents an array in PHP. As all arrays in PHP are associative arrays,
2
//! they are represented by hash tables.
3

4
use std::{convert::TryFrom, ffi::CString, fmt::Debug, ptr};
5

6
use crate::{
7
    boxed::{ZBox, ZBoxable},
8
    convert::{FromZval, FromZvalMut, IntoZval},
9
    error::Result,
10
    ffi::zend_ulong,
11
    ffi::{
12
        _zend_new_array, HT_MIN_SIZE, zend_array_count, zend_array_destroy, zend_array_dup,
13
        zend_hash_clean, zend_hash_index_del, zend_hash_index_find, zend_hash_index_update,
14
        zend_hash_next_index_insert, zend_hash_str_del, zend_hash_str_find, zend_hash_str_update,
15
    },
16
    flags::DataType,
17
    types::Zval,
18
};
19

20
mod array_key;
21
mod conversions;
22
mod iterators;
23

24
pub use array_key::ArrayKey;
25
pub use iterators::{Iter, Values};
26

27
/// A PHP hashtable.
28
///
29
/// In PHP, arrays are represented as hashtables. This allows you to push values
30
/// onto the end of the array like a vector, while also allowing you to insert
31
/// at arbitrary string key indexes.
32
///
33
/// A PHP hashtable stores values as [`Zval`]s. This allows you to insert
34
/// different types into the same hashtable. Types must implement [`IntoZval`]
35
/// to be able to be inserted into the hashtable.
36
///
37
/// # Examples
38
///
39
/// ```no_run
40
/// use ext_php_rs::types::ZendHashTable;
41
///
42
/// let mut ht = ZendHashTable::new();
43
/// ht.push(1);
44
/// ht.push("Hello, world!");
45
/// ht.insert("Like", "Hashtable");
46
///
47
/// assert_eq!(ht.len(), 3);
48
/// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(1));
49
/// ```
50
pub type ZendHashTable = crate::ffi::HashTable;
51

52
// Clippy complains about there being no `is_empty` function when implementing
53
// on the alias `ZendStr` :( <https://github.com/rust-lang/rust-clippy/issues/7702>
54
#[allow(clippy::len_without_is_empty)]
55
impl ZendHashTable {
56
    /// Creates a new, empty, PHP hashtable, returned inside a [`ZBox`].
57
    ///
58
    /// # Example
59
    ///
60
    /// ```no_run
61
    /// use ext_php_rs::types::ZendHashTable;
62
    ///
63
    /// let ht = ZendHashTable::new();
64
    /// ```
65
    ///
66
    /// # Panics
67
    ///
68
    /// Panics if memory for the hashtable could not be allocated.
69
    #[must_use]
70
    pub fn new() -> ZBox<Self> {
30✔
71
        Self::with_capacity(HT_MIN_SIZE)
30✔
72
    }
73

74
    /// Creates a new, empty, PHP hashtable with an initial size, returned
75
    /// inside a [`ZBox`].
76
    ///
77
    /// # Parameters
78
    ///
79
    /// * `size` - The size to initialize the array with.
80
    ///
81
    /// # Example
82
    ///
83
    /// ```no_run
84
    /// use ext_php_rs::types::ZendHashTable;
85
    ///
86
    /// let ht = ZendHashTable::with_capacity(10);
87
    /// ```
88
    ///
89
    /// # Panics
90
    ///
91
    /// Panics if memory for the hashtable could not be allocated.
92
    #[must_use]
93
    pub fn with_capacity(size: u32) -> ZBox<Self> {
44✔
94
        unsafe {
95
            // SAFETY: PHP allocator handles the creation of the array.
96
            #[allow(clippy::used_underscore_items)]
97
            let ptr = _zend_new_array(size);
132✔
98

99
            // SAFETY: `as_mut()` checks if the pointer is null, and panics if it is not.
100
            ZBox::from_raw(
101
                ptr.as_mut()
132✔
102
                    .expect("Failed to allocate memory for hashtable"),
44✔
103
            )
104
        }
105
    }
106

107
    /// Returns the current number of elements in the array.
108
    ///
109
    /// # Example
110
    ///
111
    /// ```no_run
112
    /// use ext_php_rs::types::ZendHashTable;
113
    ///
114
    /// let mut ht = ZendHashTable::new();
115
    ///
116
    /// ht.push(1);
117
    /// ht.push("Hello, world");
118
    ///
119
    /// assert_eq!(ht.len(), 2);
120
    /// ```
121
    #[must_use]
122
    pub fn len(&self) -> usize {
60✔
123
        unsafe { zend_array_count(ptr::from_ref(self).cast_mut()) as usize }
180✔
124
    }
125

126
    /// Returns whether the hash table is empty.
127
    ///
128
    /// # Example
129
    ///
130
    /// ```no_run
131
    /// use ext_php_rs::types::ZendHashTable;
132
    ///
133
    /// let mut ht = ZendHashTable::new();
134
    ///
135
    /// assert_eq!(ht.is_empty(), true);
136
    ///
137
    /// ht.push(1);
138
    /// ht.push("Hello, world");
139
    ///
140
    /// assert_eq!(ht.is_empty(), false);
141
    /// ```
142
    #[must_use]
143
    pub fn is_empty(&self) -> bool {
×
144
        self.len() == 0
×
145
    }
146

147
    /// Clears the hash table, removing all values.
148
    ///
149
    /// # Example
150
    ///
151
    /// ```no_run
152
    /// use ext_php_rs::types::ZendHashTable;
153
    ///
154
    /// let mut ht = ZendHashTable::new();
155
    ///
156
    /// ht.insert("test", "hello world");
157
    /// assert_eq!(ht.is_empty(), false);
158
    ///
159
    /// ht.clear();
160
    /// assert_eq!(ht.is_empty(), true);
161
    /// ```
162
    pub fn clear(&mut self) {
×
163
        unsafe { zend_hash_clean(self) }
×
164
    }
165

166
    /// Attempts to retrieve a value from the hash table with a string key.
167
    ///
168
    /// # Parameters
169
    ///
170
    /// * `key` - The key to search for in the hash table.
171
    ///
172
    /// # Returns
173
    ///
174
    /// * `Some(&Zval)` - A reference to the zval at the position in the hash
175
    ///   table.
176
    /// * `None` - No value at the given position was found.
177
    ///
178
    /// # Example
179
    ///
180
    /// ```no_run
181
    /// use ext_php_rs::types::ZendHashTable;
182
    ///
183
    /// let mut ht = ZendHashTable::new();
184
    ///
185
    /// ht.insert("test", "hello world");
186
    /// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world"));
187
    /// ```
188
    #[must_use]
189
    pub fn get<'a, K>(&self, key: K) -> Option<&Zval>
38✔
190
    where
191
        K: Into<ArrayKey<'a>>,
192
    {
193
        match key.into() {
38✔
194
            ArrayKey::Long(index) => unsafe {
195
                #[allow(clippy::cast_sign_loss)]
196
                zend_hash_index_find(self, index as zend_ulong).as_ref()
80✔
197
            },
198
            ArrayKey::String(key) => unsafe {
199
                zend_hash_str_find(
200
                    self,
×
201
                    CString::new(key.as_str()).ok()?.as_ptr(),
×
202
                    key.len() as _,
×
203
                )
204
                .as_ref()
205
            },
206
            ArrayKey::Str(key) => unsafe {
207
                zend_hash_str_find(self, CString::new(key).ok()?.as_ptr(), key.len() as _).as_ref()
162✔
208
            },
209
        }
210
    }
211

212
    /// Attempts to retrieve a value from the hash table with a string key.
213
    ///
214
    /// # Parameters
215
    ///
216
    /// * `key` - The key to search for in the hash table.
217
    ///
218
    /// # Returns
219
    ///
220
    /// * `Some(&Zval)` - A reference to the zval at the position in the hash
221
    ///   table.
222
    /// * `None` - No value at the given position was found.
223
    ///
224
    /// # Example
225
    ///
226
    /// ```no_run
227
    /// use ext_php_rs::types::ZendHashTable;
228
    ///
229
    /// let mut ht = ZendHashTable::new();
230
    ///
231
    /// ht.insert("test", "hello world");
232
    /// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world"));
233
    /// ```
234
    // TODO: Verify if this is safe to use, as it allows mutating the
235
    // hashtable while only having a reference to it. #461
236
    #[allow(clippy::mut_from_ref)]
237
    #[must_use]
238
    pub fn get_mut<'a, K>(&self, key: K) -> Option<&mut Zval>
×
239
    where
240
        K: Into<ArrayKey<'a>>,
241
    {
242
        match key.into() {
×
243
            ArrayKey::Long(index) => unsafe {
244
                #[allow(clippy::cast_sign_loss)]
245
                zend_hash_index_find(self, index as zend_ulong).as_mut()
×
246
            },
247
            ArrayKey::String(key) => unsafe {
248
                zend_hash_str_find(
249
                    self,
×
250
                    CString::new(key.as_str()).ok()?.as_ptr(),
×
251
                    key.len() as _,
×
252
                )
253
                .as_mut()
254
            },
255
            ArrayKey::Str(key) => unsafe {
256
                zend_hash_str_find(self, CString::new(key).ok()?.as_ptr(), key.len() as _).as_mut()
×
257
            },
258
        }
259
    }
260

261
    /// Attempts to retrieve a value from the hash table with an index.
262
    ///
263
    /// # Parameters
264
    ///
265
    /// * `key` - The key to search for in the hash table.
266
    ///
267
    /// # Returns
268
    ///
269
    /// * `Some(&Zval)` - A reference to the zval at the position in the hash
270
    ///   table.
271
    /// * `None` - No value at the given position was found.
272
    ///
273
    /// # Example
274
    ///
275
    /// ```no_run
276
    /// use ext_php_rs::types::ZendHashTable;
277
    ///
278
    /// let mut ht = ZendHashTable::new();
279
    ///
280
    /// ht.push(100);
281
    /// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100));
282
    /// ```
283
    #[must_use]
284
    pub fn get_index(&self, key: i64) -> Option<&Zval> {
×
285
        #[allow(clippy::cast_sign_loss)]
286
        unsafe {
287
            zend_hash_index_find(self, key as zend_ulong).as_ref()
×
288
        }
289
    }
290

291
    /// Attempts to retrieve a value from the hash table with an index.
292
    ///
293
    /// # Parameters
294
    ///
295
    /// * `key` - The key to search for in the hash table.
296
    ///
297
    /// # Returns
298
    ///
299
    /// * `Some(&Zval)` - A reference to the zval at the position in the hash
300
    ///   table.
301
    /// * `None` - No value at the given position was found.
302
    ///
303
    /// # Example
304
    ///
305
    /// ```no_run
306
    /// use ext_php_rs::types::ZendHashTable;
307
    ///
308
    /// let mut ht = ZendHashTable::new();
309
    ///
310
    /// ht.push(100);
311
    /// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100));
312
    /// ```
313
    // TODO: Verify if this is safe to use, as it allows mutating the
314
    // hashtable while only having a reference to it. #461
315
    #[allow(clippy::mut_from_ref)]
316
    #[must_use]
317
    pub fn get_index_mut(&self, key: i64) -> Option<&mut Zval> {
×
318
        unsafe {
319
            #[allow(clippy::cast_sign_loss)]
320
            zend_hash_index_find(self, key as zend_ulong).as_mut()
×
321
        }
322
    }
323

324
    /// Attempts to remove a value from the hash table with a string key.
325
    ///
326
    /// # Parameters
327
    ///
328
    /// * `key` - The key to remove from the hash table.
329
    ///
330
    /// # Returns
331
    ///
332
    /// * `Some(())` - Key was successfully removed.
333
    /// * `None` - No key was removed, did not exist.
334
    ///
335
    /// # Example
336
    ///
337
    /// ```no_run
338
    /// use ext_php_rs::types::ZendHashTable;
339
    ///
340
    /// let mut ht = ZendHashTable::new();
341
    ///
342
    /// ht.insert("test", "hello world");
343
    /// assert_eq!(ht.len(), 1);
344
    ///
345
    /// ht.remove("test");
346
    /// assert_eq!(ht.len(), 0);
347
    /// ```
348
    pub fn remove<'a, K>(&mut self, key: K) -> Option<()>
×
349
    where
350
        K: Into<ArrayKey<'a>>,
351
    {
352
        let result = match key.into() {
×
353
            ArrayKey::Long(index) => unsafe {
354
                #[allow(clippy::cast_sign_loss)]
355
                zend_hash_index_del(self, index as zend_ulong)
×
356
            },
357
            ArrayKey::String(key) => unsafe {
358
                zend_hash_str_del(
359
                    self,
×
360
                    CString::new(key.as_str()).ok()?.as_ptr(),
×
361
                    key.len() as _,
×
362
                )
363
            },
364
            ArrayKey::Str(key) => unsafe {
365
                zend_hash_str_del(self, CString::new(key).ok()?.as_ptr(), key.len() as _)
×
366
            },
367
        };
368

369
        if result < 0 { None } else { Some(()) }
×
370
    }
371

372
    /// Attempts to remove a value from the hash table with a string key.
373
    ///
374
    /// # Parameters
375
    ///
376
    /// * `key` - The key to remove from the hash table.
377
    ///
378
    /// # Returns
379
    ///
380
    /// * `Ok(())` - Key was successfully removed.
381
    /// * `None` - No key was removed, did not exist.
382
    ///
383
    /// # Example
384
    ///
385
    /// ```no_run
386
    /// use ext_php_rs::types::ZendHashTable;
387
    ///
388
    /// let mut ht = ZendHashTable::new();
389
    ///
390
    /// ht.push("hello");
391
    /// assert_eq!(ht.len(), 1);
392
    ///
393
    /// ht.remove_index(0);
394
    /// assert_eq!(ht.len(), 0);
395
    /// ```
396
    pub fn remove_index(&mut self, key: i64) -> Option<()> {
×
397
        let result = unsafe {
398
            #[allow(clippy::cast_sign_loss)]
399
            zend_hash_index_del(self, key as zend_ulong)
×
400
        };
401

402
        if result < 0 { None } else { Some(()) }
×
403
    }
404

405
    /// Attempts to insert an item into the hash table, or update if the key
406
    /// already exists. Returns nothing in a result if successful.
407
    ///
408
    /// # Parameters
409
    ///
410
    /// * `key` - The key to insert the value at in the hash table.
411
    /// * `value` - The value to insert into the hash table.
412
    ///
413
    /// # Returns
414
    ///
415
    /// Returns nothing in a result on success.
416
    ///
417
    /// # Errors
418
    ///
419
    /// Returns an error if the key could not be converted into a [`CString`],
420
    /// or converting the value into a [`Zval`] failed.
421
    ///
422
    /// # Example
423
    ///
424
    /// ```no_run
425
    /// use ext_php_rs::types::ZendHashTable;
426
    ///
427
    /// let mut ht = ZendHashTable::new();
428
    ///
429
    /// ht.insert("a", "A");
430
    /// ht.insert("b", "B");
431
    /// ht.insert("c", "C");
432
    /// assert_eq!(ht.len(), 3);
433
    /// ```
434
    pub fn insert<'a, K, V>(&mut self, key: K, val: V) -> Result<()>
113✔
435
    where
436
        K: Into<ArrayKey<'a>>,
437
        V: IntoZval,
438
    {
439
        let mut val = val.into_zval(false)?;
339✔
440
        match key.into() {
113✔
441
            ArrayKey::Long(index) => {
130✔
442
                unsafe {
443
                    #[allow(clippy::cast_sign_loss)]
444
                    zend_hash_index_update(self, index as zend_ulong, &raw mut val)
195✔
445
                };
446
            }
447
            ArrayKey::String(key) => {
×
448
                unsafe {
449
                    // Use raw bytes directly since zend_hash_str_update takes a length.
450
                    // This allows keys with embedded null bytes (e.g. PHP property mangling).
451
                    zend_hash_str_update(
452
                        self,
×
NEW
453
                        key.as_str().as_ptr().cast(),
×
454
                        key.len(),
×
455
                        &raw mut val,
×
456
                    )
457
                };
458
            }
459
            ArrayKey::Str(key) => {
96✔
460
                unsafe {
461
                    // Use raw bytes directly since zend_hash_str_update takes a length.
462
                    // This allows keys with embedded null bytes (e.g. PHP property mangling).
463
                    zend_hash_str_update(self, key.as_ptr().cast(), key.len(), &raw mut val)
336✔
464
                };
465
            }
466
        }
467
        val.release();
226✔
468
        Ok(())
113✔
469
    }
470

471
    /// Inserts an item into the hash table at a specified index, or updates if
472
    /// the key already exists. Returns nothing in a result if successful.
473
    ///
474
    /// # Parameters
475
    ///
476
    /// * `key` - The index at which the value should be inserted.
477
    /// * `val` - The value to insert into the hash table.
478
    ///
479
    /// # Returns
480
    ///
481
    /// Returns nothing in a result on success.
482
    ///
483
    /// # Errors
484
    ///
485
    /// Returns an error if converting the value into a [`Zval`] failed.
486
    ///
487
    /// # Example
488
    ///
489
    /// ```no_run
490
    /// use ext_php_rs::types::ZendHashTable;
491
    ///
492
    /// let mut ht = ZendHashTable::new();
493
    ///
494
    /// ht.insert_at_index(0, "A");
495
    /// ht.insert_at_index(5, "B");
496
    /// ht.insert_at_index(0, "C"); // notice overriding index 0
497
    /// assert_eq!(ht.len(), 2);
498
    /// ```
499
    pub fn insert_at_index<V>(&mut self, key: i64, val: V) -> Result<()>
×
500
    where
501
        V: IntoZval,
502
    {
503
        let mut val = val.into_zval(false)?;
×
504
        unsafe {
505
            #[allow(clippy::cast_sign_loss)]
506
            zend_hash_index_update(self, key as zend_ulong, &raw mut val)
×
507
        };
508
        val.release();
×
509
        Ok(())
×
510
    }
511

512
    /// Pushes an item onto the end of the hash table. Returns a result
513
    /// containing nothing if the element was successfully inserted.
514
    ///
515
    /// # Parameters
516
    ///
517
    /// * `val` - The value to insert into the hash table.
518
    ///
519
    /// # Returns
520
    ///
521
    /// Returns nothing in a result on success.
522
    ///
523
    /// # Errors
524
    ///
525
    /// Returns an error if converting the value into a [`Zval`] failed.
526
    ///
527
    /// # Example
528
    ///
529
    /// ```no_run
530
    /// use ext_php_rs::types::ZendHashTable;
531
    ///
532
    /// let mut ht = ZendHashTable::new();
533
    ///
534
    /// ht.push("a");
535
    /// ht.push("b");
536
    /// ht.push("c");
537
    /// assert_eq!(ht.len(), 3);
538
    /// ```
539
    pub fn push<V>(&mut self, val: V) -> Result<()>
×
540
    where
541
        V: IntoZval,
542
    {
543
        let mut val = val.into_zval(false)?;
×
544
        unsafe { zend_hash_next_index_insert(self, &raw mut val) };
×
545
        val.release();
×
546

547
        Ok(())
×
548
    }
549

550
    /// Checks if the hashtable only contains numerical keys.
551
    ///
552
    /// # Returns
553
    ///
554
    /// True if all keys on the hashtable are numerical.
555
    ///
556
    /// # Example
557
    ///
558
    /// ```no_run
559
    /// use ext_php_rs::types::ZendHashTable;
560
    ///
561
    /// let mut ht = ZendHashTable::new();
562
    ///
563
    /// ht.push(0);
564
    /// ht.push(3);
565
    /// ht.push(9);
566
    /// assert!(ht.has_numerical_keys());
567
    ///
568
    /// ht.insert("obviously not numerical", 10);
569
    /// assert!(!ht.has_numerical_keys());
570
    /// ```
571
    #[must_use]
572
    pub fn has_numerical_keys(&self) -> bool {
×
573
        !self.into_iter().any(|(k, _)| !k.is_long())
×
574
    }
575

576
    /// Checks if the hashtable has numerical, sequential keys.
577
    ///
578
    /// # Returns
579
    ///
580
    /// True if all keys on the hashtable are numerical and are in sequential
581
    /// order (i.e. starting at 0 and not skipping any keys).
582
    ///
583
    /// # Panics
584
    ///
585
    /// Panics if the number of elements in the hashtable exceeds `i64::MAX`.
586
    ///
587
    /// # Example
588
    ///
589
    /// ```no_run
590
    /// use ext_php_rs::types::ZendHashTable;
591
    ///
592
    /// let mut ht = ZendHashTable::new();
593
    ///
594
    /// ht.push(0);
595
    /// ht.push(3);
596
    /// ht.push(9);
597
    /// assert!(ht.has_sequential_keys());
598
    ///
599
    /// ht.insert_at_index(90, 10);
600
    /// assert!(!ht.has_sequential_keys());
601
    /// ```
602
    #[must_use]
603
    pub fn has_sequential_keys(&self) -> bool {
×
604
        !self
×
605
            .into_iter()
×
606
            .enumerate()
×
607
            .any(|(i, (k, _))| ArrayKey::Long(i64::try_from(i).expect("Integer overflow")) != k)
×
608
    }
609

610
    /// Returns an iterator over the values contained inside the hashtable, as
611
    /// if it was a set or list.
612
    ///
613
    /// # Example
614
    ///
615
    /// ```no_run
616
    /// use ext_php_rs::types::ZendHashTable;
617
    ///
618
    /// let mut ht = ZendHashTable::new();
619
    ///
620
    /// for val in ht.values() {
621
    ///     dbg!(val);
622
    /// }
623
    #[inline]
624
    #[must_use]
625
    pub fn values(&self) -> Values<'_> {
×
626
        Values::new(self)
×
627
    }
628

629
    /// Returns an iterator over the key(s) and value contained inside the
630
    /// hashtable.
631
    ///
632
    /// # Example
633
    ///
634
    /// ```no_run
635
    /// use ext_php_rs::types::{ZendHashTable, ArrayKey};
636
    ///
637
    /// let mut ht = ZendHashTable::new();
638
    ///
639
    /// for (key, val) in ht.iter() {
640
    ///     match &key {
641
    ///         ArrayKey::Long(index) => {
642
    ///         }
643
    ///         ArrayKey::String(key) => {
644
    ///         }
645
    ///         ArrayKey::Str(key) => {
646
    ///         }
647
    ///     }
648
    ///     dbg!(key, val);
649
    /// }
650
    #[inline]
651
    #[must_use]
652
    pub fn iter(&self) -> Iter<'_> {
×
653
        self.into_iter()
×
654
    }
655
}
656

657
unsafe impl ZBoxable for ZendHashTable {
658
    fn free(&mut self) {
23✔
659
        // SAFETY: ZBox has immutable access to `self`.
660
        unsafe { zend_array_destroy(self) }
46✔
661
    }
662
}
663

664
impl Debug for ZendHashTable {
665
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
666
        f.debug_map()
×
667
            .entries(self.into_iter().map(|(k, v)| (k.to_string(), v)))
×
668
            .finish()
669
    }
670
}
671

672
impl ToOwned for ZendHashTable {
673
    type Owned = ZBox<ZendHashTable>;
674

675
    fn to_owned(&self) -> Self::Owned {
×
676
        unsafe {
677
            // SAFETY: FFI call does not modify `self`, returns a new hashtable.
678
            let ptr = zend_array_dup(ptr::from_ref(self).cast_mut());
×
679

680
            // SAFETY: `as_mut()` checks if the pointer is null, and panics if it is not.
681
            ZBox::from_raw(
682
                ptr.as_mut()
×
683
                    .expect("Failed to allocate memory for hashtable"),
×
684
            )
685
        }
686
    }
687
}
688

689
impl Default for ZBox<ZendHashTable> {
690
    fn default() -> Self {
×
691
        ZendHashTable::new()
×
692
    }
693
}
694

695
impl Clone for ZBox<ZendHashTable> {
696
    fn clone(&self) -> Self {
×
697
        (**self).to_owned()
×
698
    }
699
}
700

701
impl IntoZval for ZBox<ZendHashTable> {
702
    const TYPE: DataType = DataType::Array;
703
    const NULLABLE: bool = false;
704

705
    fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
×
706
        zv.set_hashtable(self);
×
707
        Ok(())
×
708
    }
709
}
710

711
impl<'a> FromZval<'a> for &'a ZendHashTable {
712
    const TYPE: DataType = DataType::Array;
713

714
    fn from_zval(zval: &'a Zval) -> Option<Self> {
×
715
        zval.array()
×
716
    }
717
}
718

719
impl<'a> FromZvalMut<'a> for &'a mut ZendHashTable {
720
    const TYPE: DataType = DataType::Array;
721

722
    fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
×
723
        zval.array_mut()
×
724
    }
725
}
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

© 2026 Coveralls, Inc