Skip to content

Commit 766582b

Browse files
committed
Fix lifetimes for zval arrays
1 parent dec4d8c commit 766582b

File tree

2 files changed

+117
-8
lines changed

2 files changed

+117
-8
lines changed

src/php/props.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use crate::errors::{Error, Result};
2+
3+
use super::{
4+
exceptions::PhpException,
5+
types::zval::{FromZval, IntoZval, Zval},
6+
};
7+
8+
/// Implemented on types which can be used as PHP properties.
9+
///
10+
/// Generally, this should not be directly implemented on types, as it is automatically implemented on
11+
/// types that implement [`Clone`], [`IntoZval`] and [`FromZval`], which will be required to implement
12+
/// this trait regardless.
13+
pub trait Prop<'zval> {
14+
/// Gets the value of `self` by setting the value of `zv`.
15+
///
16+
/// # Parameters
17+
///
18+
/// * `zv` - The zval to set the value of.
19+
fn get(&self, zv: &mut Zval) -> Result<()>;
20+
21+
/// Sets the value of `self` with the contents of a given zval `zv`.
22+
///
23+
/// # Parameters
24+
///
25+
/// * `zv` - The zval containing the new value of `self`.
26+
fn set(&mut self, zv: &'zval Zval) -> Result<()>;
27+
}
28+
29+
impl<'a, T: Clone + IntoZval + FromZval<'a>> Prop<'a> for T {
30+
fn get(&self, zv: &mut Zval) -> Result<()> {
31+
self.clone().set_zval(zv, false)
32+
}
33+
34+
fn set(&mut self, zv: &'a Zval) -> Result<()> {
35+
let x = Self::from_zval(zv).ok_or(Error::ZvalConversion(zv.get_type()?))?;
36+
*self = x;
37+
Ok(())
38+
}
39+
}
40+
41+
pub enum Property<'a, 'b, T> {
42+
Field(&'a mut dyn Prop<'a>),
43+
GetterSetter {
44+
get: Option<Box<dyn Fn(&'b T, &mut Zval) -> Option<()> + 'b>>,
45+
set: Option<Box<dyn FnMut(&'b mut T, Zval) -> Option<()> + 'b>>,
46+
},
47+
}
48+
49+
impl<'a, 'b, T> Property<'a, 'b, T> {
50+
pub fn field(field: &'a mut dyn Prop<'a>) -> Self {
51+
Self::Field(field)
52+
}
53+
54+
pub fn method<V>(get: Option<fn(&'b T) -> V>, set: Option<fn(&'b mut T, V)>) -> Self
55+
where
56+
V: IntoZval + From<Zval> + 'b,
57+
{
58+
let get = get.map(|get| {
59+
Box::new(move |self_: &'b T, zv: &mut Zval| {
60+
let result = get(self_);
61+
result.set_zval(zv, false).ok()?;
62+
Some(())
63+
}) as Box<dyn Fn(&'b T, &mut Zval) -> Option<()> + 'b>
64+
});
65+
66+
let set = set.map(|set| {
67+
Box::new(move |self_: &'b mut T, zv: Zval| {
68+
let val = zv.into();
69+
set(self_, val);
70+
Some(())
71+
}) as Box<dyn FnMut(&'b mut T, Zval) -> Option<()> + 'b>
72+
});
73+
74+
Self::GetterSetter { get, set }
75+
}
76+
77+
pub fn get(&self, self_: &'b T, zv: &mut Zval) -> Result<(), PhpException<'static>> {
78+
match self {
79+
Property::Field(field) => field
80+
.get(zv)
81+
.map_err(|e| PhpException::from(format!("Failed to get property value: {}", e))),
82+
Property::GetterSetter { get, set: _ } => {
83+
if let Some(get) = get {
84+
get(self_, zv)
85+
.ok_or_else(|| PhpException::from("Failed to get property value."))
86+
} else {
87+
Err(PhpException::from("This property has no get handler."))
88+
}
89+
}
90+
}
91+
}
92+
93+
pub fn set(&mut self, self_: &'b mut T, zv: &Zval) -> Result<(), PhpException<'static>> {
94+
todo!()
95+
// match self {
96+
// Property::Field(field) => field
97+
// .set(zv)
98+
// .map_err(|e| PhpException::from(format!("Failed to set property value: {}", e))),
99+
// Property::GetterSetter { get: _, set } => {
100+
// if let Some(set) = set {
101+
// set(self_, zv)
102+
// .ok_or_else(|| PhpException::from("Failed to set property value."))
103+
// } else {
104+
// Err(PhpException::from("This property has no set handler."))
105+
// }
106+
// }
107+
// }
108+
}
109+
}

src/php/types/zval.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub type Zval = zval;
3232
unsafe impl Send for Zval {}
3333
unsafe impl Sync for Zval {}
3434

35-
impl<'a> Zval {
35+
impl Zval {
3636
/// Creates a new, empty zval.
3737
pub(crate) const fn new() -> Self {
3838
// NOTE: Once the `Drop` implementation has been improved this can be public.
@@ -94,7 +94,7 @@ impl<'a> Zval {
9494
/// Note that this functions output will not be the same as [`string()`](#method.string), as
9595
/// this function does not attempt to convert other types into a [`String`], as it could not
9696
/// pass back a [`&str`] in those cases.
97-
pub fn str(&'a self) -> Option<&'a str> {
97+
pub fn str(&self) -> Option<&str> {
9898
if self.is_string() {
9999
// SAFETY: Zend strings have a length that we know we can read.
100100
// By reading this many bytes we will not run into any issues.
@@ -146,7 +146,7 @@ impl<'a> Zval {
146146
}
147147

148148
/// Returns the value of the zval if it is an array.
149-
pub fn array(&self) -> Option<ZendHashTable<'a>> {
149+
pub fn array(&self) -> Option<ZendHashTable> {
150150
if self.is_array() {
151151
unsafe { ZendHashTable::from_ptr(self.value.arr, false) }.ok()
152152
} else {
@@ -173,7 +173,7 @@ impl<'a> Zval {
173173
}
174174

175175
/// Returns the value of the zval if it is callable.
176-
pub fn callable(&'a self) -> Option<Callable<'a>> {
176+
pub fn callable(&self) -> Option<Callable> {
177177
// The Zval is checked if it is callable in the `new` function.
178178
Callable::new(self).ok()
179179
}
@@ -726,9 +726,9 @@ where
726726
}
727727
}
728728

729-
impl<'a, T> TryFrom<Zval> for Vec<T>
729+
impl<T> TryFrom<Zval> for Vec<T>
730730
where
731-
T: FromZval<'a>,
731+
for<'a> T: FromZval<'a>,
732732
{
733733
type Error = Error;
734734

@@ -751,9 +751,9 @@ where
751751
}
752752
}
753753

754-
impl<'a, T> TryFrom<Zval> for HashMap<String, T>
754+
impl<T> TryFrom<Zval> for HashMap<String, T>
755755
where
756-
T: FromZval<'a>,
756+
for<'a> T: FromZval<'a>,
757757
{
758758
type Error = Error;
759759

0 commit comments

Comments
 (0)