Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add zend_function wrapper, try_call_method zval/object methods #264

Merged
merged 35 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
cc50781
Add async support
danog May 16, 2023
10d3c50
Update
danog May 19, 2023
2088fad
A small hack to workaround known good lifetimes
danog May 21, 2023
3fb905a
Fix borrowing of parameters in async functions
danog Jun 19, 2023
07ce829
Fix option args
danog Jun 19, 2023
751d4f4
Refactoring
danog Jun 22, 2023
ee5c3e1
Cleanup
danog Jun 22, 2023
ab55a49
Cleanup
danog Jun 22, 2023
33f9d6e
Improvement
danog Jun 22, 2023
e0ae481
Cleanup
danog Jun 22, 2023
064ef74
Fixes
danog Jun 22, 2023
ae2bd55
Cleanup
danog Jun 22, 2023
c517931
Cleanup
danog Jun 22, 2023
6657223
Further cleanup
danog Jun 22, 2023
ca2bc9a
Performance trick
danog Jun 22, 2023
8514614
Remove redundant fclose
danog Jun 22, 2023
e658ce8
Small fix
danog Jun 29, 2023
eda544c
Simplify
danog Jun 29, 2023
ab2af45
Fix class construction
danog Jun 29, 2023
c8c1df0
Fix construction of native classes and any class with properties
danog Jun 29, 2023
4cd4a20
Cleanup
danog Aug 21, 2023
725dfec
Cleanup
danog Aug 27, 2023
253255b
Move async stuff to danog/php-tokio
danog Aug 27, 2023
f669891
Merge remote-tracking branch 'origin/master'
danog Aug 27, 2023
4b82171
Update docs
danog Aug 27, 2023
714b155
Fixes
danog Aug 27, 2023
c87c0dc
Fix
danog Aug 28, 2023
891fa4a
Cargo fmt
danog Sep 4, 2023
e1672be
Update bindings
danog Oct 10, 2023
e820b24
Fix
danog Oct 10, 2023
85836da
Fix
danog Oct 10, 2023
a4e3485
Update
danog Oct 10, 2023
0773d72
fmt
danog Oct 10, 2023
e1a5260
Fixup warnings on latest nightly
danog Oct 10, 2023
4f1565f
Fmt
danog Oct 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions allowed_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ bind! {
zend_array_destroy,
zend_array_dup,
zend_call_known_function,
zend_fetch_function_str,
zend_hash_str_find_ptr_lc,
zend_ce_argument_count_error,
zend_ce_arithmetic_error,
zend_ce_compile_error,
Expand Down
1 change: 1 addition & 0 deletions crates/cli/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use anyhow::{Context, Result};
use ext_php_rs::describe::Description;
use libloading::os::unix::{Library, Symbol};

#[allow(improper_ctypes_definitions)]
pub struct Ext {
// These need to be here to keep the libraries alive. The extension library needs to be alive
// to access the describe function. Missing here is the lifetime on `Symbol<'a, fn() ->
Expand Down
30 changes: 28 additions & 2 deletions docsrs_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ pub const CONST_CS: u32 = 0;
pub const CONST_PERSISTENT: u32 = 1;
pub const CONST_NO_FILE_CACHE: u32 = 2;
pub const CONST_DEPRECATED: u32 = 4;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct __sigset_t {
pub __val: [::std::os::raw::c_ulong; 16usize],
}
pub type zend_long = i64;
pub type zend_ulong = u64;
pub type zend_uchar = ::std::os::raw::c_uchar;
Expand Down Expand Up @@ -405,6 +410,13 @@ extern "C" {
extern "C" {
pub fn zend_array_destroy(ht: *mut HashTable);
}
extern "C" {
pub fn zend_hash_str_find_ptr_lc(
ht: *const HashTable,
str_: *const ::std::os::raw::c_char,
len: usize,
) -> *mut ::std::os::raw::c_void;
}
extern "C" {
pub fn gc_possible_root(ref_: *mut zend_refcounted);
}
Expand Down Expand Up @@ -1049,7 +1061,15 @@ pub struct _zend_execute_data {
pub run_time_cache: *mut *mut ::std::os::raw::c_void,
pub extra_named_params: *mut zend_array,
}
pub type sigjmp_buf = [::std::os::raw::c_int; 49usize];
pub type __jmp_buf = [::std::os::raw::c_long; 8usize];
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct __jmp_buf_tag {
pub __jmpbuf: __jmp_buf,
pub __mask_was_saved: ::std::os::raw::c_int,
pub __saved_mask: __sigset_t,
}
pub type jmp_buf = [__jmp_buf_tag; 1usize];
pub type zend_executor_globals = _zend_executor_globals;
extern "C" {
pub static mut executor_globals: zend_executor_globals;
Expand Down Expand Up @@ -1119,7 +1139,7 @@ pub struct _zend_executor_globals {
pub symtable_cache_ptr: *mut *mut zend_array,
pub symbol_table: zend_array,
pub included_files: HashTable,
pub bailout: *mut sigjmp_buf,
pub bailout: *mut jmp_buf,
pub error_reporting: ::std::os::raw::c_int,
pub exit_status: ::std::os::raw::c_int,
pub function_table: *mut HashTable,
Expand Down Expand Up @@ -1262,6 +1282,12 @@ pub struct _zend_vm_stack {
pub end: *mut zval,
pub prev: zend_vm_stack,
}
extern "C" {
pub fn zend_fetch_function_str(
name: *const ::std::os::raw::c_char,
len: usize,
) -> *mut zend_function;
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct _zend_function_entry {
Expand Down
1 change: 1 addition & 0 deletions guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- [Object](./types/object.md)
- [Class Object](./types/class_object.md)
- [Closure](./types/closure.md)
- [Functions & methods](./types/functions.md)
- [Macros](./macros/index.md)
- [Module](./macros/module.md)
- [Module Startup Function](./macros/module_startup.md)
Expand Down
30 changes: 30 additions & 0 deletions guide/src/types/functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Functions & methods

PHP functions and methods are represented by the `Function` struct.

You can use the `try_from_function` and `try_from_method` methods to obtain a Function struct corresponding to the passed function or static method name.
It's heavily recommended you reuse returned `Function` objects, to avoid the overhead of looking up the function/method name.

You may also use the infallible `from_function` and `from_method` variants, for example when working with native PHP functions/classes that are guaranteed to be always available.

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
use ext_php_rs::prelude::*;

use ext_php_rs::zend::Function;

#[php_function]
pub fn test_function() -> () {
let var_dump = Function::from_function("var_dump");
let _ = var_dump.try_call(vec![&"abc"]);
}

#[php_function]
pub fn test_method() -> () {
let f = Function::from_method("ClassName", "staticMethod");
let _ = f.try_call(vec![&"abc"]);
}

# fn main() {}
```
19 changes: 19 additions & 0 deletions guide/src/types/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,25 @@ object.

## Examples

### Calling a method

```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
use ext_php_rs::{prelude::*, types::ZendObject};

// Take an object reference and also return it.
#[php_function]
pub fn take_obj(obj: &mut ZendObject) -> () {
let _ = obj.try_call_method("hello", vec![&"arg1", &"arg2"]);
}
# #[php_module]
# pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
# module
# }
# fn main() {}
```

### Taking an object reference

```rust,no_run
Expand Down
1 change: 1 addition & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ impl<'a> Arg<'a> {
/// # Parameters
///
/// * `params` - A list of parameters to call the function with.
#[inline(always)]
pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
self.zval.as_ref().ok_or(Error::Callable)?.try_call(params)
}
Expand Down
4 changes: 3 additions & 1 deletion src/describe/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ impl<T> Deref for Vec<T> {

impl<T> Drop for Vec<T> {
fn drop(&mut self) {
unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(self.ptr, self.len)) };
unsafe {
let _ = Box::from_raw(std::ptr::slice_from_raw_parts_mut(self.ptr, self.len));
};
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/describe/stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl ToStub for Module {
// Inserts a value into the entries hashmap. Takes a key and an entry, creating
// the internal vector if it doesn't already exist.
let mut insert = |ns, entry| {
let bucket = entries.entry(ns).or_insert_with(StdVec::new);
let bucket = entries.entry(ns).or_default();
bucket.push(entry);
};

Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ pub enum Error {
InvalidUtf8,
/// Could not call the given function.
Callable,
/// An object was expected.
Object,
/// An invalid exception type was thrown.
InvalidException(ClassFlags),
/// Converting integer arguments resulted in an overflow.
Expand Down Expand Up @@ -89,6 +91,7 @@ impl Display for Error {
),
Error::InvalidUtf8 => write!(f, "Invalid Utf8 byte sequence."),
Error::Callable => write!(f, "Could not call given function."),
Error::Object => write!(f, "An object was expected."),
Error::InvalidException(flags) => {
write!(f, "Invalid exception type was thrown: {flags:?}")
}
Expand Down
2 changes: 1 addition & 1 deletion src/exception.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Types and functions used for throwing exceptions from Rust to PHP.

use std::ffi::CString;
use std::{ffi::CString, fmt::Debug};

use crate::{
class::RegisteredClass,
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub mod zend;
/// A module typically glob-imported containing the typically required macros
/// and imports.
pub mod prelude {

pub use crate::builders::ModuleBuilder;
#[cfg(any(docs, feature = "closure"))]
#[cfg_attr(docs, doc(cfg(feature = "closure")))]
Expand Down
57 changes: 55 additions & 2 deletions src/types/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,33 @@ impl ZendHashTable {
unsafe { zend_hash_str_find(self, str.as_ptr(), key.len() as _).as_ref() }
}

/// Attempts to retrieve a value from the hash table with a string key.
///
/// # Parameters
///
/// * `key` - The key to search for in the hash table.
///
/// # Returns
///
/// * `Some(&Zval)` - A reference to the zval at the position in the hash
/// table.
/// * `None` - No value at the given position was found.
///
/// # Example
///
/// ```no_run
/// use ext_php_rs::types::ZendHashTable;
///
/// let mut ht = ZendHashTable::new();
///
/// ht.insert("test", "hello world");
/// assert_eq!(ht.get("test").and_then(|zv| zv.str()), Some("hello world"));
/// ```
pub fn get_mut(&self, key: &'_ str) -> Option<&mut Zval> {
let str = CString::new(key).ok()?;
unsafe { zend_hash_str_find(self, str.as_ptr(), key.len() as _).as_mut() }
}

/// Attempts to retrieve a value from the hash table with an index.
///
/// # Parameters
Expand All @@ -213,6 +240,32 @@ impl ZendHashTable {
unsafe { zend_hash_index_find(self, key).as_ref() }
}

/// Attempts to retrieve a value from the hash table with an index.
///
/// # Parameters
///
/// * `key` - The key to search for in the hash table.
///
/// # Returns
///
/// * `Some(&Zval)` - A reference to the zval at the position in the hash
/// table.
/// * `None` - No value at the given position was found.
///
/// # Example
///
/// ```no_run
/// use ext_php_rs::types::ZendHashTable;
///
/// let mut ht = ZendHashTable::new();
///
/// ht.push(100);
/// assert_eq!(ht.get_index(0).and_then(|zv| zv.long()), Some(100));
/// ```
pub fn get_index_mut(&self, key: u64) -> Option<&mut Zval> {
unsafe { zend_hash_index_find(self, key).as_mut() }
}

/// Attempts to remove a value from the hash table with a string key.
///
/// # Parameters
Expand Down Expand Up @@ -715,7 +768,7 @@ impl<'a> FromZval<'a> for &'a ZendHashTable {
}

///////////////////////////////////////////
//// HashMap
/// HashMap
///////////////////////////////////////////

impl<'a, V> TryFrom<&'a ZendHashTable> for HashMap<String, V>
Expand Down Expand Up @@ -784,7 +837,7 @@ where
}

///////////////////////////////////////////
//// Vec
/// Vec
///////////////////////////////////////////

impl<'a, T> TryFrom<&'a ZendHashTable> for Vec<T>
Expand Down
1 change: 1 addition & 0 deletions src/types/callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl<'a> ZendCallable<'a> {
/// let result = strpos.try_call(vec![&"hello", &"e"]).unwrap();
/// assert_eq!(result.long(), Some(1));
/// ```
#[inline(always)]
pub fn try_call(&self, params: Vec<&dyn IntoZvalDyn>) -> Result<Zval> {
if !self.0.is_callable() {
return Err(Error::Callable);
Expand Down
1 change: 1 addition & 0 deletions src/types/class_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ impl<T: RegisteredClass> ZendClassObject<T> {
/// # Parameters
///
/// * `obj` - The zend object to get the [`ZendClassObject`] for.
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn from_zend_obj_mut(std: &mut zend_object) -> Option<&mut Self> {
Self::_from_zend_obj(std)
}
Expand Down
52 changes: 48 additions & 4 deletions src/types/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ use std::{convert::TryInto, fmt::Debug, ops::DerefMut};
use crate::{
boxed::{ZBox, ZBoxable},
class::RegisteredClass,
convert::{FromZendObject, FromZval, FromZvalMut, IntoZval},
convert::{FromZendObject, FromZval, FromZvalMut, IntoZval, IntoZvalDyn},
error::{Error, Result},
ffi::{
ext_php_rs_zend_object_release, zend_call_known_function, zend_object, zend_objects_new,
HashTable, ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, ZEND_PROPERTY_ISSET,
ext_php_rs_zend_object_release, object_properties_init, zend_call_known_function,
zend_function, zend_hash_str_find_ptr_lc, zend_object, zend_objects_new, HashTable,
ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, ZEND_PROPERTY_ISSET,
},
flags::DataType,
rc::PhpRc,
Expand Down Expand Up @@ -41,7 +42,18 @@ impl ZendObject {
// SAFETY: Using emalloc to allocate memory inside Zend arena. Casting `ce` to
// `*mut` is valid as the function will not mutate `ce`.
unsafe {
let ptr = zend_objects_new(ce as *const _ as *mut _);
let ptr = match ce.__bindgen_anon_2.create_object {
None => {
let ptr = zend_objects_new(ce as *const _ as *mut _);
if ptr.is_null() {
panic!("Failed to allocate memory for Zend object")
}
object_properties_init(ptr, ce as *const _ as *mut _);
ptr
}
Some(v) => v(ce as *const _ as *mut _),
};

ZBox::from_raw(
ptr.as_mut()
.expect("Failed to allocate memory for Zend object"),
Expand Down Expand Up @@ -121,6 +133,38 @@ impl ZendObject {
(self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _))
}

#[inline(always)]
pub fn try_call_method(&self, name: &str, params: Vec<&dyn IntoZvalDyn>) -> 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 = params.into_boxed_slice();

unsafe {
let res = zend_hash_str_find_ptr_lc(
&(*self.ce).function_table,
name.as_ptr() as *const i8,
name.len(),
) as *mut zend_function;
if res.is_null() {
return Err(Error::Callable);
}
zend_call_known_function(
res,
self as *const _ as *mut _,
self.ce,
&mut retval,
len as _,
packed.as_ptr() as *mut _,
std::ptr::null_mut(),
)
};

Ok(retval)
}
/// Attempts to read a property from the Object. Returns a result containing
/// the value of the property if it exists and can be read, and an
/// [`Error`] otherwise.
Expand Down
Loading