Skip to content

Commit 8b0adf1

Browse files
Add try_from into FromZval trait (#62)
Allows generic implementations of the trait
1 parent 72b0491 commit 8b0adf1

File tree

6 files changed

+81
-112
lines changed

6 files changed

+81
-112
lines changed

example/skel/src/lib.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ pub struct Test {
128128
pub test: String,
129129
}
130130

131+
#[php_function]
132+
pub fn take_test(test: &Test) -> String {
133+
test.test.clone()
134+
}
135+
131136
#[php_class]
132137
#[derive(Default)]
133138
struct PhpFuture {
@@ -150,11 +155,9 @@ impl PhpFuture {
150155
}
151156

152157
pub fn obj(&self) -> ClassObject<Test> {
153-
let obj = ClassObject::new(Test {
158+
ClassObject::new(Test {
154159
test: "Hello world from class entry :)".into(),
155-
});
156-
157-
obj
160+
})
158161
}
159162

160163
pub fn return_self(&self) -> ClassRef<PhpFuture> {

src/php/args.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
//! Builder and objects relating to function and method arguments.
22
3-
use std::{
4-
convert::{TryFrom, TryInto},
5-
ffi::CString,
6-
ptr,
7-
};
3+
use std::{ffi::CString, ptr};
84

95
use super::{
106
enums::DataType,
117
execution_data::ExecutionData,
128
types::{
13-
zval::{IntoZvalDyn, Zval},
9+
zval::{FromZval, IntoZvalDyn, Zval},
1410
ZendType,
1511
},
1612
};
@@ -85,8 +81,8 @@ impl<'a> Arg<'a> {
8581
/// Attempts to retrieve the value of the argument.
8682
/// This will be None until the ArgParser is used to parse
8783
/// the arguments.
88-
pub fn val<T: TryFrom<&'a Zval>>(&self) -> Option<T> {
89-
self.zval.and_then(|zv| zv.try_into().ok())
84+
pub fn val<T: FromZval<'a>>(&self) -> Option<T> {
85+
self.zval.and_then(|zv| T::from_zval(zv))
9086
}
9187

9288
/// Attempts to return a reference to the arguments internal Zval.

src/php/types/array.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ use crate::{
1818
HT_MIN_SIZE,
1919
},
2020
errors::{Error, Result},
21-
php::enums::DataType,
2221
};
2322

2423
use super::{
2524
string::ZendString,
26-
zval::{IntoZval, Zval},
25+
zval::{FromZval, IntoZval, Zval},
2726
};
2827

2928
/// Result type returned after attempting to insert an element into a hash table.
@@ -369,7 +368,7 @@ build_iter!(IntoIter, ZendHashTable<'a>);
369368

370369
impl<'a, V> TryFrom<ZendHashTable<'a>> for HashMap<String, V>
371370
where
372-
V: TryFrom<&'a Zval>,
371+
V: FromZval<'a>,
373372
{
374373
type Error = Error;
375374

@@ -379,8 +378,7 @@ where
379378
for (idx, key, val) in zht.into_iter() {
380379
hm.insert(
381380
key.unwrap_or_else(|| idx.to_string()),
382-
val.try_into()
383-
.map_err(|_| Error::ZvalConversion(DataType::Null))?,
381+
V::from_zval(val).ok_or(Error::ZvalConversion(val.get_type()?))?,
384382
);
385383
}
386384

@@ -432,16 +430,13 @@ where
432430
/// a type `T`.
433431
impl<'a, V> TryFrom<ZendHashTable<'a>> for Vec<V>
434432
where
435-
V: TryFrom<&'a Zval>,
433+
V: FromZval<'a>,
436434
{
437435
type Error = Error;
438436

439437
fn try_from(ht: ZendHashTable<'a>) -> Result<Self> {
440438
ht.into_iter()
441-
.map(|(_, _, v)| {
442-
v.try_into()
443-
.map_err(|_| Error::ZvalConversion(DataType::Null))
444-
})
439+
.map(|(_, _, v)| V::from_zval(v).ok_or(Error::ZvalConversion(v.get_type()?)))
445440
.collect::<Result<Vec<_>>>()
446441
}
447442
}

src/php/types/binary.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Types relating to binary data transmission between Rust and PHP.
22
33
use std::{
4-
convert::{TryFrom, TryInto},
4+
convert::TryFrom,
55
iter::FromIterator,
66
ops::{Deref, DerefMut},
77
};
@@ -44,26 +44,19 @@ impl<T: Pack> DerefMut for Binary<T> {
4444
}
4545
}
4646

47-
impl<'a, T: Pack> FromZval<'a> for Binary<T> {
47+
impl<T: Pack> FromZval<'_> for Binary<T> {
4848
const TYPE: DataType = DataType::String;
49-
}
50-
51-
impl<T: Pack> TryFrom<&Zval> for Binary<T> {
52-
type Error = Error;
5349

54-
fn try_from(value: &Zval) -> Result<Self> {
55-
match value.binary() {
56-
Some(b) => Ok(Binary(b)),
57-
None => Err(Error::Callable),
58-
}
50+
fn from_zval(zval: &Zval) -> Option<Self> {
51+
zval.binary().map(Binary)
5952
}
6053
}
6154

6255
impl<T: Pack> TryFrom<Zval> for Binary<T> {
6356
type Error = Error;
6457

6558
fn try_from(value: Zval) -> Result<Self> {
66-
(&value).try_into()
59+
Self::from_zval(&value).ok_or(Error::ZvalConversion(value.get_type()?))
6760
}
6861
}
6962

src/php/types/object.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,38 @@ impl<T: RegisteredClass> IntoZval for T {
343343
}
344344
}
345345

346+
impl<'a, T: RegisteredClass> FromZval<'a> for &'a T {
347+
const TYPE: DataType = DataType::Object;
348+
349+
fn from_zval(zval: &'a Zval) -> Option<Self> {
350+
let obj = zval.object()?;
351+
352+
if obj.is_instance::<T>() {
353+
// SAFETY: If the zend object is an instance of `T`, we can guarantee that the memory before
354+
// it is occupied by an instance of `T`.
355+
unsafe { ((obj as *mut ZendObject) as *mut T).offset(-1).as_ref() }
356+
} else {
357+
None
358+
}
359+
}
360+
}
361+
362+
impl<'a, T: RegisteredClass> FromZval<'a> for &'a mut T {
363+
const TYPE: DataType = DataType::Object;
364+
365+
fn from_zval(zval: &'a Zval) -> Option<Self> {
366+
let obj = zval.object()?;
367+
368+
if obj.is_instance::<T>() {
369+
// SAFETY: If the zend object is an instance of `T`, we can guarantee that the memory before
370+
// it is occupied by an instance of `T`.
371+
unsafe { ((obj as *mut ZendObject) as *mut T).offset(-1).as_mut() }
372+
} else {
373+
None
374+
}
375+
}
376+
}
377+
346378
/// Implemented on Rust types which are exported to PHP. Allows users to get and set PHP properties on
347379
/// the object.
348380
pub trait RegisteredClass: Default + Sized
@@ -375,7 +407,7 @@ where
375407
unsafe fn get_property<'a, T: FromZval<'a>>(&'a self, name: &str) -> Option<T> {
376408
let obj = ZendClassObject::<Self>::from_obj_ptr(self)?;
377409
let zv = obj.std.get_property(name).ok()?;
378-
zv.try_into().ok()
410+
T::from_zval(zv)
379411
}
380412

381413
/// Attempts to set the value of a property on the class object.

src/php/types/zval.rs

Lines changed: 27 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -629,53 +629,44 @@ where
629629

630630
/// Allows zvals to be converted into Rust types in a fallible way. Reciprocal of the [`IntoZval`]
631631
/// trait.
632-
///
633-
/// This trait requires the [`TryFrom`] trait to be implemented. All this trait does is contain the
634-
/// type of data that is expected when parsing the value, which is used when parsing arguments.
635-
pub trait FromZval<'a>: TryFrom<&'a Zval> {
632+
pub trait FromZval<'a>: Sized {
636633
/// The corresponding type of the implemented value in PHP.
637634
const TYPE: DataType;
635+
636+
/// Attempts to retrieve an instance of `Self` from a reference to a [`Zval`].
637+
///
638+
/// # Parameters
639+
///
640+
/// * `zval` - Zval to get value from.
641+
fn from_zval(zval: &'a Zval) -> Option<Self>;
638642
}
639643

640644
impl<'a, T> FromZval<'a> for Option<T>
641645
where
642646
T: FromZval<'a>,
643647
{
644648
const TYPE: DataType = T::TYPE;
645-
}
646649

647-
// Converting to an option is infallible.
648-
impl<'a, T> From<&'a Zval> for Option<T>
649-
where
650-
T: FromZval<'a>,
651-
{
652-
fn from(val: &'a Zval) -> Self {
653-
val.try_into().ok()
650+
fn from_zval(zval: &'a Zval) -> Option<Self> {
651+
Some(T::from_zval(zval))
654652
}
655653
}
656654

657655
macro_rules! try_from_zval {
658656
($type: ty, $fn: ident, $dt: ident) => {
659-
impl<'a> FromZval<'a> for $type {
657+
impl FromZval<'_> for $type {
660658
const TYPE: DataType = DataType::$dt;
661-
}
662-
663-
impl TryFrom<&Zval> for $type {
664-
type Error = Error;
665659

666-
fn try_from(value: &Zval) -> Result<Self> {
667-
value
668-
.$fn()
669-
.and_then(|val| val.try_into().ok())
670-
.ok_or(Error::ZvalConversion(value.get_type()?))
660+
fn from_zval(zval: &Zval) -> Option<Self> {
661+
zval.$fn().and_then(|val| val.try_into().ok())
671662
}
672663
}
673664

674665
impl TryFrom<Zval> for $type {
675666
type Error = Error;
676667

677668
fn try_from(value: Zval) -> Result<Self> {
678-
(&value).try_into()
669+
Self::from_zval(&value).ok_or(Error::ZvalConversion(value.get_type()?))
679670
}
680671
}
681672
};
@@ -698,44 +689,27 @@ try_from_zval!(f64, double, Double);
698689
try_from_zval!(bool, bool, Bool);
699690
try_from_zval!(String, string, String);
700691

701-
impl<'a> FromZval<'a> for f32 {
692+
impl FromZval<'_> for f32 {
702693
const TYPE: DataType = DataType::Double;
703-
}
704-
705-
impl<'a> TryFrom<&'a Zval> for f32 {
706-
type Error = Error;
707694

708-
fn try_from(value: &'a Zval) -> Result<Self> {
709-
value
710-
.double()
711-
.map(|v| v as f32)
712-
.ok_or(Error::ZvalConversion(value.get_type()?))
695+
fn from_zval(zval: &Zval) -> Option<Self> {
696+
zval.double().map(|v| v as f32)
713697
}
714698
}
715699

716700
impl<'a> FromZval<'a> for &'a str {
717701
const TYPE: DataType = DataType::String;
718-
}
719-
720-
impl<'a> TryFrom<&'a Zval> for &'a str {
721-
type Error = Error;
722702

723-
fn try_from(value: &'a Zval) -> Result<Self> {
724-
value.str().ok_or(Error::ZvalConversion(value.get_type()?))
703+
fn from_zval(zval: &'a Zval) -> Option<Self> {
704+
zval.str()
725705
}
726706
}
727707

728708
impl<'a> FromZval<'a> for ZendHashTable<'a> {
729709
const TYPE: DataType = DataType::Array;
730-
}
731-
732-
impl<'a> TryFrom<&'a Zval> for ZendHashTable<'a> {
733-
type Error = Error;
734710

735-
fn try_from(value: &'a Zval) -> Result<Self> {
736-
value
737-
.array()
738-
.ok_or(Error::ZvalConversion(value.get_type()?))
711+
fn from_zval(zval: &'a Zval) -> Option<Self> {
712+
zval.array()
739713
}
740714
}
741715

@@ -744,19 +718,9 @@ where
744718
T: FromZval<'a>,
745719
{
746720
const TYPE: DataType = DataType::Array;
747-
}
748-
749-
impl<'a, T> TryFrom<&'a Zval> for Vec<T>
750-
where
751-
T: FromZval<'a>,
752-
{
753-
type Error = Error;
754721

755-
fn try_from(value: &'a Zval) -> Result<Self> {
756-
value
757-
.array()
758-
.ok_or(Error::ZvalConversion(value.get_type()?))?
759-
.try_into()
722+
fn from_zval(zval: &'a Zval) -> Option<Self> {
723+
zval.array().and_then(|arr| arr.try_into().ok())
760724
}
761725
}
762726

@@ -779,19 +743,9 @@ where
779743
T: FromZval<'a>,
780744
{
781745
const TYPE: DataType = DataType::Array;
782-
}
783746

784-
impl<'a, T> TryFrom<&'a Zval> for HashMap<String, T>
785-
where
786-
T: FromZval<'a>,
787-
{
788-
type Error = Error;
789-
790-
fn try_from(value: &'a Zval) -> Result<Self> {
791-
value
792-
.array()
793-
.ok_or(Error::ZvalConversion(value.get_type()?))?
794-
.try_into()
747+
fn from_zval(zval: &'a Zval) -> Option<Self> {
748+
zval.array().and_then(|arr| arr.try_into().ok())
795749
}
796750
}
797751

@@ -811,13 +765,9 @@ where
811765

812766
impl<'a> FromZval<'a> for Callable<'a> {
813767
const TYPE: DataType = DataType::Callable;
814-
}
815-
816-
impl<'a> TryFrom<&'a Zval> for Callable<'a> {
817-
type Error = Error;
818768

819-
fn try_from(value: &'a Zval) -> Result<Self> {
820-
Callable::new(value)
769+
fn from_zval(zval: &'a Zval) -> Option<Self> {
770+
Callable::new(zval).ok()
821771
}
822772
}
823773

0 commit comments

Comments
 (0)