mirror of
https://github.com/danog/ext-php-rs.git
synced 2024-11-26 20:15:22 +01:00
Remove uses of unwrap
, improve library safety (#47)
* Remove uses of `unwrap`, improve library safety * `set_array` doesn't return `Result`
This commit is contained in:
parent
0031df1604
commit
2a0e1f8086
@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, convert::TryInto};
|
||||
|
||||
use ext_php_rs::{
|
||||
call_user_func, info_table_end, info_table_row, info_table_start, parse_args,
|
||||
@ -76,7 +76,7 @@ impl Test {
|
||||
|
||||
let result = call_user_func!(_fn, "Hello", 5);
|
||||
|
||||
if let Some(r) = result {
|
||||
if let Ok(r) = result {
|
||||
println!("{}", r.string().unwrap());
|
||||
}
|
||||
|
||||
@ -105,37 +105,52 @@ pub extern "C" fn module_init(_type: i32, module_number: i32) -> i32 {
|
||||
|
||||
ClassBuilder::new("TestClass")
|
||||
.method(
|
||||
FunctionBuilder::constructor(Test::constructor).build(),
|
||||
FunctionBuilder::constructor(Test::constructor)
|
||||
.build()
|
||||
.expect("could not build constructor"),
|
||||
MethodFlags::Public,
|
||||
)
|
||||
.method(
|
||||
FunctionBuilder::new("set", Test::set).build(),
|
||||
FunctionBuilder::new("set", Test::set)
|
||||
.build()
|
||||
.expect("could not build set"),
|
||||
MethodFlags::Public,
|
||||
)
|
||||
.method(
|
||||
FunctionBuilder::new("get", Test::get).build(),
|
||||
FunctionBuilder::new("get", Test::get)
|
||||
.build()
|
||||
.expect("could not build get"),
|
||||
MethodFlags::Public,
|
||||
)
|
||||
.method(
|
||||
FunctionBuilder::new("debug", Test::debug)
|
||||
.arg(Arg::new("val", DataType::Object))
|
||||
.build(),
|
||||
.build()
|
||||
.expect("could not build debug"),
|
||||
MethodFlags::Public,
|
||||
)
|
||||
.method(
|
||||
FunctionBuilder::new("call", Test::call)
|
||||
.arg(Arg::new("fn", DataType::Callable))
|
||||
.build(),
|
||||
.build()
|
||||
.expect("could not build call"),
|
||||
MethodFlags::Public,
|
||||
)
|
||||
.property("asdf", "world", PropertyFlags::Public)
|
||||
.expect("failed to add asdf property")
|
||||
.property("jhki", 12345, PropertyFlags::Public)
|
||||
.expect("failed to add jhki property")
|
||||
.constant("TEST", "Hello world")
|
||||
.expect("failed to add test constant")
|
||||
.object_override::<Test>()
|
||||
.build();
|
||||
.build()
|
||||
.expect("failed to build TestClass");
|
||||
|
||||
"Test constant".register_constant("SKEL_TEST_CONST", module_number);
|
||||
1234.register_constant("SKEL_TEST_LONG_CONST", module_number);
|
||||
"Test constant"
|
||||
.register_constant("SKEL_TEST_CONST", module_number)
|
||||
.expect("failed to add skel test const");
|
||||
1234.register_constant("SKEL_TEST_LONG_CONST", module_number)
|
||||
.expect("failed to add skel test long const");
|
||||
|
||||
0
|
||||
}
|
||||
@ -148,20 +163,24 @@ pub extern "C" fn get_module() -> *mut ext_php_rs::php::module::ModuleEntry {
|
||||
.not_required()
|
||||
.arg(Arg::new("c", DataType::Double))
|
||||
.returns(DataType::String, false, false)
|
||||
.build();
|
||||
.build()
|
||||
.expect("failed to build sheleton_version");
|
||||
|
||||
let array = FunctionBuilder::new("skel_array", skeleton_array)
|
||||
.arg(Arg::new("arr", DataType::Array))
|
||||
.build();
|
||||
.build()
|
||||
.expect("failed to build skel_array");
|
||||
|
||||
let t = FunctionBuilder::new("test_array", test_array)
|
||||
.returns(DataType::Array, false, false)
|
||||
.build();
|
||||
.build()
|
||||
.expect("failed to build test_array");
|
||||
|
||||
let iter = FunctionBuilder::new("skel_unpack", skel_unpack)
|
||||
.arg(Arg::new("arr", DataType::String))
|
||||
.returns(DataType::String, false, false)
|
||||
.build();
|
||||
.build()
|
||||
.expect("failed to build skel_unpack");
|
||||
|
||||
ModuleBuilder::new("ext-skel", "0.1.0")
|
||||
.info_function(php_module_info)
|
||||
@ -171,6 +190,7 @@ pub extern "C" fn get_module() -> *mut ext_php_rs::php::module::ModuleEntry {
|
||||
.function(t)
|
||||
.function(iter)
|
||||
.build()
|
||||
.expect("failed to build module")
|
||||
.into_raw()
|
||||
}
|
||||
|
||||
@ -182,7 +202,7 @@ pub extern "C" fn skeleton_version(execute_data: &mut ExecutionData, retval: &mu
|
||||
|
||||
parse_args!(execute_data, x, y; z);
|
||||
dbg!(x);
|
||||
retval.set_string("Hello");
|
||||
retval.set_string("Hello", false);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -201,13 +221,18 @@ pub extern "C" fn skeleton_array(execute_data: &mut ExecutionData, _retval: &mut
|
||||
}
|
||||
|
||||
let mut new = ZendHashTable::new();
|
||||
new.insert("Hello", "WOrld");
|
||||
new.insert("Hello", "WOrld")
|
||||
.expect("couldnt insert into hashtable");
|
||||
let _ = _retval.set_array(new);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn test_array(_execute_data: &mut ExecutionData, retval: &mut Zval) {
|
||||
retval.set_array(vec![1, 2, 3, 4]);
|
||||
retval.set_array(
|
||||
vec![1, 2, 3, 4]
|
||||
.try_into()
|
||||
.expect("failed to convert vec to hashtable"),
|
||||
);
|
||||
}
|
||||
|
||||
pub extern "C" fn skel_unpack(execute_data: &mut ExecutionData, retval: &mut Zval) {
|
||||
|
@ -32,10 +32,12 @@ pub fn object_handler_derive(input: TokenStream) -> TokenStream {
|
||||
#handlers = Some(::ext_php_rs::php::types::object::ZendObjectHandlers::init::<#name>());
|
||||
}
|
||||
|
||||
// The handlers unwrap can never fail - we check that it is none above.
|
||||
// Unwrapping the result from `new_ptr` is nessacary as C cannot handle results.
|
||||
::ext_php_rs::php::types::object::ZendClassObject::<#name>::new_ptr(
|
||||
ce,
|
||||
#handlers.unwrap()
|
||||
)
|
||||
).expect("Failed to allocate memory for new Zend object.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,10 @@ pub enum Error {
|
||||
InvalidPointer,
|
||||
/// The given property name does not exist.
|
||||
InvalidProperty,
|
||||
/// The string could not be converted into a C-string due to the presence of a NUL character.
|
||||
InvalidCString,
|
||||
/// Could not call the given function.
|
||||
Callable,
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
@ -58,6 +62,11 @@ impl Display for Error {
|
||||
Error::InvalidScope => write!(f, "Invalid scope."),
|
||||
Error::InvalidPointer => write!(f, "Invalid pointer."),
|
||||
Error::InvalidProperty => write!(f, "Property does not exist on object."),
|
||||
Error::InvalidCString => write!(
|
||||
f,
|
||||
"String given contains NUL-bytes which cannot be present in a C string."
|
||||
),
|
||||
Error::Callable => write!(f, "Could not call given function."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Helper functions useful for interacting with PHP and Zend.
|
||||
|
||||
use crate::errors::{Error, Result};
|
||||
use std::ffi::CString;
|
||||
|
||||
/// Takes a Rust string object, converts it into a C string
|
||||
@ -14,7 +15,7 @@ use std::ffi::CString;
|
||||
/// use std::ffi::CString;
|
||||
/// use ext_php_rs::functions::c_str;
|
||||
///
|
||||
/// let mut ptr = c_str("Hello");
|
||||
/// let mut ptr = c_str("Hello").unwrap();
|
||||
///
|
||||
/// unsafe {
|
||||
/// assert_eq!(b'H', *ptr as u8);
|
||||
@ -28,9 +29,16 @@ use std::ffi::CString;
|
||||
/// let _ = CString::from_raw(ptr as *mut i8);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn c_str<S>(s: S) -> *const i8
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the given string contains a NUL byte, which for obvious reasons cannot be
|
||||
/// contained inside a C string.
|
||||
pub fn c_str<S>(s: S) -> Result<*const i8>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
CString::into_raw(CString::new(s.as_ref()).unwrap())
|
||||
Ok(CString::into_raw(
|
||||
CString::new(s.as_ref()).map_err(|_| Error::InvalidCString)?,
|
||||
))
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![deny(clippy::unwrap_used)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
@ -58,7 +58,7 @@ macro_rules! _info_table_row {
|
||||
#[macro_export]
|
||||
macro_rules! call_user_func {
|
||||
($fn: expr, $($param: expr),*) => {
|
||||
$fn.try_call(vec![$($param.into()),*])
|
||||
$fn.try_call(vec![$(&$param),*])
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,11 @@
|
||||
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use super::{enums::DataType, execution_data::ExecutionData, types::zval::Zval};
|
||||
use super::{
|
||||
enums::DataType,
|
||||
execution_data::ExecutionData,
|
||||
types::zval::{IntoZval, Zval},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
@ -97,20 +101,16 @@ impl<'a> Arg<'a> {
|
||||
|
||||
/// Attempts to call the argument as a callable with a list of arguments to pass to the function.
|
||||
/// Note that a thrown exception inside the callable is not detectable, therefore you should
|
||||
/// check if the return value is valid rather than unwrapping.
|
||||
/// check if the return value is valid rather than unwrapping. Returns a result containing the
|
||||
/// return value of the function, or an error.
|
||||
///
|
||||
/// You should not call this function directly, rather through the [`call_user_func`] macro.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `params` - A list of parameters to call the function with.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Some(Zval)` - The result of the function call.
|
||||
/// * `None` - The argument was empty, the argument was not callable or the call failed.
|
||||
pub fn try_call(&self, params: Vec<Zval>) -> Option<Zval> {
|
||||
self.zval()?.try_call(params)
|
||||
pub fn try_call(&self, params: Vec<&dyn IntoZval>) -> Result<Zval> {
|
||||
self.zval().ok_or(Error::Callable)?.try_call(params)
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,15 +139,15 @@ impl From<Arg<'_>> for _zend_expected_type {
|
||||
pub type ArgInfo = zend_internal_arg_info;
|
||||
|
||||
/// Parses the arguments of a function.
|
||||
pub struct ArgParser<'a, 'b> {
|
||||
args: Vec<&'a mut Arg<'b>>,
|
||||
pub struct ArgParser<'a, 'arg, 'zval> {
|
||||
args: Vec<&'arg mut Arg<'zval>>,
|
||||
min_num_args: Option<u32>,
|
||||
execute_data: *mut ExecutionData,
|
||||
execute_data: &'a mut ExecutionData,
|
||||
}
|
||||
|
||||
impl<'a, 'b> ArgParser<'a, 'b> {
|
||||
impl<'a, 'arg, 'zval> ArgParser<'a, 'arg, 'zval> {
|
||||
/// Builds a new function argument parser.
|
||||
pub fn new(execute_data: *mut ExecutionData) -> Self {
|
||||
pub fn new(execute_data: &'a mut ExecutionData) -> Self {
|
||||
ArgParser {
|
||||
args: vec![],
|
||||
min_num_args: None,
|
||||
@ -160,7 +160,7 @@ impl<'a, 'b> ArgParser<'a, 'b> {
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `arg` - The argument to add to the parser.
|
||||
pub fn arg(mut self, arg: &'a mut Arg<'b>) -> Self {
|
||||
pub fn arg(mut self, arg: &'arg mut Arg<'zval>) -> Self {
|
||||
self.args.push(arg);
|
||||
self
|
||||
}
|
||||
@ -172,21 +172,21 @@ impl<'a, 'b> ArgParser<'a, 'b> {
|
||||
}
|
||||
|
||||
/// Uses the argument parser to parse the arguments contained in the given
|
||||
/// `ExecutionData` object.
|
||||
/// `ExecutionData` object. Returns successfully if the arguments were parsed.
|
||||
///
|
||||
/// This function can only be safely called from within an exported PHP function.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `execute_data` - The execution data from the function.
|
||||
///
|
||||
/// # Returns
|
||||
/// # Errors
|
||||
///
|
||||
/// * `Ok(())` - The arguments were successfully parsed.
|
||||
/// * `Err(String)` - There were too many or too little arguments
|
||||
/// passed to the function. The user has already been notified so you
|
||||
/// can discard and return from the function if an `Err` is received.
|
||||
/// Returns an [`Error`] type if there were too many or too little arguments passed to the
|
||||
/// function. The user has already been notified so you should break execution after seeing an
|
||||
/// error type.
|
||||
pub fn parse(mut self) -> Result<()> {
|
||||
let execute_data = unsafe { self.execute_data.as_ref() }.unwrap();
|
||||
let num_args = unsafe { execute_data.This.u2.num_args };
|
||||
let num_args = unsafe { self.execute_data.This.u2.num_args };
|
||||
let max_num_args = self.args.len() as u32;
|
||||
let min_num_args = match self.min_num_args {
|
||||
Some(n) => n,
|
||||
@ -194,13 +194,14 @@ impl<'a, 'b> ArgParser<'a, 'b> {
|
||||
};
|
||||
|
||||
if num_args < min_num_args || num_args > max_num_args {
|
||||
// SAFETY: Exported C function is safe, return value is unused and parameters are copied.
|
||||
unsafe { zend_wrong_parameters_count_error(min_num_args, max_num_args) };
|
||||
|
||||
return Err(Error::IncorrectArguments(num_args, min_num_args));
|
||||
}
|
||||
|
||||
for (i, arg) in self.args.iter_mut().enumerate() {
|
||||
arg.zval = unsafe { execute_data.zend_call_arg(i) };
|
||||
arg.zval = unsafe { self.execute_data.zend_call_arg(i) };
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,11 +1,12 @@
|
||||
//! Builder and objects for creating classes in the PHP world.
|
||||
|
||||
use crate::errors::{Error, Result};
|
||||
use std::{mem, ptr};
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
ext_php_rs_zend_string_release, zend_class_entry, zend_declare_class_constant,
|
||||
zend_declare_property, zend_register_internal_class_ex,
|
||||
zend_class_entry, zend_declare_class_constant, zend_declare_property,
|
||||
zend_register_internal_class_ex,
|
||||
},
|
||||
functions::c_str,
|
||||
};
|
||||
@ -16,7 +17,7 @@ use super::{
|
||||
types::{
|
||||
object::{ZendObject, ZendObjectOverride},
|
||||
string::ZendString,
|
||||
zval::Zval,
|
||||
zval::{IntoZval, Zval},
|
||||
},
|
||||
};
|
||||
|
||||
@ -25,8 +26,9 @@ pub type ClassEntry = zend_class_entry;
|
||||
|
||||
/// Builds a class to be exported as a PHP class.
|
||||
pub struct ClassBuilder<'a> {
|
||||
name: String,
|
||||
ptr: &'a mut ClassEntry,
|
||||
extends: *mut ClassEntry,
|
||||
extends: Option<&'static ClassEntry>,
|
||||
methods: Vec<FunctionEntry>,
|
||||
object_override: Option<unsafe extern "C" fn(class_type: *mut ClassEntry) -> *mut ZendObject>,
|
||||
properties: Vec<(String, Zval, PropertyFlags)>,
|
||||
@ -40,20 +42,24 @@ impl<'a> ClassBuilder<'a> {
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `name` - The name of the class.
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub fn new<N>(name: N) -> Self
|
||||
where
|
||||
N: AsRef<str>,
|
||||
{
|
||||
// SAFETY: Allocating temporary class entry. Will return a null-ptr if allocation fails,
|
||||
// which will cause the program to panic (standard in Rust). Unwrapping is OK - the ptr
|
||||
// will either be valid or null.
|
||||
let ptr = unsafe {
|
||||
(libc::calloc(1, mem::size_of::<ClassEntry>()) as *mut ClassEntry)
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
};
|
||||
ptr.name = ZendString::new_interned(name).release();
|
||||
|
||||
Self {
|
||||
name: name.as_ref().to_string(),
|
||||
ptr,
|
||||
extends: ptr::null_mut(),
|
||||
extends: None,
|
||||
methods: vec![],
|
||||
object_override: None,
|
||||
properties: vec![],
|
||||
@ -67,7 +73,7 @@ impl<'a> ClassBuilder<'a> {
|
||||
///
|
||||
/// * `parent` - The parent class to extend.
|
||||
pub fn extends(mut self, parent: &'static ClassEntry) -> Self {
|
||||
self.extends = (parent as *const _) as *mut _;
|
||||
self.extends = Some(parent);
|
||||
self
|
||||
}
|
||||
|
||||
@ -86,6 +92,8 @@ impl<'a> ClassBuilder<'a> {
|
||||
/// Adds a property to the class. The initial type of the property is given by the type
|
||||
/// of the given default. Note that the user can change the type.
|
||||
///
|
||||
/// Returns a result containing the class builder if the property was successfully added.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `name` - The name of the property to add to the class.
|
||||
@ -94,40 +102,30 @@ impl<'a> ClassBuilder<'a> {
|
||||
pub fn property(
|
||||
mut self,
|
||||
name: impl AsRef<str>,
|
||||
default: impl Into<Zval>,
|
||||
default: impl IntoZval,
|
||||
flags: PropertyFlags,
|
||||
) -> Self {
|
||||
let mut default = default.into();
|
||||
|
||||
if default.is_string() {
|
||||
let val = default.string().unwrap();
|
||||
unsafe { ext_php_rs_zend_string_release(default.value.str_) };
|
||||
default.set_persistent_string(val);
|
||||
}
|
||||
) -> Result<Self> {
|
||||
let default = default.as_zval(true)?;
|
||||
|
||||
self.properties
|
||||
.push((name.as_ref().to_string(), default, flags));
|
||||
self
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Adds a constant to the class.
|
||||
/// The type of the constant is defined by the type of the given default.
|
||||
/// Adds a constant to the class. The type of the constant is defined by the type of the given
|
||||
/// default.
|
||||
///
|
||||
/// Returns a result containing the class builder if the constant was successfully added.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `name` - The name of the constant to add to the class.
|
||||
/// * `value` - The value of the constant.
|
||||
pub fn constant(mut self, name: impl AsRef<str>, value: impl Into<Zval>) -> Self {
|
||||
let mut value = value.into();
|
||||
|
||||
if value.is_string() {
|
||||
let val = value.string().unwrap();
|
||||
unsafe { ext_php_rs_zend_string_release(value.value.str_) };
|
||||
value.set_persistent_string(val);
|
||||
}
|
||||
pub fn constant(mut self, name: impl AsRef<str>, value: impl IntoZval) -> Result<Self> {
|
||||
let value = value.as_zval(true)?;
|
||||
|
||||
self.constants.push((name.as_ref().to_string(), value));
|
||||
self
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Sets the flags for the class.
|
||||
@ -160,21 +158,34 @@ impl<'a> ClassBuilder<'a> {
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the class could not be built.
|
||||
pub fn build(mut self) -> Option<&'static mut ClassEntry> {
|
||||
/// Returns an [`Error`] variant if the class could not be registered.
|
||||
pub fn build(mut self) -> Result<&'static mut ClassEntry> {
|
||||
self.ptr.name = ZendString::new_interned(self.name)?.release();
|
||||
|
||||
self.methods.push(FunctionEntry::end());
|
||||
let func = Box::into_raw(self.methods.into_boxed_slice()) as *const FunctionEntry;
|
||||
self.ptr.info.internal.builtin_functions = func;
|
||||
|
||||
let class = unsafe { zend_register_internal_class_ex(self.ptr, self.extends).as_mut()? };
|
||||
let class = unsafe {
|
||||
zend_register_internal_class_ex(
|
||||
self.ptr,
|
||||
match self.extends {
|
||||
Some(ptr) => (ptr as *const _) as *mut _,
|
||||
None => ptr::null_mut(),
|
||||
},
|
||||
)
|
||||
.as_mut()
|
||||
.ok_or(Error::InvalidPointer)?
|
||||
};
|
||||
|
||||
// SAFETY: We allocated memory for this pointer in `new`, so it is our job to free it when the builder has finished.
|
||||
unsafe { libc::free((self.ptr as *mut ClassEntry) as *mut libc::c_void) };
|
||||
|
||||
for (name, mut default, flags) in self.properties {
|
||||
unsafe {
|
||||
zend_declare_property(
|
||||
class,
|
||||
c_str(&name),
|
||||
c_str(&name)?,
|
||||
name.len() as _,
|
||||
&mut default,
|
||||
flags.bits() as _,
|
||||
@ -184,13 +195,13 @@ impl<'a> ClassBuilder<'a> {
|
||||
|
||||
for (name, value) in self.constants {
|
||||
let value = Box::into_raw(Box::new(value));
|
||||
unsafe { zend_declare_class_constant(class, c_str(&name), name.len() as u64, value) };
|
||||
unsafe { zend_declare_class_constant(class, c_str(&name)?, name.len() as u64, value) };
|
||||
}
|
||||
|
||||
if let Some(object_override) = self.object_override {
|
||||
class.__bindgen_anon_2.create_object = Some(object_override);
|
||||
}
|
||||
|
||||
Some(class)
|
||||
Ok(class)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::flags::GlobalConstantFlags;
|
||||
use crate::errors::Result;
|
||||
use crate::{
|
||||
bindings::{
|
||||
zend_register_bool_constant, zend_register_double_constant, zend_register_long_constant,
|
||||
@ -14,6 +15,8 @@ pub trait IntoConst: Sized {
|
||||
/// module number. By default, the case-insensitive and persistent flags are set when
|
||||
/// registering the constant.
|
||||
///
|
||||
/// Returns a result containing nothing if the constant was successfully registered.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `name` - The name of the constant.
|
||||
@ -30,7 +33,7 @@ pub trait IntoConst: Sized {
|
||||
/// 0
|
||||
/// }
|
||||
/// ```
|
||||
fn register_constant<N: AsRef<str>>(&self, name: N, module_number: i32) {
|
||||
fn register_constant<N: AsRef<str>>(&self, name: N, module_number: i32) -> Result<()> {
|
||||
self.register_constant_flags(
|
||||
name,
|
||||
module_number,
|
||||
@ -45,6 +48,8 @@ pub trait IntoConst: Sized {
|
||||
/// Note that the case-sensitive and persistent flags *are not* set when you use this function,
|
||||
/// you must set these yourself.
|
||||
///
|
||||
/// Returns a result containing nothing if the constant was successfully registered.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `name` - The name of the constant.
|
||||
@ -66,7 +71,7 @@ pub trait IntoConst: Sized {
|
||||
name: N,
|
||||
module_number: i32,
|
||||
flags: GlobalConstantFlags,
|
||||
);
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
impl IntoConst for String {
|
||||
@ -75,9 +80,9 @@ impl IntoConst for String {
|
||||
name: N,
|
||||
module_number: i32,
|
||||
flags: GlobalConstantFlags,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
self.as_str()
|
||||
.register_constant_flags(name, module_number, flags);
|
||||
.register_constant_flags(name, module_number, flags)
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,17 +92,18 @@ impl IntoConst for &str {
|
||||
name: N,
|
||||
module_number: i32,
|
||||
flags: GlobalConstantFlags,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let name = name.as_ref();
|
||||
unsafe {
|
||||
zend_register_string_constant(
|
||||
c_str(name),
|
||||
c_str(name)?,
|
||||
name.len() as _,
|
||||
c_str(self),
|
||||
c_str(self)?,
|
||||
flags.bits() as _,
|
||||
module_number,
|
||||
)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,17 +113,18 @@ impl IntoConst for bool {
|
||||
name: N,
|
||||
module_number: i32,
|
||||
flags: GlobalConstantFlags,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let name = name.as_ref();
|
||||
unsafe {
|
||||
zend_register_bool_constant(
|
||||
c_str(name),
|
||||
c_str(name)?,
|
||||
name.len() as _,
|
||||
*self,
|
||||
flags.bits() as _,
|
||||
module_number,
|
||||
)
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,17 +137,17 @@ macro_rules! into_const_num {
|
||||
name: N,
|
||||
module_number: i32,
|
||||
flags: GlobalConstantFlags,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let name = name.as_ref();
|
||||
unsafe {
|
||||
Ok(unsafe {
|
||||
$fn(
|
||||
c_str(name),
|
||||
c_str(name)?,
|
||||
name.len() as _,
|
||||
*self as _,
|
||||
flags.bits() as _,
|
||||
module_number,
|
||||
)
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -8,12 +8,15 @@ use crate::{
|
||||
zend_ce_parse_error, zend_ce_throwable, zend_ce_type_error, zend_ce_unhandled_match_error,
|
||||
zend_ce_value_error, zend_throw_exception_ex,
|
||||
},
|
||||
errors::Result,
|
||||
functions::c_str,
|
||||
};
|
||||
|
||||
/// Throws an exception with a given message. See [`ClassEntry`] for some built-in exception
|
||||
/// types.
|
||||
///
|
||||
/// Returns a result containing nothing if the exception was successfully thown.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `ex` - The exception type to throw.
|
||||
@ -26,13 +29,15 @@ use crate::{
|
||||
///
|
||||
/// throw(ClassEntry::compile_error(), "This is a CompileError.");
|
||||
/// ```
|
||||
pub fn throw(ex: &ClassEntry, message: &str) {
|
||||
throw_with_code(ex, 0, message);
|
||||
pub fn throw(ex: &ClassEntry, message: &str) -> Result<()> {
|
||||
throw_with_code(ex, 0, message)
|
||||
}
|
||||
|
||||
/// Throws an exception with a given message and status code. See [`ClassEntry`] for some built-in
|
||||
/// exception types.
|
||||
///
|
||||
/// Returns a result containing nothing if the exception was successfully thown.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `ex` - The exception type to throw.
|
||||
@ -46,19 +51,23 @@ pub fn throw(ex: &ClassEntry, message: &str) {
|
||||
///
|
||||
/// throw_with_code(ClassEntry::compile_error(), 123, "This is a CompileError.");
|
||||
/// ```
|
||||
pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) {
|
||||
pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> {
|
||||
// SAFETY: We are given a reference to a `ClassEntry` therefore when we cast it to a pointer it
|
||||
// will be valid.
|
||||
unsafe {
|
||||
zend_throw_exception_ex(
|
||||
(ex as *const _) as *mut _,
|
||||
code as _,
|
||||
c_str("%s"),
|
||||
c_str(message),
|
||||
c_str("%s")?,
|
||||
c_str(message)?,
|
||||
)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// SAFETY: All default exceptions have been initialized by the time our extension has been loaded.
|
||||
// Due to this, we can safely unwrap the results.
|
||||
#[allow(clippy::unwrap_used)]
|
||||
impl ClassEntry {
|
||||
/// Returns the base `Throwable` class.
|
||||
pub fn throwable() -> &'static Self {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use std::{mem, os::raw::c_char, ptr};
|
||||
|
||||
use crate::errors::Result;
|
||||
use crate::{bindings::zend_function_entry, functions::c_str};
|
||||
|
||||
use super::{
|
||||
@ -42,6 +43,7 @@ type FunctionPointerHandler = extern "C" fn(execute_data: *mut ExecutionData, re
|
||||
/// Builds a function to be exported as a PHP function.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunctionBuilder<'a> {
|
||||
name: String,
|
||||
function: FunctionEntry,
|
||||
args: Vec<Arg<'a>>,
|
||||
n_req: Option<usize>,
|
||||
@ -63,8 +65,9 @@ impl<'a> FunctionBuilder<'a> {
|
||||
N: AsRef<str>,
|
||||
{
|
||||
Self {
|
||||
name: name.as_ref().to_string(),
|
||||
function: FunctionEntry {
|
||||
fname: c_str(name),
|
||||
fname: ptr::null(),
|
||||
handler: Some(unsafe {
|
||||
mem::transmute::<FunctionHandler, FunctionPointerHandler>(handler)
|
||||
}),
|
||||
@ -121,7 +124,9 @@ impl<'a> FunctionBuilder<'a> {
|
||||
}
|
||||
|
||||
/// Builds the function converting it into a Zend function entry.
|
||||
pub fn build(mut self) -> FunctionEntry {
|
||||
///
|
||||
/// Returns a result containing the function entry if successful.
|
||||
pub fn build(mut self) -> Result<FunctionEntry> {
|
||||
let mut args = Vec::with_capacity(self.args.len() + 1);
|
||||
|
||||
// argument header, retval etc
|
||||
@ -142,17 +147,19 @@ impl<'a> FunctionBuilder<'a> {
|
||||
// arguments
|
||||
for arg in self.args.iter() {
|
||||
args.push(ArgInfo {
|
||||
name: c_str(arg.name.clone()),
|
||||
name: c_str(&arg.name)?,
|
||||
type_: ZendType::empty_from_type(arg._type, arg.as_ref, false, arg.allow_null),
|
||||
default_value: match &arg.default_value {
|
||||
Some(val) => c_str(val),
|
||||
Some(val) => c_str(val)?,
|
||||
None => ptr::null(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
self.function.fname = c_str(self.name)?;
|
||||
self.function.num_args = (args.len() - 1) as u32;
|
||||
self.function.arg_info = Box::into_raw(args.into_boxed_slice()) as *const ArgInfo;
|
||||
self.function
|
||||
|
||||
Ok(self.function)
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
bindings::{
|
||||
ext_php_rs_php_build_id, zend_module_entry, USING_ZTS, ZEND_DEBUG, ZEND_MODULE_API_NO,
|
||||
},
|
||||
errors::Result,
|
||||
functions::c_str,
|
||||
};
|
||||
|
||||
@ -44,6 +45,8 @@ pub type InfoFunc = extern "C" fn(zend_module: *mut ModuleEntry);
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleBuilder {
|
||||
name: String,
|
||||
version: String,
|
||||
module: ModuleEntry,
|
||||
functions: Vec<FunctionEntry>,
|
||||
}
|
||||
@ -61,6 +64,8 @@ impl ModuleBuilder {
|
||||
V: AsRef<str>,
|
||||
{
|
||||
Self {
|
||||
name: name.as_ref().to_string(),
|
||||
version: version.as_ref().to_string(),
|
||||
module: ModuleEntry {
|
||||
size: mem::size_of::<ModuleEntry>() as u16,
|
||||
zend_api: ZEND_MODULE_API_NO,
|
||||
@ -68,14 +73,14 @@ impl ModuleBuilder {
|
||||
zts: USING_ZTS as u8,
|
||||
ini_entry: ptr::null(),
|
||||
deps: ptr::null(),
|
||||
name: c_str(name),
|
||||
name: ptr::null(),
|
||||
functions: ptr::null(),
|
||||
module_startup_func: None,
|
||||
module_shutdown_func: None,
|
||||
request_startup_func: None,
|
||||
request_shutdown_func: None,
|
||||
info_func: None,
|
||||
version: c_str(version),
|
||||
version: ptr::null(),
|
||||
globals_size: 0,
|
||||
#[cfg(not(feature = "zts"))]
|
||||
globals_ptr: ptr::null::<c_void>() as *mut c_void,
|
||||
@ -155,12 +160,16 @@ impl ModuleBuilder {
|
||||
}
|
||||
|
||||
/// Builds the extension and returns a `ModuleEntry`.
|
||||
pub fn build(mut self) -> ModuleEntry {
|
||||
// TODO: move to seperate function
|
||||
///
|
||||
/// Returns a result containing the module entry if successful.
|
||||
pub fn build(mut self) -> Result<ModuleEntry> {
|
||||
self.functions.push(FunctionEntry::end());
|
||||
self.module.functions =
|
||||
Box::into_raw(self.functions.into_boxed_slice()) as *const FunctionEntry;
|
||||
self.module
|
||||
self.module.name = c_str(self.name)?;
|
||||
self.module.version = c_str(self.version)?;
|
||||
|
||||
Ok(self.module)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,19 @@ use crate::{
|
||||
functions::c_str,
|
||||
};
|
||||
|
||||
use super::{string::ZendString, zval::Zval};
|
||||
use super::{
|
||||
string::ZendString,
|
||||
zval::{IntoZval, Zval},
|
||||
};
|
||||
|
||||
/// Result type returned after attempting to insert an element into a hash table.
|
||||
#[derive(Debug)]
|
||||
pub enum HashTableInsertResult<'a> {
|
||||
/// The element was inserted into the hash table successfully.
|
||||
Ok,
|
||||
/// The element was inserted into the hash table successfully, over-writing an existing element.
|
||||
OkWithOverwrite(&'a Zval),
|
||||
}
|
||||
|
||||
/// A PHP array, which internally is a hash table.
|
||||
pub struct ZendHashTable {
|
||||
@ -97,7 +109,7 @@ impl ZendHashTable {
|
||||
{
|
||||
let _key = key.into();
|
||||
let len = _key.len();
|
||||
unsafe { zend_hash_str_find(self.ptr, c_str(_key), len as u64).as_ref() }
|
||||
unsafe { zend_hash_str_find(self.ptr, c_str(_key).ok()?, len as u64).as_ref() }
|
||||
}
|
||||
|
||||
/// Attempts to retrieve a value from the hash table with an index.
|
||||
@ -122,15 +134,15 @@ impl ZendHashTable {
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Ok(())` - Key was successfully removed.
|
||||
/// * `Err(())` - No key was removed, did not exist.
|
||||
/// * `Some(())` - Key was successfully removed.
|
||||
/// * `None` - No key was removed, did not exist.
|
||||
pub fn remove<K>(&self, key: K) -> Option<()>
|
||||
where
|
||||
K: Into<String>,
|
||||
{
|
||||
let _key = key.into();
|
||||
let len = _key.len();
|
||||
let result = unsafe { zend_hash_str_del(self.ptr, c_str(_key), len as u64) };
|
||||
let result = unsafe { zend_hash_str_del(self.ptr, c_str(_key).ok()?, len as u64) };
|
||||
|
||||
if result < 0 {
|
||||
None
|
||||
@ -160,29 +172,26 @@ impl ZendHashTable {
|
||||
}
|
||||
|
||||
/// Attempts to insert an item into the hash table, or update if the key already exists.
|
||||
/// Returns a result containing a [`HashTableInsertResult`], which will indicate a successful
|
||||
/// insert, with the insert result variants either containing the overwritten value or nothing.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `key` - The key to insert the value at in the hash table.
|
||||
/// * `value` - The value to insert into the hash table.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Some(Zval)` - The existing value in the hash table that was overriden.
|
||||
/// * `None` - The element was inserted.
|
||||
pub fn insert<K, V>(&mut self, key: K, val: V) -> Option<&Zval>
|
||||
pub fn insert<K, V>(&mut self, key: K, val: V) -> Result<HashTableInsertResult>
|
||||
where
|
||||
K: Into<String>,
|
||||
V: Into<Zval>,
|
||||
V: IntoZval,
|
||||
{
|
||||
let key: String = key.into();
|
||||
let len = key.len();
|
||||
let val: Zval = val.into();
|
||||
let val = val.as_zval(false)?;
|
||||
|
||||
let existing_ptr = unsafe {
|
||||
zend_hash_str_update(
|
||||
self.ptr,
|
||||
c_str(key),
|
||||
c_str(key)?,
|
||||
len as u64,
|
||||
Box::into_raw(Box::new(val)), // Do we really want to allocate the value on the heap?
|
||||
// I read somewhere that zvals are't usually (or never) allocated on the heap.
|
||||
@ -191,7 +200,13 @@ impl ZendHashTable {
|
||||
|
||||
// Should we be claiming this Zval into rust?
|
||||
// I'm not sure if the PHP GC will collect this.
|
||||
unsafe { existing_ptr.as_ref() }
|
||||
|
||||
// SAFETY: The `zend_hash_str_update` function will either return a valid pointer or a null pointer.
|
||||
// In the latter case, `as_ref()` will return `None`.
|
||||
Ok(match unsafe { existing_ptr.as_ref() } {
|
||||
Some(ptr) => HashTableInsertResult::OkWithOverwrite(ptr),
|
||||
None => HashTableInsertResult::Ok,
|
||||
})
|
||||
}
|
||||
|
||||
/// Inserts an item into the hash table at a specified index,
|
||||
@ -206,31 +221,37 @@ impl ZendHashTable {
|
||||
///
|
||||
/// * `Some(&Zval)` - The existing value in the hash table that was overriden.
|
||||
/// * `None` - The element was inserted.
|
||||
pub fn insert_at_index<V>(&mut self, key: u64, val: V) -> Option<&Zval>
|
||||
pub fn insert_at_index<V>(&mut self, key: u64, val: V) -> Result<HashTableInsertResult>
|
||||
where
|
||||
V: Into<Zval>,
|
||||
V: IntoZval,
|
||||
{
|
||||
let val: Zval = val.into();
|
||||
let val = val.as_zval(false)?;
|
||||
|
||||
let existing_ptr =
|
||||
unsafe { zend_hash_index_update(self.ptr, key, Box::into_raw(Box::new(val))) };
|
||||
|
||||
// See `insert` function comment.
|
||||
unsafe { existing_ptr.as_ref() }
|
||||
// SAFETY: The `zend_hash_str_update` function will either return a valid pointer or a null pointer.
|
||||
// In the latter case, `as_ref()` will return `None`.
|
||||
Ok(match unsafe { existing_ptr.as_ref() } {
|
||||
Some(ptr) => HashTableInsertResult::OkWithOverwrite(ptr),
|
||||
None => HashTableInsertResult::Ok,
|
||||
})
|
||||
}
|
||||
|
||||
/// Pushes an item onto the end of the hash table.
|
||||
/// Pushes an item onto the end of the hash table. Returns a result containing nothing if the
|
||||
/// element was sucessfully inserted.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `val` - The value to insert into the hash table.
|
||||
pub fn push<V>(&mut self, val: V)
|
||||
pub fn push<V>(&mut self, val: V) -> Result<()>
|
||||
where
|
||||
V: Into<Zval>,
|
||||
V: IntoZval,
|
||||
{
|
||||
let val: Zval = val.into();
|
||||
let val = val.as_zval(false)?;
|
||||
|
||||
unsafe { zend_hash_next_index_insert(self.ptr, Box::into_raw(Box::new(val))) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns an iterator over the hash table.
|
||||
@ -343,19 +364,21 @@ impl<'a> From<&'a ZendHashTable> for HashMap<String, &'a Zval> {
|
||||
}
|
||||
|
||||
/// Implementation converting a Rust HashTable into a ZendHashTable.
|
||||
impl<'a, K, V> From<&'a HashMap<K, V>> for ZendHashTable
|
||||
impl<'a, K, V> TryFrom<&'a HashMap<K, V>> for ZendHashTable
|
||||
where
|
||||
K: Into<String> + Copy,
|
||||
V: Into<Zval> + Copy,
|
||||
V: IntoZval + Copy,
|
||||
{
|
||||
fn from(hm: &'a HashMap<K, V>) -> Self {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(hm: &'a HashMap<K, V>) -> Result<Self> {
|
||||
let mut ht = ZendHashTable::with_capacity(hm.len() as u32);
|
||||
|
||||
for (k, v) in hm.iter() {
|
||||
ht.insert(*k, *v);
|
||||
ht.insert(*k, *v)?;
|
||||
}
|
||||
|
||||
ht
|
||||
Ok(ht)
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,17 +397,19 @@ where
|
||||
}
|
||||
|
||||
/// Implementation for converting a Rust Vec into a ZendHashTable.
|
||||
impl<'a, V> From<Vec<V>> for ZendHashTable
|
||||
impl<'a, V> TryFrom<Vec<V>> for ZendHashTable
|
||||
where
|
||||
V: Into<Zval>,
|
||||
V: IntoZval,
|
||||
{
|
||||
fn from(vec: Vec<V>) -> Self {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(vec: Vec<V>) -> Result<Self> {
|
||||
let mut ht = ZendHashTable::with_capacity(vec.len() as u32);
|
||||
|
||||
for v in vec {
|
||||
ht.push(v);
|
||||
ht.push(v)?;
|
||||
}
|
||||
|
||||
ht
|
||||
Ok(ht)
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,10 @@ use crate::{
|
||||
php::{class::ClassEntry, execution_data::ExecutionData, types::string::ZendString},
|
||||
};
|
||||
|
||||
use super::{array::ZendHashTable, zval::Zval};
|
||||
use super::{
|
||||
array::ZendHashTable,
|
||||
zval::{IntoZval, Zval},
|
||||
};
|
||||
|
||||
pub type ZendObject = zend_object;
|
||||
pub type ZendObjectHandlers = zend_object_handlers;
|
||||
@ -63,7 +66,7 @@ impl ZendObject {
|
||||
return Err(Error::InvalidProperty);
|
||||
}
|
||||
|
||||
let name = ZendString::new(name, false);
|
||||
let name = ZendString::new(name, false)?;
|
||||
let mut rv = Zval::new();
|
||||
|
||||
unsafe {
|
||||
@ -86,9 +89,9 @@ impl ZendObject {
|
||||
///
|
||||
/// * `name` - The name of the property.
|
||||
/// * `value` - The value to set the property to.
|
||||
pub fn set_property(&mut self, name: impl AsRef<str>, value: impl Into<Zval>) -> Result<&Zval> {
|
||||
let name = ZendString::new(name, false);
|
||||
let mut value = value.into();
|
||||
pub fn set_property(&mut self, name: impl AsRef<str>, value: impl IntoZval) -> Result<&Zval> {
|
||||
let name = ZendString::new(name, false)?;
|
||||
let mut value = value.as_zval(false)?;
|
||||
|
||||
if value.is_string() {
|
||||
value.set_refcount(0);
|
||||
@ -115,7 +118,7 @@ impl ZendObject {
|
||||
/// * `name` - The name of the property.
|
||||
/// * `query` - The 'query' to classify if a property exists.
|
||||
pub fn has_property(&self, name: impl AsRef<str>, query: PropertyQuery) -> Result<bool> {
|
||||
let name = ZendString::new(name.as_ref(), false);
|
||||
let name = ZendString::new(name.as_ref(), false)?;
|
||||
|
||||
Ok(unsafe {
|
||||
self.handlers()?.has_property.ok_or(Error::InvalidScope)?(
|
||||
@ -215,12 +218,12 @@ impl<T: Default> ZendClassObject<T> {
|
||||
pub unsafe fn new_ptr(
|
||||
ce: *mut ClassEntry,
|
||||
handlers: *mut ZendObjectHandlers,
|
||||
) -> *mut zend_object {
|
||||
) -> Result<*mut zend_object> {
|
||||
let obj = {
|
||||
let obj = (ext_php_rs_zend_object_alloc(std::mem::size_of::<Self>() as _, ce)
|
||||
as *mut Self)
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
.ok_or(Error::InvalidPointer)?;
|
||||
|
||||
zend_object_std_init(&mut obj.std, ce);
|
||||
object_properties_init(&mut obj.std, ce);
|
||||
@ -229,7 +232,7 @@ impl<T: Default> ZendClassObject<T> {
|
||||
|
||||
obj.obj = T::default();
|
||||
obj.std.handlers = handlers;
|
||||
&mut obj.std
|
||||
Ok(&mut obj.std)
|
||||
}
|
||||
|
||||
/// Attempts to retrieve the Zend class object container from the
|
||||
|
@ -24,33 +24,36 @@ pub struct ZendString {
|
||||
}
|
||||
|
||||
impl ZendString {
|
||||
/// Creates a new Zend string.
|
||||
/// Creates a new Zend string. Returns a result containin the string.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `str_` - The string to create a Zend string from.
|
||||
/// * `persistent` - Whether the request should relive the request boundary.
|
||||
pub fn new(str_: impl AsRef<str>, persistent: bool) -> Self {
|
||||
pub fn new(str_: impl AsRef<str>, persistent: bool) -> Result<Self> {
|
||||
let str_ = str_.as_ref();
|
||||
|
||||
Self {
|
||||
ptr: unsafe { ext_php_rs_zend_string_init(c_str(str_), str_.len() as _, persistent) },
|
||||
Ok(Self {
|
||||
ptr: unsafe { ext_php_rs_zend_string_init(c_str(str_)?, str_.len() as _, persistent) },
|
||||
free: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new interned Zend string.
|
||||
/// Creates a new interned Zend string. Returns a result containing the interned string.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `str_` - The string to create a Zend string from.
|
||||
pub fn new_interned(str_: impl AsRef<str>) -> Self {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub fn new_interned(str_: impl AsRef<str>) -> Result<Self> {
|
||||
let str_ = str_.as_ref();
|
||||
|
||||
Self {
|
||||
ptr: unsafe { zend_string_init_interned.unwrap()(c_str(str_), str_.len() as _, true) },
|
||||
// Unwrap is OK here - `zend_string_init_interned` will be a valid function ptr by the time
|
||||
// our extension is loaded.
|
||||
Ok(Self {
|
||||
ptr: unsafe { zend_string_init_interned.unwrap()(c_str(str_)?, str_.len() as _, true) },
|
||||
free: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new [`ZendString`] wrapper from a raw pointer to a [`zend_string`].
|
||||
|
@ -147,26 +147,26 @@ impl<'a> Zval {
|
||||
|
||||
/// Attempts to call the argument as a callable with a list of arguments to pass to the function.
|
||||
/// Note that a thrown exception inside the callable is not detectable, therefore you should
|
||||
/// check if the return value is valid rather than unwrapping.
|
||||
/// check if the return value is valid rather than unwrapping. Returns a result containing the
|
||||
/// return value of the function, or an error.
|
||||
///
|
||||
/// You should not call this function directly, rather through the [`call_user_func`] macro.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `params` - A list of parameters to call the function with.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Some(Zval)` - The result of the function call.
|
||||
/// * `None` - The zval was not callable or the call failed.
|
||||
pub fn try_call(&self, params: Vec<Zval>) -> Option<Zval> {
|
||||
pub fn try_call(&self, params: Vec<&dyn IntoZval>) -> Result<Zval> {
|
||||
let mut retval = Zval::new();
|
||||
let len = params.len();
|
||||
let params = params
|
||||
.into_iter()
|
||||
.map(|val| val.as_zval(false))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let packed = Box::into_raw(params.into_boxed_slice()) as *mut Self;
|
||||
let ptr: *const Self = self;
|
||||
|
||||
if !self.is_callable() {
|
||||
return None;
|
||||
return Err(Error::Callable);
|
||||
}
|
||||
|
||||
let result = unsafe {
|
||||
@ -194,9 +194,9 @@ impl<'a> Zval {
|
||||
};
|
||||
|
||||
if result < 0 {
|
||||
None
|
||||
Err(Error::Callable)
|
||||
} else {
|
||||
Some(retval)
|
||||
Ok(retval)
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,18 +266,20 @@ impl<'a> Zval {
|
||||
unsafe { zend_is_callable(ptr as *mut Self, 0, std::ptr::null_mut()) }
|
||||
}
|
||||
|
||||
/// Sets the value of the zval as a string.
|
||||
/// Sets the value of the zval as a string. Returns nothing in a result when successful.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `val` - The value to set the zval as.
|
||||
pub fn set_string<S>(&mut self, val: S)
|
||||
/// * `persistent` - Whether the string should persist between requests.
|
||||
pub fn set_string<S>(&mut self, val: S, persistent: bool) -> Result<()>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let zend_str = ZendString::new(val, false);
|
||||
let zend_str = ZendString::new(val, persistent)?;
|
||||
self.value.str_ = zend_str.release();
|
||||
self.u1.type_info = ZvalTypeFlags::StringEx.bits();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the value of the zval as a binary string.
|
||||
@ -291,34 +293,19 @@ impl<'a> Zval {
|
||||
self.u1.type_info = ZvalTypeFlags::StringEx.bits();
|
||||
}
|
||||
|
||||
/// Sets the value of the zval as a persistent string.
|
||||
/// This means that the zend string will persist between
|
||||
/// request lifetime.
|
||||
/// Sets the value of the zval as a interned string. Returns nothing in a result when successful.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `val` - The value to set the zval as.
|
||||
pub fn set_persistent_string<S>(&mut self, val: S)
|
||||
pub fn set_interned_string<S>(&mut self, val: S) -> Result<()>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let zend_str = ZendString::new(val, true);
|
||||
self.value.str_ = zend_str.release();
|
||||
self.u1.type_info = ZvalTypeFlags::StringEx.bits();
|
||||
}
|
||||
|
||||
/// Sets the value of the zval as a interned string.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `val` - The value to set the zval as.
|
||||
pub fn set_interned_string<S>(&mut self, val: S)
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let zend_str = ZendString::new_interned(val);
|
||||
let zend_str = ZendString::new_interned(val)?;
|
||||
self.value.str_ = zend_str.release();
|
||||
self.u1.type_info = ZvalTypeFlags::InternedStringEx.bits();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the value of the zval as a long.
|
||||
@ -355,6 +342,7 @@ impl<'a> Zval {
|
||||
}
|
||||
|
||||
/// Sets the value of the zval as null.
|
||||
///
|
||||
/// This is the default of a zval.
|
||||
pub fn set_null(&mut self) {
|
||||
self.u1.type_info = ZvalTypeFlags::Null.bits();
|
||||
@ -386,12 +374,9 @@ impl<'a> Zval {
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `val` - The value to set the zval as.
|
||||
pub fn set_array<V>(&mut self, val: V)
|
||||
where
|
||||
V: Into<ZendHashTable>,
|
||||
{
|
||||
pub fn set_array(&mut self, val: ZendHashTable) {
|
||||
self.u1.type_info = ZvalTypeFlags::ArrayEx.bits();
|
||||
self.value.arr = val.into().into_ptr();
|
||||
self.value.arr = val.into_ptr();
|
||||
}
|
||||
|
||||
/// Sets the reference count of the Zval.
|
||||
@ -435,10 +420,24 @@ impl Debug for Zval {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides implementations for converting Rust primitive types into PHP zvals. Alternative to the
|
||||
/// built-in Rust [`From`] and [`TryFrom`] implementations, allowing the caller to specify whether
|
||||
/// the Zval contents will persist between requests.
|
||||
pub trait IntoZval {
|
||||
/// Converts a Rust primitive type into a Zval. Returns a result containing the Zval if
|
||||
/// successful.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `persistent` - Whether the contents of the Zval will persist between requests.
|
||||
fn as_zval(&self, persistent: bool) -> Result<Zval>;
|
||||
}
|
||||
|
||||
macro_rules! try_from_zval {
|
||||
($type: ty, $fn: ident) => {
|
||||
impl TryFrom<&Zval> for $type {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &Zval) -> Result<Self> {
|
||||
match value.$fn() {
|
||||
Some(v) => match <$type>::try_from(v) {
|
||||
@ -480,6 +479,14 @@ macro_rules! into_zval {
|
||||
zv
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoZval for $type {
|
||||
fn as_zval(&self, _: bool) -> Result<Zval> {
|
||||
let mut zv = Zval::new();
|
||||
zv.$fn(*self);
|
||||
Ok(zv)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -497,6 +504,28 @@ into_zval!(f64, set_double);
|
||||
|
||||
into_zval!(bool, set_bool);
|
||||
|
||||
into_zval!(String, set_string);
|
||||
into_zval!(&String, set_string);
|
||||
into_zval!(&str, set_string);
|
||||
macro_rules! try_into_zval_str {
|
||||
($type: ty) => {
|
||||
impl TryFrom<$type> for Zval {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: $type) -> Result<Self> {
|
||||
let mut zv = Self::new();
|
||||
zv.set_string(value, false)?;
|
||||
Ok(zv)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoZval for $type {
|
||||
fn as_zval(&self, persistent: bool) -> Result<Zval> {
|
||||
let mut zv = Zval::new();
|
||||
zv.set_string(self, persistent)?;
|
||||
Ok(zv)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try_into_zval_str!(String);
|
||||
try_into_zval_str!(&String);
|
||||
try_into_zval_str!(&str);
|
||||
|
Loading…
Reference in New Issue
Block a user