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    fn from_zend_object(obj: &'a ZendObject) -> Result<Self>;
83}
84
85/// Implemented on types which can be extracted from a mutable zend object.
86///
87/// If `Self` does not require the object to be mutable, it should implement
88/// [`FromZendObject`] instead, as this trait is generically implemented for
89/// any types that also implement [`FromZendObject`].
90pub trait FromZendObjectMut<'a>: Sized {
91    /// Extracts `Self` from the source `ZendObject`.
92    fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result<Self>;
93}
94
95impl<'a, T> FromZendObjectMut<'a> for T
96where
97    T: FromZendObject<'a>,
98{
99    #[inline]
100    fn from_zend_object_mut(obj: &'a mut ZendObject) -> Result<Self> {
101        Self::from_zend_object(obj)
102    }
103}
104
105/// Implemented on types which can be converted into a Zend object. It is up to
106/// the implementation to determine the type of object which is produced.
107pub trait IntoZendObject {
108    /// Attempts to convert `self` into a Zend object.
109    fn into_zend_object(self) -> Result<ZBox<ZendObject>>;
110}
111
112/// Provides implementations for converting Rust primitive types into PHP zvals.
113/// Alternative to the built-in Rust [`From`] and [`TryFrom`] implementations,
114/// allowing the caller to specify whether the Zval contents will persist
115/// between requests.
116///
117/// [`TryFrom`]: std::convert::TryFrom
118pub trait IntoZval: Sized {
119    /// The corresponding type of the implemented value in PHP.
120    const TYPE: DataType;
121
122    /// Converts a Rust primitive type into a Zval. Returns a result containing
123    /// the Zval if successful.
124    ///
125    /// # Parameters
126    ///
127    /// * `persistent` - Whether the contents of the Zval will persist between
128    ///   requests.
129    fn into_zval(self, persistent: bool) -> Result<Zval> {
130        let mut zval = Zval::new();
131        self.set_zval(&mut zval, persistent)?;
132        Ok(zval)
133    }
134
135    /// Sets the content of a pre-existing zval. Returns a result containing
136    /// nothing if setting the content was successful.
137    ///
138    /// # Parameters
139    ///
140    /// * `zv` - The Zval to set the content of.
141    /// * `persistent` - Whether the contents of the Zval will persist between
142    ///   requests.
143    fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()>;
144}
145
146impl IntoZval for () {
147    const TYPE: DataType = DataType::Void;
148
149    #[inline]
150    fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
151        zv.set_null();
152        Ok(())
153    }
154}
155
156impl<T> IntoZval for Option<T>
157where
158    T: IntoZval,
159{
160    const TYPE: DataType = T::TYPE;
161
162    #[inline]
163    fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> {
164        match self {
165            Some(val) => val.set_zval(zv, persistent),
166            None => {
167                zv.set_null();
168                Ok(())
169            }
170        }
171    }
172}
173
174impl<T, E> IntoZval for std::result::Result<T, E>
175where
176    T: IntoZval,
177    E: Into<PhpException>,
178{
179    const TYPE: DataType = T::TYPE;
180
181    fn set_zval(self, zv: &mut Zval, persistent: bool) -> Result<()> {
182        match self {
183            Ok(val) => val.set_zval(zv, persistent),
184            Err(e) => {
185                let ex: PhpException = e.into();
186                ex.throw()
187            }
188        }
189    }
190}
191
192/// An object-safe version of the [`IntoZval`] trait.
193///
194/// This trait is automatically implemented on any type that implements both
195/// [`IntoZval`] and [`Clone`]. You avoid implementing this trait directly,
196/// rather implement these two other traits.
197pub trait IntoZvalDyn {
198    /// Converts a Rust primitive type into a Zval. Returns a result containing
199    /// the Zval if successful. `self` is cloned before being converted into
200    /// a zval.
201    ///
202    /// # Parameters
203    ///
204    /// * `persistent` - Whether the contents of the Zval will persist between
205    ///   requests.
206    fn as_zval(&self, persistent: bool) -> Result<Zval>;
207
208    /// Returns the PHP type of the type.
209    fn get_type(&self) -> DataType;
210}
211
212impl<T: IntoZval + Clone> IntoZvalDyn for T {
213    fn as_zval(&self, persistent: bool) -> Result<Zval> {
214        self.clone().into_zval(persistent)
215    }
216
217    fn get_type(&self) -> DataType {
218        Self::TYPE
219    }
220}
221
222impl IntoZvalDyn for Zval {
223    fn as_zval(&self, _persistent: bool) -> Result<Zval> {
224        Ok(self.shallow_clone())
225    }
226
227    fn get_type(&self) -> DataType {
228        self.get_type()
229    }
230}