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}