ext_php_rs/convert.rs
1//! Traits used to convert between Zend/PHP and Rust types.
2
3use crate::{
4 boxed::ZBox,
5 error::Result,
6 exception::PhpException,
7 flags::DataType,
8 types::{ZendObject, Zval},
9};
10
11/// Allows zvals to be converted into Rust types in a fallible way. Reciprocal
12/// of the [`IntoZval`] trait.
13pub trait FromZval<'a>: Sized {
14 /// The corresponding type of the implemented value in PHP.
15 const TYPE: DataType;
16
17 /// Attempts to retrieve an instance of `Self` from a reference to a
18 /// [`Zval`].
19 ///
20 /// # Parameters
21 ///
22 /// * `zval` - Zval to get value from.
23 fn from_zval(zval: &'a Zval) -> Option<Self>;
24}
25
26impl<'a, T> FromZval<'a> for Option<T>
27where
28 T: FromZval<'a>,
29{
30 const TYPE: DataType = T::TYPE;
31
32 fn from_zval(zval: &'a Zval) -> Option<Self> {
33 Some(T::from_zval(zval))
34 }
35}
36
37/// Allows mutable zvals to be converted into Rust types in a fallible way.
38///
39/// If `Self` does not require the zval to be mutable to be extracted, you
40/// should implement [`FromZval`] instead, as this trait is generically
41/// implemented for any type that implements [`FromZval`].
42pub trait FromZvalMut<'a>: Sized {
43 /// The corresponding type of the implemented value in PHP.
44 const TYPE: DataType;
45
46 /// Attempts to retrieve an instance of `Self` from a mutable reference to a
47 /// [`Zval`].
48 ///
49 /// # Parameters
50 ///
51 /// * `zval` - Zval to get value from.
52 fn from_zval_mut(zval: &'a mut Zval) -> Option<Self>;
53}
54
55impl<'a, T> FromZvalMut<'a> for T
56where
57 T: FromZval<'a>,
58{
59 const TYPE: DataType = <T as FromZval>::TYPE;
60
61 #[inline]
62 fn from_zval_mut(zval: &'a mut Zval) -> Option<Self> {
63 Self::from_zval(zval)
64 }
65}
66
67/// `FromZendObject` is implemented by types which can be extracted from a Zend
68/// object.
69///
70/// Normal usage is through the helper method `ZendObject::extract`:
71///
72/// ```rust,ignore
73/// let obj: ZendObject = ...;
74/// let repr: String = obj.extract();
75/// let props: HashMap = obj.extract();
76/// ```
77///
78/// Should be functionally equivalent to casting an object to another compatible
79/// type.
80pub trait FromZendObject<'a>: Sized {
81 /// Extracts `Self` from the source `ZendObject`.
82 ///
83 /// # Errors
84 ///
85 /// If the conversion fails, an [`Error`] is returned.
86 ///
87 /// [`Error`]: crate::error::Error
88 // TODO: Expand on error information
89 fn from_zend_object(obj: &'a ZendObject) -> Result<Self>;
90}
91
92/// Implemented on types which can be extracted from a mutable zend object.
93///
94/// If `Self` does not require the object to be mutable, it should implement
95/// [`FromZendObject`] instead, as this trait is generically implemented for
96/// any types that also implement [`FromZendObject`].
97pub trait FromZendObjectMut<'a>: Sized {
98 /// Extracts `Self` from the source `ZendObject`.
99 ///
100 /// # Errors
101 ///
102 /// If the conversion fails, an [`Error`] is returned.
103 ///
104 /// [`Error`]: crate::error::Error
105 // TODO: Expand on error information
106 fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result<Self>;
107}
108
109impl<'a, T> FromZendObjectMut<'a> for T
110where
111 T: FromZendObject<'a>,
112{
113 #[inline]
114 fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result<Self> {
115 Self::from_zend_object(obj)
116 }
117}
118
119/// Implemented on types which can be converted into a Zend object. It is up to
120/// the implementation to determine the type of object which is produced.
121pub trait IntoZendObject {
122 /// Attempts to convert `self` into a Zend object.
123 ///
124 /// # Errors
125 ///
126 /// If the conversion fails, an [`Error`] is returned.
127 ///
128 /// [`Error`]: crate::error::Error
129 // TODO: Expand on error information
130 fn into_zend_object(self) -> Result<ZBox<ZendObject>>;
131}
132
133/// Provides implementations for converting Rust primitive types into PHP zvals.
134/// Alternative to the built-in Rust [`From`] and [`TryFrom`] implementations,
135/// allowing the caller to specify whether the Zval contents will persist
136/// between requests.
137///
138/// [`TryFrom`]: std::convert::TryFrom
139pub trait IntoZval: Sized {
140 /// The corresponding type of the implemented value in PHP.
141 const TYPE: DataType;
142
143 /// Whether converting into a [`Zval`] may result in null.
144 const NULLABLE: bool;
145
146 /// Converts a Rust primitive type into a Zval. Returns a result containing
147 /// the Zval if successful.
148 ///
149 /// # Parameters
150 ///
151 /// * `persistent` - Whether the contents of the Zval will persist between
152 /// requests.
153 ///
154 /// # Errors
155 ///
156 /// If the conversion fails, an [`Error`] is returned.
157 ///
158 /// [`Error`]: crate::error::Error
159 // TODO: Expand on error information
160 fn into_zval(self, persistent: bool) -> Result<Zval> {
161 let mut zval = Zval::new();
162 self.set_zval(&mut zval, persistent)?;
163 Ok(zval)
164 }
165
166 /// Sets the content of a pre-existing zval. Returns a result containing
167 /// nothing if setting the content was successful.
168 ///
169 /// # Parameters
170 ///
171 /// * `zv` - The Zval to set the content of.
172 /// * `persistent` - Whether the contents of the Zval will persist between
173 /// requests.
174 ///
175 /// # Errors
176 ///
177 /// If setting the content fails, an [`Error`] is returned.
178 ///
179 /// [`Error`]: crate::error::Error
180 // TODO: Expand on error information
181 fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()>;
182}
183
184impl IntoZval for () {
185 const TYPE: DataType = DataType::Void;
186 const NULLABLE: bool = true;
187
188 #[inline]
189 fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
190 zv.set_null();
191 Ok(())
192 }
193}
194
195impl<T> IntoZval for Option<T>
196where
197 T: IntoZval,
198{
199 const TYPE: DataType = T::TYPE;
200 const NULLABLE: bool = true;
201
202 #[inline]
203 fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> {
204 if let Some(val) = self {
205 val.set_zval(zv, persistent)
206 } else {
207 zv.set_null();
208 Ok(())
209 }
210 }
211}
212
213impl<T, E> IntoZval for std::result::Result<T, E>
214where
215 T: IntoZval,
216 E: Into<PhpException>,
217{
218 const TYPE: DataType = T::TYPE;
219 const NULLABLE: bool = T::NULLABLE;
220
221 fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> {
222 match self {
223 Ok(val) => val.set_zval(zv, persistent),
224 Err(e) => {
225 let ex: PhpException = e.into();
226 ex.throw()
227 }
228 }
229 }
230}
231
232/// An object-safe version of the [`IntoZval`] trait.
233///
234/// This trait is automatically implemented on any type that implements both
235/// [`IntoZval`] and [`Clone`]. You avoid implementing this trait directly,
236/// rather implement these two other traits.
237pub trait IntoZvalDyn {
238 /// Converts a Rust primitive type into a Zval. Returns a result containing
239 /// the Zval if successful. `self` is cloned before being converted into
240 /// a zval.
241 ///
242 /// # Parameters
243 ///
244 /// * `persistent` - Whether the contents of the Zval will persist between
245 /// requests.
246 ///
247 /// # Errors
248 ///
249 /// If the conversion fails, an [`Error`] is returned.
250 ///
251 /// [`Error`]: crate::error::Error
252 fn as_zval(&self, persistent: bool) -> Result<Zval>;
253
254 /// Returns the PHP type of the type.
255 fn get_type(&self) -> DataType;
256}
257
258impl<T: IntoZval + Clone> IntoZvalDyn for T {
259 fn as_zval(&self, persistent: bool) -> Result<Zval> {
260 self.clone().into_zval(persistent)
261 }
262
263 fn get_type(&self) -> DataType {
264 Self::TYPE
265 }
266}
267
268impl IntoZvalDyn for Zval {
269 fn as_zval(&self, _persistent: bool) -> Result<Zval> {
270 Ok(self.shallow_clone())
271 }
272
273 fn get_type(&self) -> DataType {
274 self.get_type()
275 }
276}