Skip to main content

ext_php_rs/types/
separated.rs

1//! Separated zval wrapper for COW-safe mutation without PHP pass-by-reference.
2
3use std::ops::{Deref, DerefMut};
4
5use crate::convert::FromZvalMut;
6use crate::flags::DataType;
7use crate::types::Zval;
8
9/// A zval whose underlying value can be COW-separated for safe mutation.
10///
11/// Use this type in [`#[php_function]`](crate::php_function) signatures when
12/// you need mutable access to a PHP value **without** requiring the caller to
13/// pass by reference (`&$x`).
14///
15/// Unlike `&mut Zval`, which sets PHP's `ZEND_SEND_BY_REF` flag and forces the
16/// caller to write `foo(&$var)`, `Separated` receives the value normally and
17/// exposes `&mut Zval` for local mutation. Call [`Zval::array_mut`] on the
18/// inner value to trigger PHP's Copy-on-Write separation before modifying
19/// arrays.
20///
21/// # Examples
22///
23/// ```rust,ignore
24/// use ext_php_rs::prelude::*;
25/// use ext_php_rs::types::Separated;
26///
27/// #[php_function]
28/// pub fn append_value(mut data: Separated) -> bool {
29///     let Some(ht) = data.array_mut() else {
30///         return false;
31///     };
32///     ht.push("appended").is_ok()
33/// }
34/// ```
35///
36/// PHP callers can pass literals directly:
37///
38/// ```php
39/// append_value([1, 2, 3]); // works — no & required
40/// ```
41#[repr(transparent)]
42pub struct Separated<'a>(&'a mut Zval);
43
44impl Deref for Separated<'_> {
45    type Target = Zval;
46
47    #[inline]
48    fn deref(&self) -> &Zval {
49        self.0
50    }
51}
52
53impl DerefMut for Separated<'_> {
54    #[inline]
55    fn deref_mut(&mut self) -> &mut Zval {
56        self.0
57    }
58}
59
60impl<'a> FromZvalMut<'a> for Separated<'a> {
61    const TYPE: DataType = DataType::Mixed;
62
63    #[inline]
64    fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
65        Some(Separated(zval))
66    }
67}
68
69impl std::fmt::Debug for Separated<'_> {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        f.debug_tuple("Separated")
72            .field(&self.0.get_type())
73            .finish()
74    }
75}