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

extphprs / ext-php-rs / 23737577490

30 Mar 2026 09:25AM UTC coverage: 66.102% (+0.2%) from 65.904%
23737577490

Pull #706

github

ptondereau
feat(types): add Separated and PhpRef wrappers for zval ownership (#526)

Decouples Rust mutability from PHP pass-by-reference semantics.
`Separated<'a>` gives `&mut Zval` access without ZEND_SEND_BY_REF,
allowing literal values like `foo([1,2,3])`. `PhpRef<'a>` is the
explicit opt-in for PHP pass-by-reference (`&$x`).
Pull Request #706: feat(types): add Separated and PhpRef wrappers for zval ownership

25 of 33 new or added lines in 3 files covered. (75.76%)

8114 of 12275 relevant lines covered (66.1%)

32.89 hits per line

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

75.0
/src/types/php_ref.rs
1
//! PHP reference wrapper that explicitly requires pass-by-reference from callers.
2

3
use std::ops::{Deref, DerefMut};
4

5
use crate::convert::FromZvalMut;
6
use crate::flags::DataType;
7
use crate::types::Zval;
8

9
/// A PHP reference (`&$x`) that explicitly requires the caller to pass by
10
/// reference.
11
///
12
/// Use this type in [`#[php_function]`](crate::php_function) signatures when
13
/// you need to modify the caller's original variable. This is the **only** type
14
/// that sets PHP's `ZEND_SEND_BY_REF` flag.
15
///
16
/// Mutations through `PhpRef` affect the original variable in the caller's
17
/// scope. The proc macro automatically dereferences the `zend_reference`
18
/// wrapper before passing the inner value to your function.
19
///
20
/// # When to use `PhpRef` vs `Separated`
21
///
22
/// | Type | PHP call syntax | Caller's variable modified? |
23
/// |---|---|---|
24
/// | [`Separated`](super::Separated) | `foo($x)` or `foo([1,2])` | No |
25
/// | `PhpRef` | `foo(&$x)` — literals rejected | Yes |
26
///
27
/// # Examples
28
///
29
/// ```rust,ignore
30
/// use ext_php_rs::prelude::*;
31
/// use ext_php_rs::types::PhpRef;
32
///
33
/// #[php_function]
34
/// pub fn increment(mut val: PhpRef) {
35
///     if let Some(n) = val.long() {
36
///         val.set_long(n + 1);
37
///     }
38
/// }
39
/// ```
40
///
41
/// ```php
42
/// $x = 5;
43
/// increment($x); // $x is now 6
44
/// ```
45
#[repr(transparent)]
46
pub struct PhpRef<'a>(&'a mut Zval);
47

48
impl Deref for PhpRef<'_> {
49
    type Target = Zval;
50

51
    #[inline]
52
    fn deref(&self) -> &Zval {
2✔
53
        self.0
2✔
54
    }
2✔
55
}
56

57
impl DerefMut for PhpRef<'_> {
58
    #[inline]
59
    fn deref_mut(&mut self) -> &mut Zval {
4✔
60
        self.0
4✔
61
    }
4✔
62
}
63

64
impl<'a> FromZvalMut<'a> for PhpRef<'a> {
65
    const TYPE: DataType = DataType::Mixed;
66

67
    #[inline]
68
    fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
4✔
69
        Some(PhpRef(zval))
4✔
70
    }
4✔
71
}
72

73
impl std::fmt::Debug for PhpRef<'_> {
NEW
74
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
NEW
75
        f.debug_tuple("PhpRef").field(&self.0.get_type()).finish()
×
NEW
76
    }
×
77
}
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