From 0dd1a3f80fcacc5d8ee06043b3ae5af4d23aff1a Mon Sep 17 00:00:00 2001 From: David Date: Wed, 10 Mar 2021 21:09:18 +1300 Subject: [PATCH] Added argument overloading (#8) * Added argument overloading If a double is requested but a long is available, we can cast the long into a double. This is required if the user gives an integer into a double field. As well as this, if a string is requested but a long or double is available, we can cast both of these into a string. * Check if zval is present before setting in arg * Added optional argument example * Updated documentation for ZendLong * WIP: added null checks --- example/skel/src/lib.rs | 13 +++++++--- src/php/args.rs | 56 ++++++++++++++++++++++++++++++++++++++--- src/php/enums.rs | 3 ++- src/php/types.rs | 2 ++ src/php/zval.rs | 4 +++ 5 files changed, 71 insertions(+), 7 deletions(-) diff --git a/example/skel/src/lib.rs b/example/skel/src/lib.rs index 47b0178..195f2db 100644 --- a/example/skel/src/lib.rs +++ b/example/skel/src/lib.rs @@ -36,16 +36,23 @@ pub extern "C" fn get_module() -> *mut php_rs::php::module::ModuleEntry { pub extern "C" fn skeleton_version(execute_data: *mut ExecutionData, _retval: *mut Zval) { let mut x = Arg::new("x", DataType::Long); let mut y = Arg::new("y", DataType::Double); + let mut z = Arg::new("z", DataType::Double); - let result = ArgParser::new(execute_data).arg(&mut x).arg(&mut y).parse(); + let result = ArgParser::new(execute_data) + .arg(&mut x) + .arg(&mut y) + .not_required() + .arg(&mut z) + .parse(); if let Err(_) = result { return; } println!( - "x: {}, y: {}", + "x: {}, y: {}, z: {}", x.val::().unwrap_or_default(), - y.val::().unwrap_or_default() + y.val::().unwrap_or_default(), + z.val::().unwrap_or_default() ); } diff --git a/src/php/args.rs b/src/php/args.rs index c399c22..9a37ee9 100644 --- a/src/php/args.rs +++ b/src/php/args.rs @@ -1,8 +1,18 @@ -use std::convert::{TryFrom, TryInto}; +use std::{ + borrow::Borrow, + convert::{TryFrom, TryInto}, +}; use super::{enums::DataType, execution_data::ExecutionData, zval::Zval}; -use crate::bindings::{zend_internal_arg_info, zend_wrong_parameters_count_error}; +use crate::bindings::{ + _zend_expected_type, _zend_expected_type_Z_EXPECTED_ARRAY, _zend_expected_type_Z_EXPECTED_BOOL, + _zend_expected_type_Z_EXPECTED_DOUBLE, _zend_expected_type_Z_EXPECTED_LONG, + _zend_expected_type_Z_EXPECTED_OBJECT, _zend_expected_type_Z_EXPECTED_RESOURCE, + _zend_expected_type_Z_EXPECTED_STRING, zend_internal_arg_info, zend_wrong_parameter_error, + zend_wrong_parameters_count_error, ZPP_ERROR_WRONG_CLASS_OR_NULL, +}; +use crate::functions::c_str; /// Represents an argument to a function. pub struct Arg<'a> { @@ -73,6 +83,27 @@ impl<'a> Arg<'a> { } } +impl From> for _zend_expected_type { + fn from(arg: Arg) -> Self { + let err = match arg._type { + DataType::False | DataType::True => _zend_expected_type_Z_EXPECTED_BOOL, + DataType::Long => _zend_expected_type_Z_EXPECTED_LONG, + DataType::Double => _zend_expected_type_Z_EXPECTED_DOUBLE, + DataType::String => _zend_expected_type_Z_EXPECTED_STRING, + DataType::Array => _zend_expected_type_Z_EXPECTED_ARRAY, + DataType::Object => _zend_expected_type_Z_EXPECTED_OBJECT, + DataType::Resource => _zend_expected_type_Z_EXPECTED_RESOURCE, + _ => unreachable!(), + }; + + if arg.allow_null { + err + 1 + } else { + err + } + } +} + /// Internal argument information used by Zend. pub type ArgInfo = zend_internal_arg_info; @@ -142,7 +173,26 @@ impl<'a, 'b> ArgParser<'a, 'b> { for (i, arg) in self.args.iter_mut().enumerate() { let zval = unsafe { execute_data.zend_call_arg(i) }; - arg.zval = Some(zval.unwrap()); + + if let Some(zval) = zval { + // if !arg.allow_null && zval.is_null() { + // unsafe { + // zend_wrong_parameter_error( + // ZPP_ERROR_WRONG_CLASS_OR_NULL as i32, + // i as u32, + // c_str(arg.name) as *mut i8, + // _zend_expected_type::from(**arg), + // &mut *zval, + // ); + // } + // return Err(format!( + // "Argument at index {} was null but is non-nullable.", + // i + // )); + // } + + arg.zval = Some(zval); + } } Ok(()) diff --git a/src/php/enums.rs b/src/php/enums.rs index 13d9a4d..b9a867e 100644 --- a/src/php/enums.rs +++ b/src/php/enums.rs @@ -9,6 +9,7 @@ use super::types::ZendLong; #[derive(Clone, Copy)] pub enum DataType { Undef = IS_UNDEF as isize, + Null = IS_NULL as isize, False = IS_FALSE as isize, True = IS_TRUE as isize, @@ -19,8 +20,8 @@ pub enum DataType { Object = IS_OBJECT as isize, Resource = IS_RESOURCE as isize, Reference = IS_REFERENCE as isize, - ConstantExpression = IS_CONSTANT_AST as isize, + ConstantExpression = IS_CONSTANT_AST as isize, Void = IS_VOID as isize, } diff --git a/src/php/types.rs b/src/php/types.rs index 0290a87..d899bf2 100644 --- a/src/php/types.rs +++ b/src/php/types.rs @@ -86,4 +86,6 @@ impl ZendType { } /// Internal identifier used for a long. +/// The size depends on the system architecture. On 32-bit systems, +/// a ZendLong is 32-bits, while on a 64-bit system it is 64-bits. pub type ZendLong = zend_long; diff --git a/src/php/zval.rs b/src/php/zval.rs index f3fdb91..8c22904 100644 --- a/src/php/zval.rs +++ b/src/php/zval.rs @@ -33,6 +33,8 @@ impl Zval { pub fn double(&self) -> Option { if self.is_double() { Some(unsafe { self.value.dval }) + } else if let Some(long) = self.long() { + Some(long as f64) } else { None } @@ -53,6 +55,8 @@ impl Zval { Some(_str.to_string()) } + } else if let Some(double) = self.double() { + Some(double.to_string()) } else { None }