Returning from functions (#9)

* Added ability to set value of zval

This allows the return value to now be set.
See the example in `example/skel/src.lib`.

* Added PHP internals book to resources

* Pass self as pointer to keep consistency
This commit is contained in:
David 2021-03-11 01:06:58 +13:00 committed by GitHub
parent 0dd1a3f80f
commit 8f108a61c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 231 additions and 7 deletions

View File

@ -14,6 +14,10 @@ See the [example project](example/skel). There is inline documentation. Starting
Contributions are very much welcome. I am a novice Rust developer and any suggestions are wanted and welcome. Feel free to file issues and PRs through Github.
## Resources
- [PHP Internals Book](https://www.phpinternalsbook.com/)
## License
```

View File

@ -7,7 +7,7 @@ use php_rs::{
function::FunctionBuilder,
module::{ModuleBuilder, ModuleEntry},
types::ZendLong,
zval::Zval,
zval::{SetZval, Zval},
},
};
@ -49,10 +49,12 @@ pub extern "C" fn skeleton_version(execute_data: *mut ExecutionData, _retval: *m
return;
}
println!(
let result = format!(
"x: {}, y: {}, z: {}",
x.val::<ZendLong>().unwrap_or_default(),
y.val::<f64>().unwrap_or_default(),
z.val::<f64>().unwrap_or_default()
);
_retval.set_string(result).unwrap();
}

View File

@ -1,8 +1,12 @@
use std::{ffi::c_void, ptr};
use crate::bindings::{
zend_long, zend_type, IS_MIXED, MAY_BE_ANY, MAY_BE_BOOL, _IS_BOOL, _ZEND_IS_VARIADIC_BIT,
_ZEND_SEND_MODE_SHIFT, _ZEND_TYPE_NULLABLE_BIT,
use crate::{
bindings::{
zend_long, zend_string, zend_strpprintf, zend_type, GC_FLAGS_MASK, GC_FLAGS_SHIFT,
GC_INFO_SHIFT, IS_MIXED, IS_STR_INTERNED, MAY_BE_ANY, MAY_BE_BOOL, _IS_BOOL,
_ZEND_IS_VARIADIC_BIT, _ZEND_SEND_MODE_SHIFT, _ZEND_TYPE_NULLABLE_BIT,
},
functions::c_str,
};
use super::enums::DataType;
@ -89,3 +93,35 @@ impl ZendType {
/// 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;
/// String type used in the Zend internals.
/// The actual size of the 'string' differs, as the
/// end of this struct is only 1 char long, but the length
/// inside the struct defines how many characters are in the string.
pub type ZendString = zend_string;
impl ZendString {
/// Creates a new Zend string.
///
/// Note that this returns a raw pointer, and will not be freed by
/// Rust.
///
/// # Parameters
///
/// * `str_` - The string to create a Zend string from.
pub fn new<S>(str_: S) -> *mut Self
where
S: AsRef<str>,
{
let str_ = str_.as_ref();
unsafe { zend_strpprintf(str_.len() as u64, c_str(str_)) }
}
/// Translation of the `ZSTR_IS_INTERNED` macro.
/// zend_string.h:76
pub(crate) unsafe fn is_interned(&self) -> bool {
(((self.gc.u.type_info >> GC_INFO_SHIFT) & (GC_FLAGS_MASK >> GC_FLAGS_SHIFT))
& IS_STR_INTERNED)
!= 0
}
}

View File

@ -1,9 +1,12 @@
use core::slice;
use std::convert::TryFrom;
use crate::bindings::{zend_object, zend_resource, zval};
use crate::bindings::{zend_object, zend_resource, zval, IS_INTERNED_STRING_EX, IS_STRING_EX};
use super::{enums::DataType, types::ZendLong};
use super::{
enums::DataType,
types::{ZendLong, ZendString},
};
/// Zend value. Represents most data types that are in the Zend engine.
pub type Zval = zval;
@ -149,6 +152,185 @@ impl Zval {
}
}
/// Used to set the value of the zval.
///
/// This needs to be a trait to be implemented on a pointer that
/// points to a zval.
pub trait SetZval {
/// Sets the value of the zval as a string.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
fn set_string<S>(&self, val: S) -> Result<(), String>
where
S: AsRef<str>;
/// Sets the value of the zval as a long.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
fn set_long(&self, val: ZendLong) -> Result<(), String>;
/// Sets the value of the zval as a double.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
fn set_double(&self, val: f64) -> Result<(), String>;
/// Sets the value of the zval as a boolean.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
fn set_bool(&self, val: bool) -> Result<(), String>;
/// Sets the value of the zval as null.
/// This is the default of a zval.
fn set_null(&self) -> Result<(), String>;
/// Sets the value of the zval as a resource.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
fn set_resource(&self, val: *mut zend_resource) -> Result<(), String>;
/// Sets the value of the zval as an object.
///
/// # Parameters
///
/// * `val` - The value to set the zval as.
/// * `copy` - Whether to copy the object or pass as a reference.
fn set_object(&self, val: *mut zend_object, copy: bool) -> Result<(), String>;
}
impl SetZval for *mut Zval {
fn set_string<S>(&self, val: S) -> Result<(), String>
where
S: AsRef<str>,
{
let _self = match unsafe { self.as_mut() } {
Some(val) => val,
None => {
return Err(String::from(
"Could not retrieve mutable reference of zend value.",
))
}
};
let zend_str = ZendString::new(val);
_self.value.str = zend_str;
_self.u1.type_info = if unsafe { zend_str.as_ref().unwrap().is_interned() } {
IS_INTERNED_STRING_EX
} else {
IS_STRING_EX
};
Ok(())
}
fn set_long(&self, val: ZendLong) -> Result<(), String> {
let _self = match unsafe { self.as_mut() } {
Some(val) => val,
None => {
return Err(String::from(
"Could not retrieve mutable reference of zend value.",
))
}
};
_self.value.lval = val;
_self.u1.type_info = DataType::Long as u32;
Ok(())
}
fn set_double(&self, val: f64) -> Result<(), String> {
let _self = match unsafe { self.as_mut() } {
Some(val) => val,
None => {
return Err(String::from(
"Could not retrieve mutable reference of zend value.",
))
}
};
_self.value.dval = val;
_self.u1.type_info = DataType::Double as u32;
Ok(())
}
fn set_bool(&self, val: bool) -> Result<(), String> {
let _self = match unsafe { self.as_mut() } {
Some(val) => val,
None => {
return Err(String::from(
"Could not retrieve mutable reference of zend value.",
))
}
};
_self.u1.type_info = if val {
DataType::True as u32
} else {
DataType::False as u32
};
Ok(())
}
fn set_null(&self) -> Result<(), String> {
let _self = match unsafe { self.as_mut() } {
Some(val) => val,
None => {
return Err(String::from(
"Could not retrieve mutable reference of zend value.",
))
}
};
_self.u1.type_info = DataType::Null as u32;
Ok(())
}
fn set_resource(&self, val: *mut zend_resource) -> Result<(), String> {
let _self = match unsafe { self.as_mut() } {
Some(val) => val,
None => {
return Err(String::from(
"Could not retrieve mutable reference of zend value.",
))
}
};
_self.u1.type_info = DataType::Resource as u32;
_self.value.res = val;
Ok(())
}
fn set_object(&self, val: *mut zend_object, _copy: bool) -> Result<(), String> {
let _self = match unsafe { self.as_mut() } {
Some(val) => val,
None => {
return Err(String::from(
"Could not retrieve mutable reference of zend value.",
))
}
};
_self.u1.type_info = DataType::Object as u32;
_self.value.obj = val;
Ok(())
}
}
impl TryFrom<&Zval> for ZendLong {
type Error = ();
fn try_from(value: &Zval) -> Result<Self, Self::Error> {