From d5e05f04a10bed4777ce3328ead79ad680aa1b9b Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Tue, 20 Dec 2022 14:19:51 +0000 Subject: [PATCH 001/100] Add support for throwing Exception objects Currently we only support throwing exception class entries, i.e. stateless exceptions. Ideally we also want to support throwing a ZVal which has a ClassEntry that extends the Exception PHP class. This can be used to throw stateful exceptions which is not uncommon. This PR is missing one piece: `throw_object` will currently `drop` the Zval when the function is completed, causing reference / null pointer errors. It seems `ZVal::Drop` doesn't actually free the zval currently, it just sets the type to NULL (which also breaks things.) Discussed briefly in https://discord.com/channels/115233111977099271/1025314959179120714 on how best to solve this, but I'm not totally clear still! Ideally I think we want `throw_object` to own the `zval` but not free it once the function returns. --- allowed_bindings.rs | 1 + src/exception.rs | 60 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index f417e95ba4..972e151edd 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -81,6 +81,7 @@ bind! { zend_string, zend_string_init_interned, zend_throw_exception_ex, + zend_throw_exception_object, zend_type, zend_value, zend_wrong_parameters_count_error, diff --git a/src/exception.rs b/src/exception.rs index d1504b46c5..e8a48fe02a 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -6,8 +6,9 @@ use crate::{ class::RegisteredClass, error::{Error, Result}, ffi::zend_throw_exception_ex, + ffi::zend_throw_exception_object, flags::ClassFlags, - zend::{ce, ClassEntry}, + zend::{ce, ClassEntry}, types::Zval, }; /// Result type with the error variant as a [`PhpException`]. @@ -25,6 +26,7 @@ pub struct PhpException { message: String, code: i32, ex: &'static ClassEntry, + object: Option, } impl PhpException { @@ -36,7 +38,7 @@ impl PhpException { /// * `code` - Integer code to go inside the exception. /// * `ex` - Exception type to throw. pub fn new(message: String, code: i32, ex: &'static ClassEntry) -> Self { - Self { message, code, ex } + Self { message, code, ex, object: None, } } /// Creates a new default exception instance, using the default PHP @@ -59,10 +61,27 @@ impl PhpException { Self::new(message, 0, T::get_metadata().ce()) } + /// Set the Zval object for the exception. + /// + /// Exceptions can be based of instantiated Zval objects when you are throwing a custom exception with + /// stateful properties. + /// + /// # Parameters + /// + /// * `object` - The Zval object. + pub fn set_object(&mut self, object: Option) { + self.object = object; + } + /// Throws the exception, returning nothing inside a result if successful /// and an error otherwise. pub fn throw(self) -> Result<()> { - throw_with_code(self.ex, self.code, &self.message) + match self.object { + Some(object) => { + throw_object(object) + }, + None => throw_with_code(self.ex, self.code, &self.message), + } } } @@ -146,3 +165,38 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> }; Ok(()) } + + +/// Throws an exception object. +/// +/// Returns a result containing nothing if the exception was successfully +/// thrown. +/// +/// # Parameters +/// +/// * `object` - The zval of type object +/// +/// # Examples +/// +/// ```no_run +/// use ext_php_rs::{zend::{ce, ClassEntry}, exception::throw_with_code}; +/// +/// #[php_class] +/// #[extends(ext_php_rs::zend::ce::exception())] +/// pub struct JsException { +/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)] +/// message: String, +/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)] +/// code: i32, +/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)] +/// file: String, +/// } +/// let error = JsException { message: "A JS error occurred.", code: 100, file: "index.js" }; +/// throw_object( error.into_val(true) ); +/// ``` +pub fn throw_object(zval: Zval) -> Result<()> { + unsafe { + zend_throw_exception_object((&zval as *const _) as *mut _); + }; + Ok(()) +} From 94fb7c78a1326cf2374669e52b8dafc13adda778 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Tue, 20 Dec 2022 16:00:45 +0000 Subject: [PATCH 002/100] Fix docs example --- src/exception.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/exception.rs b/src/exception.rs index e8a48fe02a..f167c8d24c 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -179,7 +179,9 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> /// # Examples /// /// ```no_run -/// use ext_php_rs::{zend::{ce, ClassEntry}, exception::throw_with_code}; +/// use ext_php_rs::prelude::*; +/// use ext_php_rs::exception::throw_object; +/// use crate::ext_php_rs::convert::IntoZval; /// /// #[php_class] /// #[extends(ext_php_rs::zend::ce::exception())] @@ -191,8 +193,14 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> /// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)] /// file: String, /// } -/// let error = JsException { message: "A JS error occurred.", code: 100, file: "index.js" }; -/// throw_object( error.into_val(true) ); +/// +/// #[php_module] +/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { +/// module +/// } +/// +/// let error = JsException { message: "A JS error occurred.".to_string(), code: 100, file: "index.js".to_string() }; +/// throw_object( error.into_zval(true).unwrap() ); /// ``` pub fn throw_object(zval: Zval) -> Result<()> { unsafe { From 8d6850afd07342fd9921f3ffab1e3dd8d3e3a54f Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Tue, 20 Dec 2022 16:04:41 +0000 Subject: [PATCH 003/100] Format --- src/exception.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/exception.rs b/src/exception.rs index f167c8d24c..e1f4c637f2 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -8,7 +8,8 @@ use crate::{ ffi::zend_throw_exception_ex, ffi::zend_throw_exception_object, flags::ClassFlags, - zend::{ce, ClassEntry}, types::Zval, + types::Zval, + zend::{ce, ClassEntry}, }; /// Result type with the error variant as a [`PhpException`]. @@ -38,7 +39,12 @@ impl PhpException { /// * `code` - Integer code to go inside the exception. /// * `ex` - Exception type to throw. pub fn new(message: String, code: i32, ex: &'static ClassEntry) -> Self { - Self { message, code, ex, object: None, } + Self { + message, + code, + ex, + object: None, + } } /// Creates a new default exception instance, using the default PHP @@ -77,9 +83,7 @@ impl PhpException { /// and an error otherwise. pub fn throw(self) -> Result<()> { match self.object { - Some(object) => { - throw_object(object) - }, + Some(object) => throw_object(object), None => throw_with_code(self.ex, self.code, &self.message), } } @@ -166,7 +170,6 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> Ok(()) } - /// Throws an exception object. /// /// Returns a result containing nothing if the exception was successfully From 4fabeb2d5ee7f4830c6ac4e242e5bbb64dbda8ee Mon Sep 17 00:00:00 2001 From: Pierre Tondereau Date: Tue, 20 Dec 2022 17:36:16 +0100 Subject: [PATCH 004/100] Update doc bindings. --- docsrs_bindings.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index a2ecbc71fc..9714e61aa7 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -1442,6 +1442,9 @@ extern "C" { ... ) -> *mut zend_object; } +extern "C" { + pub fn zend_throw_exception_object(exception: *mut zval); +} extern "C" { pub fn zend_do_implement_interface(ce: *mut zend_class_entry, iface: *mut zend_class_entry); } From 7b2dd27fd4dc52af7c14cf106de4b1ca33d713a6 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 7 Jul 2023 13:43:26 +0200 Subject: [PATCH 005/100] Add ProcessGlobals This is akin to ExecutorGlobals, but for the process globals --- allowed_bindings.rs | 10 ++++- src/zend/globals.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++- src/zend/mod.rs | 1 + 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 7943f33396..3e1e7e7be8 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -224,8 +224,16 @@ bind! { gc_possible_root, ZEND_ACC_NOT_SERIALIZABLE, executor_globals, + php_core_globals, + core_globals, php_printf, __zend_malloc, tsrm_get_ls_cache, - executor_globals_offset + TRACK_VARS_POST, + TRACK_VARS_GET, + TRACK_VARS_COOKIE, + TRACK_VARS_SERVER, + TRACK_VARS_ENV, + TRACK_VARS_FILES, + TRACK_VARS_REQUEST } diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 7ba76c4980..b241cee49c 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -5,7 +5,11 @@ use std::ops::{Deref, DerefMut}; use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; -use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals}; +use crate::ffi::{ + _zend_executor_globals, core_globals, ext_php_rs_executor_globals, php_core_globals, + TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, + TRACK_VARS_REQUEST, TRACK_VARS_SERVER, +}; use crate::types::{ZendHashTable, ZendObject}; @@ -67,11 +71,98 @@ impl ExecutorGlobals { } } +/// Stores global variables used in the PHP executor. +pub type ProcessGlobals = php_core_globals; + +impl ProcessGlobals { + /// Returns a reference to the PHP process globals. + /// + /// The process globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get() -> GlobalReadGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { &core_globals }; + let guard = PROCESS_GLOBALS_LOCK.read(); + GlobalReadGuard { globals, guard } + } + + /// Returns a mutable reference to the PHP executor globals. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get_mut() -> GlobalWriteGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { &mut core_globals }; + let guard = PROCESS_GLOBALS_LOCK.write(); + GlobalWriteGuard { globals, guard } + } + + /// Get the HTTP Server variables. Equivalent of $_SERVER. + pub fn http_server_vars(&self) -> Option<&ZendHashTable> { + if self.http_globals[TRACK_VARS_SERVER as usize].is_array() { + self.http_globals[TRACK_VARS_SERVER as usize].array() + } else { + None + } + } + + /// Get the HTTP POST variables. Equivalent of $_POST. + pub fn http_post_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_POST as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP GET variables. Equivalent of $_GET. + pub fn http_get_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_GET as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP Cookie variables. Equivalent of $_COOKIE. + pub fn http_cookie_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_COOKIE as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP Request variables. Equivalent of $_REQUEST. + pub fn http_request_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_REQUEST as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP Environment variables. Equivalent of $_ENV. + pub fn http_env_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_ENV as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP Files variables. Equivalent of $_FILES. + pub fn http_files_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_FILES as usize] + .array() + .expect("Type is not a ZendArray") + } +} + /// Executor globals rwlock. /// /// PHP provides no indication if the executor globals are being accessed so /// this is only effective on the Rust side. static GLOBALS_LOCK: RwLock<()> = const_rwlock(()); +static PROCESS_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); /// Wrapper guard that contains a reference to a given type `T`. Dropping a /// guard releases the lock on the relevant rwlock. diff --git a/src/zend/mod.rs b/src/zend/mod.rs index 74547f044a..2aabd81e44 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -17,6 +17,7 @@ pub use class::ClassEntry; pub use ex::ExecuteData; pub use function::FunctionEntry; pub use globals::ExecutorGlobals; +pub use globals::ProcessGlobals; pub use handlers::ZendObjectHandlers; pub use module::ModuleEntry; From 21c4bf68daecc58eff54780df4bcc7420a8e1ea8 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 7 Jul 2023 13:58:49 +0200 Subject: [PATCH 006/100] Update bindings --- docsrs_bindings.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index f4fb348490..619d19fc36 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -1341,6 +1341,88 @@ extern "C" { extern "C" { pub fn php_printf(format: *const ::std::os::raw::c_char, ...) -> usize; } +pub type php_core_globals = _php_core_globals; +#[repr(C)] +pub struct _php_core_globals { + pub implicit_flush: zend_bool, + pub output_buffering: zend_long, + pub enable_dl: zend_bool, + pub output_handler: *mut ::std::os::raw::c_char, + pub unserialize_callback_func: *mut ::std::os::raw::c_char, + pub serialize_precision: zend_long, + pub memory_limit: zend_long, + pub max_input_time: zend_long, + pub display_errors: zend_uchar, + pub display_startup_errors: zend_bool, + pub log_errors: zend_bool, + pub log_errors_max_len: zend_long, + pub ignore_repeated_errors: zend_bool, + pub ignore_repeated_source: zend_bool, + pub report_memleaks: zend_bool, + pub error_log: *mut ::std::os::raw::c_char, + pub doc_root: *mut ::std::os::raw::c_char, + pub user_dir: *mut ::std::os::raw::c_char, + pub include_path: *mut ::std::os::raw::c_char, + pub open_basedir: *mut ::std::os::raw::c_char, + pub extension_dir: *mut ::std::os::raw::c_char, + pub php_binary: *mut ::std::os::raw::c_char, + pub sys_temp_dir: *mut ::std::os::raw::c_char, + pub upload_tmp_dir: *mut ::std::os::raw::c_char, + pub upload_max_filesize: zend_long, + pub error_append_string: *mut ::std::os::raw::c_char, + pub error_prepend_string: *mut ::std::os::raw::c_char, + pub auto_prepend_file: *mut ::std::os::raw::c_char, + pub auto_append_file: *mut ::std::os::raw::c_char, + pub input_encoding: *mut ::std::os::raw::c_char, + pub internal_encoding: *mut ::std::os::raw::c_char, + pub output_encoding: *mut ::std::os::raw::c_char, + pub arg_separator: arg_separators, + pub variables_order: *mut ::std::os::raw::c_char, + pub rfc1867_protected_variables: HashTable, + pub connection_status: ::std::os::raw::c_short, + pub ignore_user_abort: zend_bool, + pub header_is_being_sent: ::std::os::raw::c_uchar, + pub tick_functions: zend_llist, + pub http_globals: [zval; 6usize], + pub expose_php: zend_bool, + pub register_argc_argv: zend_bool, + pub auto_globals_jit: zend_bool, + pub docref_root: *mut ::std::os::raw::c_char, + pub docref_ext: *mut ::std::os::raw::c_char, + pub html_errors: zend_bool, + pub xmlrpc_errors: zend_bool, + pub xmlrpc_error_number: zend_long, + pub activated_auto_globals: [zend_bool; 8usize], + pub modules_activated: zend_bool, + pub file_uploads: zend_bool, + pub during_request_startup: zend_bool, + pub allow_url_fopen: zend_bool, + pub enable_post_data_reading: zend_bool, + pub report_zend_debug: zend_bool, + pub last_error_type: ::std::os::raw::c_int, + pub last_error_message: *mut zend_string, + pub last_error_file: *mut ::std::os::raw::c_char, + pub last_error_lineno: ::std::os::raw::c_int, + pub php_sys_temp_dir: *mut ::std::os::raw::c_char, + pub disable_classes: *mut ::std::os::raw::c_char, + pub allow_url_include: zend_bool, + pub max_input_nesting_level: zend_long, + pub max_input_vars: zend_long, + pub in_user_include: zend_bool, + pub user_ini_filename: *mut ::std::os::raw::c_char, + pub user_ini_cache_ttl: zend_long, + pub request_order: *mut ::std::os::raw::c_char, + pub mail_x_header: zend_bool, + pub mail_log: *mut ::std::os::raw::c_char, + pub in_error_log: zend_bool, + pub syslog_facility: zend_long, + pub syslog_ident: *mut ::std::os::raw::c_char, + pub have_called_openlog: zend_bool, + pub syslog_filter: zend_long, +} +extern "C" { + pub static mut core_globals: _php_core_globals; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _zend_ini_entry { From 01060ff08e49f33710c7450848099b09c35a5805 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 7 Jul 2023 14:21:16 +0200 Subject: [PATCH 007/100] Support ZTS --- src/ffi.rs | 1 + src/wrapper.c | 12 ++++++++++++ src/wrapper.h | 3 ++- src/zend/globals.rs | 10 +++++----- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index 92614c475e..a23fcb1710 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -26,6 +26,7 @@ extern "C" { pub fn ext_php_rs_zend_object_alloc(obj_size: usize, ce: *mut zend_class_entry) -> *mut c_void; pub fn ext_php_rs_zend_object_release(obj: *mut zend_object); pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals; + pub fn ext_php_rs_process_globals() -> *mut php_core_globals; } include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/src/wrapper.c b/src/wrapper.c index faf585e417..7a5eba5569 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -39,3 +39,15 @@ zend_executor_globals *ext_php_rs_executor_globals() { return &executor_globals; #endif } + +php_core_globals *ext_php_rs_process_globals() { +#ifdef ZTS +#ifdef ZEND_ENABLE_STATIC_TSRMLS_CACHE + return TSRMG_FAST_BULK_STATIC(core_globals_offset, php_core_globals); +#else + return TSRMG_FAST_BULK(core_globals_offset, php_core_globals *); +#endif +#else + return &core_globals; +#endif +} diff --git a/src/wrapper.h b/src/wrapper.h index 2813263676..c00ab5b9bd 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -29,4 +29,5 @@ void ext_php_rs_set_known_valid_utf8(zend_string *zs); const char *ext_php_rs_php_build_id(); void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce); void ext_php_rs_zend_object_release(zend_object *obj); -zend_executor_globals *ext_php_rs_executor_globals(); \ No newline at end of file +zend_executor_globals *ext_php_rs_executor_globals(); +php_core_globals *ext_php_rs_process_globals(); diff --git a/src/zend/globals.rs b/src/zend/globals.rs index b241cee49c..7ed5eef418 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -6,9 +6,9 @@ use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; use crate::ffi::{ - _zend_executor_globals, core_globals, ext_php_rs_executor_globals, php_core_globals, - TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, - TRACK_VARS_REQUEST, TRACK_VARS_SERVER, + _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_process_globals, + php_core_globals, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, + TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, }; use crate::types::{ZendHashTable, ZendObject}; @@ -85,7 +85,7 @@ impl ProcessGlobals { pub fn get() -> GlobalReadGuard { // SAFETY: PHP executor globals are statically declared therefore should never // return an invalid pointer. - let globals = unsafe { &core_globals }; + let globals = unsafe { &*ext_php_rs_process_globals() }; let guard = PROCESS_GLOBALS_LOCK.read(); GlobalReadGuard { globals, guard } } @@ -100,7 +100,7 @@ impl ProcessGlobals { pub fn get_mut() -> GlobalWriteGuard { // SAFETY: PHP executor globals are statically declared therefore should never // return an invalid pointer. - let globals = unsafe { &mut core_globals }; + let globals = unsafe { &mut *ext_php_rs_process_globals() }; let guard = PROCESS_GLOBALS_LOCK.write(); GlobalWriteGuard { globals, guard } } From 36d6159514f1950ee3320aa6bae842d3c9ca96aa Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 7 Jul 2023 14:41:44 +0200 Subject: [PATCH 008/100] update bindings --- docsrs_bindings.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 619d19fc36..e030037258 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -83,12 +83,20 @@ pub const ZEND_MODULE_API_NO: u32 = 20220829; pub const USING_ZTS: u32 = 0; pub const MAY_BE_BOOL: u32 = 12; pub const MAY_BE_ANY: u32 = 1022; +pub const TRACK_VARS_POST: u32 = 0; +pub const TRACK_VARS_GET: u32 = 1; +pub const TRACK_VARS_COOKIE: u32 = 2; +pub const TRACK_VARS_SERVER: u32 = 3; +pub const TRACK_VARS_ENV: u32 = 4; +pub const TRACK_VARS_FILES: u32 = 5; +pub const TRACK_VARS_REQUEST: u32 = 6; 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; pub type zend_long = i64; pub type zend_ulong = u64; +pub type zend_bool = bool; pub type zend_uchar = ::std::os::raw::c_uchar; pub const ZEND_RESULT_CODE_SUCCESS: ZEND_RESULT_CODE = 0; pub const ZEND_RESULT_CODE_FAILURE: ZEND_RESULT_CODE = -1; @@ -305,6 +313,28 @@ extern "C" { extern "C" { pub fn __zend_malloc(len: usize) -> *mut ::std::os::raw::c_void; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_llist_element { + pub next: *mut _zend_llist_element, + pub prev: *mut _zend_llist_element, + pub data: [::std::os::raw::c_char; 1usize], +} +pub type zend_llist_element = _zend_llist_element; +pub type llist_dtor_func_t = + ::std::option::Option; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_llist { + pub head: *mut zend_llist_element, + pub tail: *mut zend_llist_element, + pub count: usize, + pub size: usize, + pub dtor: llist_dtor_func_t, + pub persistent: ::std::os::raw::c_uchar, + pub traverse_ptr: *mut zend_llist_element, +} +pub type zend_llist = _zend_llist; pub type zend_string_init_interned_func_t = ::std::option::Option< unsafe extern "C" fn( str_: *const ::std::os::raw::c_char, @@ -1425,6 +1455,13 @@ extern "C" { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct _arg_separators { + pub output: *mut ::std::os::raw::c_char, + pub input: *mut ::std::os::raw::c_char, +} +pub type arg_separators = _arg_separators; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct _zend_ini_entry { pub name: *mut zend_string, pub on_modify: ::std::option::Option< From ca04318cf8f2b04b258ae1c47c8b068b0e65050c Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 12 Jul 2023 20:05:37 +0200 Subject: [PATCH 009/100] Add support for SapiGlobals --- allowed_bindings.rs | 2 + docsrs_bindings.rs | 194 +++++++++++++------------------------------- src/wrapper.c | 13 +++ src/wrapper.h | 2 + src/zend/globals.rs | 94 ++++++++++++++++++++- 5 files changed, 163 insertions(+), 142 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 3e1e7e7be8..cc17d5cbdd 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -226,6 +226,8 @@ bind! { executor_globals, php_core_globals, core_globals, + sapi_globals_struct, + sapi_globals, php_printf, __zend_malloc, tsrm_get_ls_cache, diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index e030037258..9d1578156d 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -1,7 +1,7 @@ /* automatically generated by rust-bindgen 0.65.1 */ -pub const ZEND_DEBUG: u32 = 1; -pub const _ZEND_TYPE_NAME_BIT: u32 = 16777216; +pub const ZEND_DEBUG: u32 = 0; +pub const _ZEND_TYPE_NAME_BIT: u32 = 8388608; pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2; pub const HT_MIN_SIZE: u32 = 8; pub const IS_UNDEF: u32 = 0; @@ -20,7 +20,7 @@ pub const IS_CALLABLE: u32 = 12; pub const IS_VOID: u32 = 14; pub const IS_MIXED: u32 = 16; pub const IS_PTR: u32 = 13; -pub const _IS_BOOL: u32 = 18; +pub const _IS_BOOL: u32 = 17; pub const Z_TYPE_FLAGS_SHIFT: u32 = 8; pub const IS_TYPE_REFCOUNTED: u32 = 1; pub const IS_TYPE_COLLECTABLE: u32 = 2; @@ -54,11 +54,13 @@ pub const ZEND_ACC_USE_GUARDS: u32 = 2048; pub const ZEND_ACC_CONSTANTS_UPDATED: u32 = 4096; pub const ZEND_ACC_NO_DYNAMIC_PROPERTIES: u32 = 8192; pub const ZEND_HAS_STATIC_IN_METHODS: u32 = 16384; +pub const ZEND_ACC_PROPERTY_TYPES_RESOLVED: u32 = 32768; +pub const ZEND_ACC_REUSE_GET_ITERATOR: u32 = 65536; pub const ZEND_ACC_RESOLVED_PARENT: u32 = 131072; pub const ZEND_ACC_RESOLVED_INTERFACES: u32 = 262144; pub const ZEND_ACC_UNRESOLVED_VARIANCE: u32 = 524288; pub const ZEND_ACC_NEARLY_LINKED: u32 = 1048576; -pub const ZEND_ACC_NOT_SERIALIZABLE: u32 = 536870912; +pub const ZEND_ACC_HAS_UNLINKED_USES: u32 = 2097152; pub const ZEND_ACC_DEPRECATED: u32 = 2048; pub const ZEND_ACC_RETURN_REFERENCE: u32 = 4096; pub const ZEND_ACC_HAS_RETURN_TYPE: u32 = 8192; @@ -77,9 +79,9 @@ pub const ZEND_ACC_DONE_PASS_TWO: u32 = 33554432; pub const ZEND_ACC_HEAP_RT_CACHE: u32 = 67108864; pub const ZEND_ACC_STRICT_TYPES: u32 = 2147483648; pub const ZEND_ISEMPTY: u32 = 1; -pub const _ZEND_SEND_MODE_SHIFT: u32 = 25; -pub const _ZEND_IS_VARIADIC_BIT: u32 = 134217728; -pub const ZEND_MODULE_API_NO: u32 = 20220829; +pub const _ZEND_SEND_MODE_SHIFT: u32 = 24; +pub const _ZEND_IS_VARIADIC_BIT: u32 = 67108864; +pub const ZEND_MODULE_API_NO: u32 = 20200930; pub const USING_ZTS: u32 = 0; pub const MAY_BE_BOOL: u32 = 12; pub const MAY_BE_ANY: u32 = 1022; @@ -180,6 +182,7 @@ pub union _zval_struct__bindgen_ty_2 { pub num_args: u32, pub fe_pos: u32, pub fe_iter_idx: u32, + pub access_flags: u32, pub property_guard: u32, pub constant_flags: u32, pub extra: u32, @@ -221,7 +224,7 @@ pub struct _zend_array { pub gc: zend_refcounted_h, pub u: _zend_array__bindgen_ty_1, pub nTableMask: u32, - pub __bindgen_anon_1: _zend_array__bindgen_ty_2, + pub arData: *mut Bucket, pub nNumUsed: u32, pub nNumOfElements: u32, pub nTableSize: u32, @@ -243,13 +246,6 @@ pub struct _zend_array__bindgen_ty_1__bindgen_ty_1 { pub nIteratorsCount: zend_uchar, pub _unused2: zend_uchar, } -#[repr(C)] -#[derive(Copy, Clone)] -pub union _zend_array__bindgen_ty_2 { - pub arHash: *mut u32, - pub arData: *mut Bucket, - pub arPacked: *mut zval, -} pub type HashPosition = u32; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -271,7 +267,7 @@ pub struct _zend_object { #[derive(Copy, Clone)] pub struct _zend_resource { pub gc: zend_refcounted_h, - pub handle: zend_long, + pub handle: ::std::os::raw::c_int, pub type_: ::std::os::raw::c_int, pub ptr: *mut ::std::os::raw::c_void, } @@ -293,22 +289,10 @@ pub struct _zend_ast_ref { pub gc: zend_refcounted_h, } extern "C" { - pub fn _emalloc( - size: usize, - __zend_filename: *const ::std::os::raw::c_char, - __zend_lineno: u32, - __zend_orig_filename: *const ::std::os::raw::c_char, - __zend_orig_lineno: u32, - ) -> *mut ::std::os::raw::c_void; -} -extern "C" { - pub fn _efree( - ptr: *mut ::std::os::raw::c_void, - __zend_filename: *const ::std::os::raw::c_char, - __zend_lineno: u32, - __zend_orig_filename: *const ::std::os::raw::c_char, - __zend_orig_lineno: u32, - ); + pub fn _emalloc(size: usize) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn _efree(ptr: *mut ::std::os::raw::c_void); } extern "C" { pub fn __zend_malloc(len: usize) -> *mut ::std::os::raw::c_void; @@ -393,7 +377,7 @@ extern "C" { pub fn zend_hash_get_current_key_zval_ex( ht: *const HashTable, key: *mut zval, - pos: *const HashPosition, + pos: *mut HashPosition, ); } extern "C" { @@ -476,15 +460,6 @@ pub struct _zend_class_iterator_funcs { pub type zend_class_iterator_funcs = _zend_class_iterator_funcs; #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct _zend_class_arrayaccess_funcs { - pub zf_offsetget: *mut zend_function, - pub zf_offsetexists: *mut zend_function, - pub zf_offsetset: *mut zend_function, - pub zf_offsetunset: *mut zend_function, -} -pub type zend_class_arrayaccess_funcs = _zend_class_arrayaccess_funcs; -#[repr(C)] -#[derive(Debug, Copy, Clone)] pub struct _zend_serialize_data { _unused: [u8; 0], } @@ -528,44 +503,6 @@ pub struct _zend_trait_alias { } pub type zend_trait_alias = _zend_trait_alias; #[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _zend_class_mutable_data { - pub default_properties_table: *mut zval, - pub constants_table: *mut HashTable, - pub ce_flags: u32, - pub backed_enum_table: *mut HashTable, -} -pub type zend_class_mutable_data = _zend_class_mutable_data; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _zend_class_dependency { - pub name: *mut zend_string, - pub ce: *mut zend_class_entry, -} -pub type zend_class_dependency = _zend_class_dependency; -pub type zend_inheritance_cache_entry = _zend_inheritance_cache_entry; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _zend_error_info { - pub type_: ::std::os::raw::c_int, - pub lineno: u32, - pub filename: *mut zend_string, - pub message: *mut zend_string, -} -pub type zend_error_info = _zend_error_info; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _zend_inheritance_cache_entry { - pub next: *mut zend_inheritance_cache_entry, - pub ce: *mut zend_class_entry, - pub parent: *mut zend_class_entry, - pub dependencies: *mut zend_class_dependency, - pub dependencies_count: u32, - pub num_warnings: u32, - pub warnings: *mut *mut zend_error_info, - pub traits_and_interfaces: [*mut zend_class_entry; 1usize], -} -#[repr(C)] pub struct _zend_class_entry { pub type_: ::std::os::raw::c_char, pub name: *mut zend_string, @@ -576,12 +513,10 @@ pub struct _zend_class_entry { pub default_static_members_count: ::std::os::raw::c_int, pub default_properties_table: *mut zval, pub default_static_members_table: *mut zval, - pub static_members_table__ptr: *mut zval, + pub static_members_table__ptr: *mut *mut zval, pub function_table: HashTable, pub properties_info: HashTable, pub constants_table: HashTable, - pub mutable_data__ptr: *mut zend_class_mutable_data, - pub inheritance_cache: *mut zend_inheritance_cache_entry, pub properties_info_table: *mut *mut _zend_property_info, pub constructor: *mut zend_function, pub destructor: *mut zend_function, @@ -597,7 +532,6 @@ pub struct _zend_class_entry { pub __serialize: *mut zend_function, pub __unserialize: *mut zend_function, pub iterator_funcs_ptr: *mut zend_class_iterator_funcs, - pub arrayaccess_funcs_ptr: *mut zend_class_arrayaccess_funcs, pub __bindgen_anon_2: _zend_class_entry__bindgen_ty_2, pub get_iterator: ::std::option::Option< unsafe extern "C" fn( @@ -636,8 +570,6 @@ pub struct _zend_class_entry { pub trait_aliases: *mut *mut zend_trait_alias, pub trait_precedences: *mut *mut zend_trait_precedence, pub attributes: *mut HashTable, - pub enum_backing_type: u32, - pub backed_enum_table: *mut HashTable, pub info: _zend_class_entry__bindgen_ty_4, } #[repr(C)] @@ -779,10 +711,10 @@ pub type zend_object_get_method_t = ::std::option::Option< >; pub type zend_object_get_constructor_t = ::std::option::Option *mut zend_function>; -pub type zend_object_free_obj_t = - ::std::option::Option; pub type zend_object_dtor_obj_t = ::std::option::Option; +pub type zend_object_free_obj_t = + ::std::option::Option; pub type zend_object_clone_obj_t = ::std::option::Option *mut zend_object>; pub type zend_object_get_class_name_t = @@ -795,10 +727,10 @@ pub type zend_object_cast_t = ::std::option::Option< readobj: *mut zend_object, retval: *mut zval, type_: ::std::os::raw::c_int, - ) -> zend_result, + ) -> ::std::os::raw::c_int, >; pub type zend_object_count_elements_t = ::std::option::Option< - unsafe extern "C" fn(object: *mut zend_object, count: *mut zend_long) -> zend_result, + unsafe extern "C" fn(object: *mut zend_object, count: *mut zend_long) -> ::std::os::raw::c_int, >; pub type zend_object_get_closure_t = ::std::option::Option< unsafe extern "C" fn( @@ -806,8 +738,8 @@ pub type zend_object_get_closure_t = ::std::option::Option< ce_ptr: *mut *mut zend_class_entry, fptr_ptr: *mut *mut zend_function, obj_ptr: *mut *mut zend_object, - check_only: bool, - ) -> zend_result, + check_only: zend_bool, + ) -> ::std::os::raw::c_int, >; pub type zend_object_get_gc_t = ::std::option::Option< unsafe extern "C" fn( @@ -822,7 +754,7 @@ pub type zend_object_do_operation_t = ::std::option::Option< result: *mut zval, op1: *mut zval, op2: *mut zval, - ) -> zend_result, + ) -> ::std::os::raw::c_int, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -885,7 +817,7 @@ extern "C" { ) -> ::std::os::raw::c_int; } extern "C" { - pub fn zend_is_identical(op1: *mut zval, op2: *mut zval) -> bool; + pub fn zend_is_identical(op1: *mut zval, op2: *mut zval) -> zend_bool; } extern "C" { pub fn zend_is_true(op: *mut zval) -> ::std::os::raw::c_int; @@ -973,13 +905,13 @@ pub struct _zend_op_array { pub required_num_args: u32, pub arg_info: *mut zend_arg_info, pub attributes: *mut HashTable, - pub T: u32, - pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, pub cache_size: ::std::os::raw::c_int, pub last_var: ::std::os::raw::c_int, + pub T: u32, pub last: u32, pub opcodes: *mut zend_op, - pub static_variables_ptr__ptr: *mut HashTable, + pub run_time_cache__ptr: *mut *mut *mut ::std::os::raw::c_void, + pub static_variables_ptr__ptr: *mut *mut HashTable, pub static_variables: *mut HashTable, pub vars: *mut *mut zend_string, pub refcount: *mut u32, @@ -992,9 +924,7 @@ pub struct _zend_op_array { pub line_end: u32, pub doc_comment: *mut zend_string, pub last_literal: ::std::os::raw::c_int, - pub num_dynamic_func_defs: u32, pub literals: *mut zval, - pub dynamic_func_defs: *mut *mut zend_op_array, pub reserved: [*mut ::std::os::raw::c_void; 6usize], } pub type zif_handler = ::std::option::Option< @@ -1013,8 +943,6 @@ pub struct _zend_internal_function { pub required_num_args: u32, pub arg_info: *mut zend_internal_arg_info, pub attributes: *mut HashTable, - pub T: u32, - pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, pub handler: zif_handler, pub module: *mut _zend_module_entry, pub reserved: [*mut ::std::os::raw::c_void; 6usize], @@ -1042,8 +970,6 @@ pub struct _zend_function__bindgen_ty_1 { pub required_num_args: u32, pub arg_info: *mut zend_arg_info, pub attributes: *mut HashTable, - pub T: u32, - pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, } #[repr(C)] pub struct _zend_execute_data { @@ -1064,12 +990,6 @@ extern "C" { } #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct zend_atomic_bool_s { - pub value: u8, -} -pub type zend_atomic_bool = zend_atomic_bool_s; -#[repr(C)] -#[derive(Debug, Copy, Clone)] pub struct _zend_stack { pub size: ::std::os::raw::c_int, pub top: ::std::os::raw::c_int, @@ -1104,18 +1024,6 @@ extern "C" { pub type zend_vm_stack = *mut _zend_vm_stack; pub type zend_ini_entry = _zend_ini_entry; #[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _zend_fiber_context { - _unused: [u8; 0], -} -pub type zend_fiber_context = _zend_fiber_context; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _zend_fiber { - _unused: [u8; 0], -} -pub type zend_fiber = _zend_fiber; -#[repr(C)] pub struct _zend_executor_globals { pub uninitialized_zval: zval, pub error_zval: zval, @@ -1143,10 +1051,10 @@ pub struct _zend_executor_globals { pub persistent_functions_count: u32, pub persistent_classes_count: u32, pub in_autoload: *mut HashTable, - pub full_tables_cleanup: bool, - pub no_extensions: bool, - pub vm_interrupt: zend_atomic_bool, - pub timed_out: zend_atomic_bool, + pub full_tables_cleanup: zend_bool, + pub no_extensions: zend_bool, + pub vm_interrupt: zend_bool, + pub timed_out: zend_bool, pub hard_timeout: zend_long, pub regular_list: HashTable, pub persistent_list: HashTable, @@ -1159,7 +1067,7 @@ pub struct _zend_executor_globals { pub error_handling: zend_error_handling_t, pub exception_class: *mut zend_class_entry, pub timeout_seconds: zend_long, - pub capture_warnings_during_sccp: ::std::os::raw::c_int, + pub lambda_count: ::std::os::raw::c_int, pub ini_directives: *mut HashTable, pub modified_ini_directives: *mut HashTable, pub error_reporting_ini_entry: *mut zend_ini_entry, @@ -1169,7 +1077,7 @@ pub struct _zend_executor_globals { pub opline_before_exception: *const zend_op, pub exception_op: [zend_op; 3usize], pub current_module: *mut _zend_module_entry, - pub active: bool, + pub active: zend_bool, pub flags: zend_uchar, pub assertions: zend_long, pub ht_iterators_count: u32, @@ -1180,18 +1088,9 @@ pub struct _zend_executor_globals { pub trampoline: zend_function, pub call_trampoline_op: zend_op, pub weakrefs: HashTable, - pub exception_ignore_args: bool, + pub exception_ignore_args: zend_bool, pub exception_string_param_max_len: zend_long, pub get_gc_buffer: zend_get_gc_buffer, - pub main_fiber_context: *mut zend_fiber_context, - pub current_fiber_context: *mut zend_fiber_context, - pub active_fiber: *mut zend_fiber, - pub fiber_stack_size: zend_long, - pub record_errors: bool, - pub num_errors: u32, - pub errors: *mut *mut zend_error_info, - pub filename_override: *mut zend_string, - pub lineno_override: zend_long, pub reserved: [*mut ::std::os::raw::c_void; 6usize], } pub type zend_module_entry = _zend_module_entry; @@ -1288,7 +1187,7 @@ extern "C" { callable: *mut zval, check_flags: u32, callable_name: *mut *mut zend_string, - ) -> bool; + ) -> zend_bool; } extern "C" { pub fn zend_declare_property( @@ -1491,7 +1390,7 @@ extern "C" { pub fn zend_register_bool_constant( name: *const ::std::os::raw::c_char, name_len: usize, - bval: bool, + bval: zend_bool, flags: ::std::os::raw::c_int, module_number: ::std::os::raw::c_int, ); @@ -1600,3 +1499,20 @@ extern "C" { extern "C" { pub static mut zend_ce_stringable: *mut zend_class_entry; } +extern "C" { + pub fn zend_class_serialize_deny( + object: *mut zval, + buffer: *mut *mut ::std::os::raw::c_uchar, + buf_len: *mut usize, + data: *mut zend_serialize_data, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn zend_class_unserialize_deny( + object: *mut zval, + ce: *mut zend_class_entry, + buf: *const ::std::os::raw::c_uchar, + buf_len: usize, + data: *mut zend_unserialize_data, + ) -> ::std::os::raw::c_int; +} diff --git a/src/wrapper.c b/src/wrapper.c index 7a5eba5569..514882b6f6 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -51,3 +51,16 @@ php_core_globals *ext_php_rs_process_globals() { return &core_globals; #endif } + + +sapi_globals_struct *ext_php_rs_sapi_globals() { +#ifdef ZTS +#ifdef ZEND_ENABLE_STATIC_TSRMLS_CACHE + return TSRMG_FAST_BULK_STATIC(sapi_globals_offset, sapi_globals_struct); +#else + return TSRMG_FAST_BULK(sapi_globals_offset, sapi_globals_struct *); +#endif +#else + return &sapi_globals; +#endif +} diff --git a/src/wrapper.h b/src/wrapper.h index c00ab5b9bd..0fb8287a7b 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -20,6 +20,7 @@ #include "zend_exceptions.h" #include "zend_inheritance.h" #include "zend_interfaces.h" +#include "SAPI.h" zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent); void ext_php_rs_zend_string_release(zend_string *zs); @@ -31,3 +32,4 @@ void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce); void ext_php_rs_zend_object_release(zend_object *obj); zend_executor_globals *ext_php_rs_executor_globals(); php_core_globals *ext_php_rs_process_globals(); +sapi_globals_struct *ext_php_rs_sapi_globals(); diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 7ed5eef418..abf3eef030 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -6,9 +6,9 @@ use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; use crate::ffi::{ - _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_process_globals, - php_core_globals, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, - TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, + _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_process_globals, ext_php_rs_sapi_globals, + php_core_globals, sapi_globals_struct, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, + TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER }; use crate::types::{ZendHashTable, ZendObject}; @@ -157,12 +157,100 @@ impl ProcessGlobals { } } + +/// Stores global variables used in the SAPI. +pub type SapiGlobals = sapi_globals_struct; + +impl SapiGlobals { + /// Returns a reference to the PHP process globals. + /// + /// The process globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get() -> GlobalReadGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { &*ext_php_rs_sapi_globals() }; + let guard = SAPI_GLOBALS_LOCK.read(); + GlobalReadGuard { globals, guard } + } + + /// Returns a mutable reference to the PHP executor globals. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get_mut() -> GlobalWriteGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { &mut *ext_php_rs_sapi_globals() }; + let guard = SAPI_GLOBALS_LOCK.write(); + GlobalWriteGuard { globals, guard } + } + + /// Get the HTTP Server variables. Equivalent of $_SERVER. + pub fn http_server_vars(&self) -> Option<&ZendHashTable> { + if self.http_globals[TRACK_VARS_SERVER as usize].is_array() { + self.http_globals[TRACK_VARS_SERVER as usize].array() + } else { + None + } + } + + /// Get the HTTP POST variables. Equivalent of $_POST. + pub fn http_post_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_POST as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP GET variables. Equivalent of $_GET. + pub fn http_get_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_GET as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP Cookie variables. Equivalent of $_COOKIE. + pub fn http_cookie_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_COOKIE as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP Request variables. Equivalent of $_REQUEST. + pub fn http_request_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_REQUEST as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP Environment variables. Equivalent of $_ENV. + pub fn http_env_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_ENV as usize] + .array() + .expect("Type is not a ZendArray") + } + + /// Get the HTTP Files variables. Equivalent of $_FILES. + pub fn http_files_vars(&self) -> &ZendHashTable { + self.http_globals[TRACK_VARS_FILES as usize] + .array() + .expect("Type is not a ZendArray") + } +} + /// Executor globals rwlock. /// /// PHP provides no indication if the executor globals are being accessed so /// this is only effective on the Rust side. static GLOBALS_LOCK: RwLock<()> = const_rwlock(()); static PROCESS_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); +static SAPI_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); /// Wrapper guard that contains a reference to a given type `T`. Dropping a /// guard releases the lock on the relevant rwlock. From fcf9c2076e54481c7dcbbe3708c40cb1bd1abaa5 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 12 Jul 2023 20:15:11 +0200 Subject: [PATCH 010/100] Add export --- src/ffi.rs | 1 + src/zend/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ffi.rs b/src/ffi.rs index a23fcb1710..cd3a81c9cb 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -27,6 +27,7 @@ extern "C" { pub fn ext_php_rs_zend_object_release(obj: *mut zend_object); pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals; pub fn ext_php_rs_process_globals() -> *mut php_core_globals; + pub fn ext_php_rs_sapi_globals() -> *mut sapi_globals_struct; } include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/src/zend/mod.rs b/src/zend/mod.rs index 2aabd81e44..246626ba02 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -18,6 +18,7 @@ pub use ex::ExecuteData; pub use function::FunctionEntry; pub use globals::ExecutorGlobals; pub use globals::ProcessGlobals; +pub use globals::SapiGlobals; pub use handlers::ZendObjectHandlers; pub use module::ModuleEntry; From 003eb8a76a4163c14bbf1a28b18aeffb6fa8be3f Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 14:33:40 +0200 Subject: [PATCH 011/100] Add support for headers etc --- allowed_bindings.rs | 7 +- src/wrapper.h | 1 + src/zend/globals.rs | 221 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 188 insertions(+), 41 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index cc17d5cbdd..c9324677bb 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -237,5 +237,10 @@ bind! { TRACK_VARS_SERVER, TRACK_VARS_ENV, TRACK_VARS_FILES, - TRACK_VARS_REQUEST + TRACK_VARS_REQUEST, + sapi_request_info, + sapi_header_struct, + zend_is_auto_global, + zend_llist_get_next_ex, + zend_llist_get_prev_ex } diff --git a/src/wrapper.h b/src/wrapper.h index 0fb8287a7b..cd8542cdc8 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -21,6 +21,7 @@ #include "zend_inheritance.h" #include "zend_interfaces.h" #include "SAPI.h" +#include "php_variables.h" zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent); void ext_php_rs_zend_string_release(zend_string *zs); diff --git a/src/zend/globals.rs b/src/zend/globals.rs index abf3eef030..55916c1af1 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -1,6 +1,9 @@ -//! Types related to the PHP executor globals. - +//! Types related to the PHP executor, sapi and process globals. +use std::ffi::CStr; use std::ops::{Deref, DerefMut}; +use std::slice; +use std::str; + use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -8,10 +11,12 @@ use crate::boxed::ZBox; use crate::ffi::{ _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_process_globals, ext_php_rs_sapi_globals, php_core_globals, sapi_globals_struct, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, - TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER + TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, sapi_request_info, zend_is_auto_global, sapi_headers_struct, sapi_header_struct }; -use crate::types::{ZendHashTable, ZendObject}; +use crate::types::{ZendHashTable, ZendObject, ZendStr}; + +use super::linked_list::ZendLinkedListIterator; /// Stores global variables used in the PHP executor. pub type ExecutorGlobals = _zend_executor_globals; @@ -107,6 +112,13 @@ impl ProcessGlobals { /// Get the HTTP Server variables. Equivalent of $_SERVER. pub fn http_server_vars(&self) -> Option<&ZendHashTable> { + + // $_SERVER is lazy-initted, we need to call zend_is_auto_global + // if it's not already populated. + if ! self.http_globals[TRACK_VARS_SERVER as usize].is_array() { + let name = ZendStr::new("_SERVER", false).as_mut_ptr(); + unsafe { zend_is_auto_global(name) }; + } if self.http_globals[TRACK_VARS_SERVER as usize].is_array() { self.http_globals[TRACK_VARS_SERVER as usize].array() } else { @@ -191,57 +203,186 @@ impl SapiGlobals { let guard = SAPI_GLOBALS_LOCK.write(); GlobalWriteGuard { globals, guard } } + // Get the request info for the Sapi. + pub fn request_info(&self) -> &SapiRequestInfo { + &self.request_info + } - /// Get the HTTP Server variables. Equivalent of $_SERVER. - pub fn http_server_vars(&self) -> Option<&ZendHashTable> { - if self.http_globals[TRACK_VARS_SERVER as usize].is_array() { - self.http_globals[TRACK_VARS_SERVER as usize].array() - } else { - None + pub fn sapi_headers(&self) -> &SapiHeaders{ + &self.sapi_headers + } +} + +pub type SapiHeaders = sapi_headers_struct; + +impl<'a> SapiHeaders { + pub fn headers(&'a mut self) -> ZendLinkedListIterator<'a, SapiHeader> { + self.headers.iter() + } +} + +pub type SapiHeader = sapi_header_struct; + +impl<'a> SapiHeader { + pub fn as_str(&'a self) -> &'a str { + unsafe { + let slice = slice::from_raw_parts(self.header as *const u8, self.header_len); + str::from_utf8(slice).unwrap() } } - /// Get the HTTP POST variables. Equivalent of $_POST. - pub fn http_post_vars(&self) -> &ZendHashTable { - self.http_globals[TRACK_VARS_POST as usize] - .array() - .expect("Type is not a ZendArray") + pub fn name(&'a self) -> &'a str { + self.as_str().split(":").next().unwrap_or("") } - /// Get the HTTP GET variables. Equivalent of $_GET. - pub fn http_get_vars(&self) -> &ZendHashTable { - self.http_globals[TRACK_VARS_GET as usize] - .array() - .expect("Type is not a ZendArray") + pub fn value(&'a self) -> Option<&'a str> { + self.as_str().split(":").nth(1) } +} - /// Get the HTTP Cookie variables. Equivalent of $_COOKIE. - pub fn http_cookie_vars(&self) -> &ZendHashTable { - self.http_globals[TRACK_VARS_COOKIE as usize] - .array() - .expect("Type is not a ZendArray") +pub type SapiRequestInfo = sapi_request_info; + +impl SapiRequestInfo { + pub fn request_method(&self) -> Option<&str> { + if self.request_method.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.request_method ).to_str().ok() + } } - /// Get the HTTP Request variables. Equivalent of $_REQUEST. - pub fn http_request_vars(&self) -> &ZendHashTable { - self.http_globals[TRACK_VARS_REQUEST as usize] - .array() - .expect("Type is not a ZendArray") + pub fn query_string(&self) -> Option<&str> { + if self.query_string.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.query_string ).to_str().ok() + } } - /// Get the HTTP Environment variables. Equivalent of $_ENV. - pub fn http_env_vars(&self) -> &ZendHashTable { - self.http_globals[TRACK_VARS_ENV as usize] - .array() - .expect("Type is not a ZendArray") + pub fn cookie_data(&self) -> Option<&str> { + if self.cookie_data.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.cookie_data ).to_str().ok() + } } - /// Get the HTTP Files variables. Equivalent of $_FILES. - pub fn http_files_vars(&self) -> &ZendHashTable { - self.http_globals[TRACK_VARS_FILES as usize] - .array() - .expect("Type is not a ZendArray") + pub fn content_length(&self) -> i64 { + self.content_length + } + + pub fn path_translated(&self) -> Option<&str> { + if self.path_translated.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.path_translated ).to_str().ok() + } + } + + pub fn request_uri(&self) -> Option<&str> { + if self.request_uri.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.request_uri ).to_str().ok() + } + } + + // Todo: request_body _php_stream + + pub fn content_type(&self) -> Option<&str> { + if self.content_type.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.content_type ).to_str().ok() + } + } + + pub fn headers_only(&self) -> bool { + self.headers_only + } + + pub fn no_headers(&self) -> bool { + self.no_headers + } + + pub fn headers_read(&self) -> bool { + self.headers_read + } + + // Todo: post_entry sapi_post_entry + + pub fn auth_user(&self) -> Option<&str> { + if self.auth_user.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.auth_user ).to_str().ok() + } } + + pub fn auth_password(&self) -> Option<&str> { + if self.auth_password.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.auth_password ).to_str().ok() + } + } + + pub fn auth_digest(&self) -> Option<&str> { + if self.auth_digest.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.auth_digest ).to_str().ok() + } + } + + pub fn argv0(&self) -> Option<&str> { + if self.argv0.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.argv0 ).to_str().ok() + } + } + + pub fn current_user(&self) -> Option<&str> { + if self.current_user.is_null() { + return None; + } + unsafe { + CStr::from_ptr( self.current_user ).to_str().ok() + } + } + + pub fn current_user_length(&self) -> i32 { + self.current_user_length + } + + pub fn argvc(&self) -> i32 { + self.argc + } + + pub fn argv(&self) -> Option<&str> { + if self.argv.is_null() { + return None; + } + unsafe { + CStr::from_ptr( *self.argv ).to_str().ok() + } + } + + pub fn proto_num(&self) -> i32 { + self.proto_num + } + } /// Executor globals rwlock. From f3bf835ddbb928e44c3666b70eb2293457449012 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 14:36:30 +0200 Subject: [PATCH 012/100] Add bindings for zend_llist --- src/zend/linked_list.rs | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/zend/linked_list.rs diff --git a/src/zend/linked_list.rs b/src/zend/linked_list.rs new file mode 100644 index 0000000000..49aeb94524 --- /dev/null +++ b/src/zend/linked_list.rs @@ -0,0 +1,46 @@ +use std::marker::PhantomData; + +use crate::ffi::{zend_llist, zend_llist_element, zend_llist_get_next_ex}; + +pub type ZendLinkedList = zend_llist; + +impl ZendLinkedList { + pub fn iter(&self) -> ZendLinkedListIterator { + ZendLinkedListIterator::new(self) + } +} + +pub struct ZendLinkedListIterator<'a, T> { + list: &'a zend_llist, + position: *mut zend_llist_element, + _marker: PhantomData, +} + +impl<'a, T> ZendLinkedListIterator<'a, T> { + fn new(list: &'a ZendLinkedList) -> Self { + ZendLinkedListIterator { + list, + position: list.head, + _marker: PhantomData, + } + } +} + +impl<'a, T: 'a> Iterator for ZendLinkedListIterator<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.position.is_null() { + return None; + } + let ptr = unsafe { (&mut *self.position).data.as_mut_ptr() }; + let value = unsafe { &*(ptr as *const T as *mut T) }; + unsafe { + zend_llist_get_next_ex( + self.list as *const ZendLinkedList as *mut ZendLinkedList, + &mut self.position, + ) + }; + Some(value) + } +} From 5395f25d7400eadfbf06ea744050e30504d38287 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 14:37:49 +0200 Subject: [PATCH 013/100] Import --- src/zend/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zend/mod.rs b/src/zend/mod.rs index 246626ba02..d5db3d9826 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -8,6 +8,7 @@ mod function; mod globals; mod handlers; mod module; +mod linked_list; use crate::{error::Result, ffi::php_printf}; use std::ffi::CString; @@ -21,6 +22,7 @@ pub use globals::ProcessGlobals; pub use globals::SapiGlobals; pub use handlers::ZendObjectHandlers; pub use module::ModuleEntry; +pub use linked_list::ZendLinkedList; // Used as the format string for `php_printf`. const FORMAT_STR: &[u8] = b"%s\0"; From c564f55183a095c2840dd7e837bd282ff00f9c59 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 15:57:29 +0200 Subject: [PATCH 014/100] Fix missing bindings --- allowed_bindings.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 3e1e7e7be8..8f5e958b9d 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -229,6 +229,8 @@ bind! { php_printf, __zend_malloc, tsrm_get_ls_cache, + executor_globals_offset, + core_globals_offset, TRACK_VARS_POST, TRACK_VARS_GET, TRACK_VARS_COOKIE, From b6a2d1861e4a081317c7a3a733240365d6b94857 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 16:32:53 +0200 Subject: [PATCH 015/100] Add missing binding --- allowed_bindings.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 1ee367913c..98248f1171 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -233,6 +233,7 @@ bind! { tsrm_get_ls_cache, executor_globals_offset, core_globals_offset, + sapi_globals_offset, TRACK_VARS_POST, TRACK_VARS_GET, TRACK_VARS_COOKIE, From a396085a923e5114d9bdd5673ae7078d07f262e7 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 16:54:35 +0200 Subject: [PATCH 016/100] fmt --- src/zend/globals.rs | 63 +++++++++++++-------------------------------- src/zend/mod.rs | 4 +-- 2 files changed, 20 insertions(+), 47 deletions(-) diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 55916c1af1..c51d3f34aa 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -4,14 +4,14 @@ use std::ops::{Deref, DerefMut}; use std::slice; use std::str; - use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; use crate::ffi::{ - _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_process_globals, ext_php_rs_sapi_globals, - php_core_globals, sapi_globals_struct, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, - TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, sapi_request_info, zend_is_auto_global, sapi_headers_struct, sapi_header_struct + _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_process_globals, + ext_php_rs_sapi_globals, php_core_globals, sapi_globals_struct, sapi_header_struct, + sapi_headers_struct, sapi_request_info, zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, + TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, }; use crate::types::{ZendHashTable, ZendObject, ZendStr}; @@ -112,10 +112,9 @@ impl ProcessGlobals { /// Get the HTTP Server variables. Equivalent of $_SERVER. pub fn http_server_vars(&self) -> Option<&ZendHashTable> { - // $_SERVER is lazy-initted, we need to call zend_is_auto_global // if it's not already populated. - if ! self.http_globals[TRACK_VARS_SERVER as usize].is_array() { + if !self.http_globals[TRACK_VARS_SERVER as usize].is_array() { let name = ZendStr::new("_SERVER", false).as_mut_ptr(); unsafe { zend_is_auto_global(name) }; } @@ -169,7 +168,6 @@ impl ProcessGlobals { } } - /// Stores global variables used in the SAPI. pub type SapiGlobals = sapi_globals_struct; @@ -208,7 +206,7 @@ impl SapiGlobals { &self.request_info } - pub fn sapi_headers(&self) -> &SapiHeaders{ + pub fn sapi_headers(&self) -> &SapiHeaders { &self.sapi_headers } } @@ -247,27 +245,21 @@ impl SapiRequestInfo { if self.request_method.is_null() { return None; } - unsafe { - CStr::from_ptr( self.request_method ).to_str().ok() - } + unsafe { CStr::from_ptr(self.request_method).to_str().ok() } } pub fn query_string(&self) -> Option<&str> { if self.query_string.is_null() { return None; } - unsafe { - CStr::from_ptr( self.query_string ).to_str().ok() - } + unsafe { CStr::from_ptr(self.query_string).to_str().ok() } } pub fn cookie_data(&self) -> Option<&str> { if self.cookie_data.is_null() { return None; } - unsafe { - CStr::from_ptr( self.cookie_data ).to_str().ok() - } + unsafe { CStr::from_ptr(self.cookie_data).to_str().ok() } } pub fn content_length(&self) -> i64 { @@ -278,18 +270,14 @@ impl SapiRequestInfo { if self.path_translated.is_null() { return None; } - unsafe { - CStr::from_ptr( self.path_translated ).to_str().ok() - } + unsafe { CStr::from_ptr(self.path_translated).to_str().ok() } } pub fn request_uri(&self) -> Option<&str> { if self.request_uri.is_null() { return None; } - unsafe { - CStr::from_ptr( self.request_uri ).to_str().ok() - } + unsafe { CStr::from_ptr(self.request_uri).to_str().ok() } } // Todo: request_body _php_stream @@ -298,9 +286,7 @@ impl SapiRequestInfo { if self.content_type.is_null() { return None; } - unsafe { - CStr::from_ptr( self.content_type ).to_str().ok() - } + unsafe { CStr::from_ptr(self.content_type).to_str().ok() } } pub fn headers_only(&self) -> bool { @@ -321,45 +307,35 @@ impl SapiRequestInfo { if self.auth_user.is_null() { return None; } - unsafe { - CStr::from_ptr( self.auth_user ).to_str().ok() - } + unsafe { CStr::from_ptr(self.auth_user).to_str().ok() } } pub fn auth_password(&self) -> Option<&str> { if self.auth_password.is_null() { return None; } - unsafe { - CStr::from_ptr( self.auth_password ).to_str().ok() - } + unsafe { CStr::from_ptr(self.auth_password).to_str().ok() } } pub fn auth_digest(&self) -> Option<&str> { if self.auth_digest.is_null() { return None; } - unsafe { - CStr::from_ptr( self.auth_digest ).to_str().ok() - } + unsafe { CStr::from_ptr(self.auth_digest).to_str().ok() } } pub fn argv0(&self) -> Option<&str> { if self.argv0.is_null() { return None; } - unsafe { - CStr::from_ptr( self.argv0 ).to_str().ok() - } + unsafe { CStr::from_ptr(self.argv0).to_str().ok() } } pub fn current_user(&self) -> Option<&str> { if self.current_user.is_null() { return None; } - unsafe { - CStr::from_ptr( self.current_user ).to_str().ok() - } + unsafe { CStr::from_ptr(self.current_user).to_str().ok() } } pub fn current_user_length(&self) -> i32 { @@ -374,15 +350,12 @@ impl SapiRequestInfo { if self.argv.is_null() { return None; } - unsafe { - CStr::from_ptr( *self.argv ).to_str().ok() - } + unsafe { CStr::from_ptr(*self.argv).to_str().ok() } } pub fn proto_num(&self) -> i32 { self.proto_num } - } /// Executor globals rwlock. diff --git a/src/zend/mod.rs b/src/zend/mod.rs index d5db3d9826..dfaa0ef9e0 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -7,8 +7,8 @@ mod ex; mod function; mod globals; mod handlers; -mod module; mod linked_list; +mod module; use crate::{error::Result, ffi::php_printf}; use std::ffi::CString; @@ -21,8 +21,8 @@ pub use globals::ExecutorGlobals; pub use globals::ProcessGlobals; pub use globals::SapiGlobals; pub use handlers::ZendObjectHandlers; -pub use module::ModuleEntry; pub use linked_list::ZendLinkedList; +pub use module::ModuleEntry; // Used as the format string for `php_printf`. const FORMAT_STR: &[u8] = b"%s\0"; From aee2625ccb136fa5b8c1fc19da7fa8b67c3b6dd6 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 20:40:27 +0200 Subject: [PATCH 017/100] Clippy --- src/zend/globals.rs | 6 +++--- src/zend/linked_list.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zend/globals.rs b/src/zend/globals.rs index c51d3f34aa..f7a1e1b68a 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -225,16 +225,16 @@ impl<'a> SapiHeader { pub fn as_str(&'a self) -> &'a str { unsafe { let slice = slice::from_raw_parts(self.header as *const u8, self.header_len); - str::from_utf8(slice).unwrap() + str::from_utf8(slice).expect("Invalid header string") } } pub fn name(&'a self) -> &'a str { - self.as_str().split(":").next().unwrap_or("") + self.as_str().split(':').next().unwrap_or("").trim() } pub fn value(&'a self) -> Option<&'a str> { - self.as_str().split(":").nth(1) + self.as_str().split(':').nth(1).map(|s| s.trim()) } } diff --git a/src/zend/linked_list.rs b/src/zend/linked_list.rs index 49aeb94524..481b229790 100644 --- a/src/zend/linked_list.rs +++ b/src/zend/linked_list.rs @@ -33,7 +33,7 @@ impl<'a, T: 'a> Iterator for ZendLinkedListIterator<'a, T> { if self.position.is_null() { return None; } - let ptr = unsafe { (&mut *self.position).data.as_mut_ptr() }; + let ptr = unsafe { (*self.position).data.as_mut_ptr() }; let value = unsafe { &*(ptr as *const T as *mut T) }; unsafe { zend_llist_get_next_ex( From 910c55217a3dc798f6371499ce336b821fae01e5 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 20:52:49 +0200 Subject: [PATCH 018/100] Update bindings --- docsrs_bindings.rs | 661 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 9d1578156d..2943262e83 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -1,5 +1,85 @@ /* automatically generated by rust-bindgen 0.65.1 */ +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct __BindgenBitfieldUnit { + storage: Storage, +} +impl __BindgenBitfieldUnit { + #[inline] + pub const fn new(storage: Storage) -> Self { + Self { storage } + } +} +impl __BindgenBitfieldUnit +where + Storage: AsRef<[u8]> + AsMut<[u8]>, +{ + #[inline] + pub fn get_bit(&self, index: usize) -> bool { + debug_assert!(index / 8 < self.storage.as_ref().len()); + let byte_index = index / 8; + let byte = self.storage.as_ref()[byte_index]; + let bit_index = if cfg!(target_endian = "big") { + 7 - (index % 8) + } else { + index % 8 + }; + let mask = 1 << bit_index; + byte & mask == mask + } + #[inline] + pub fn set_bit(&mut self, index: usize, val: bool) { + debug_assert!(index / 8 < self.storage.as_ref().len()); + let byte_index = index / 8; + let byte = &mut self.storage.as_mut()[byte_index]; + let bit_index = if cfg!(target_endian = "big") { + 7 - (index % 8) + } else { + index % 8 + }; + let mask = 1 << bit_index; + if val { + *byte |= mask; + } else { + *byte &= !mask; + } + } + #[inline] + pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + let mut val = 0; + for i in 0..(bit_width as usize) { + if self.get_bit(i + bit_offset) { + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + val |= 1 << index; + } + } + val + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + for i in 0..(bit_width as usize) { + let mask = 1 << i; + let val_bit_is_set = val & mask == mask; + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + self.set_bit(index + bit_offset, val_bit_is_set); + } + } +} pub const ZEND_DEBUG: u32 = 0; pub const _ZEND_TYPE_NAME_BIT: u32 = 8388608; pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2; @@ -96,8 +176,94 @@ 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; +pub type __uint16_t = ::std::os::raw::c_ushort; +pub type __int32_t = ::std::os::raw::c_int; +pub type __uint32_t = ::std::os::raw::c_uint; +pub type __int64_t = ::std::os::raw::c_longlong; +pub type __uint64_t = ::std::os::raw::c_ulonglong; +pub type __darwin_time_t = ::std::os::raw::c_long; +pub type __darwin_blkcnt_t = __int64_t; +pub type __darwin_blksize_t = __int32_t; +pub type __darwin_dev_t = __int32_t; +pub type __darwin_gid_t = __uint32_t; +pub type __darwin_ino64_t = __uint64_t; +pub type __darwin_mode_t = __uint16_t; +pub type __darwin_off_t = __int64_t; +pub type __darwin_uid_t = __uint32_t; +pub type uid_t = __darwin_uid_t; +pub type dev_t = __darwin_dev_t; +pub type mode_t = __darwin_mode_t; +pub type blkcnt_t = __darwin_blkcnt_t; +pub type blksize_t = __darwin_blksize_t; +pub type gid_t = __darwin_gid_t; +pub type nlink_t = __uint16_t; +pub type off_t = __darwin_off_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct timespec { + pub tv_sec: __darwin_time_t, + pub tv_nsec: ::std::os::raw::c_long, +} +pub type fpos_t = __darwin_off_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sbuf { + pub _base: *mut ::std::os::raw::c_uchar, + pub _size: ::std::os::raw::c_int, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sFILEX { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sFILE { + pub _p: *mut ::std::os::raw::c_uchar, + pub _r: ::std::os::raw::c_int, + pub _w: ::std::os::raw::c_int, + pub _flags: ::std::os::raw::c_short, + pub _file: ::std::os::raw::c_short, + pub _bf: __sbuf, + pub _lbfsize: ::std::os::raw::c_int, + pub _cookie: *mut ::std::os::raw::c_void, + pub _close: ::std::option::Option< + unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int, + >, + pub _read: ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut ::std::os::raw::c_void, + arg2: *mut ::std::os::raw::c_char, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >, + pub _seek: ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut ::std::os::raw::c_void, + arg2: fpos_t, + arg3: ::std::os::raw::c_int, + ) -> fpos_t, + >, + pub _write: ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut ::std::os::raw::c_void, + arg2: *const ::std::os::raw::c_char, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >, + pub _ub: __sbuf, + pub _extra: *mut __sFILEX, + pub _ur: ::std::os::raw::c_int, + pub _ubuf: [::std::os::raw::c_uchar; 3usize], + pub _nbuf: [::std::os::raw::c_uchar; 1usize], + pub _lb: __sbuf, + pub _blksize: ::std::os::raw::c_int, + pub _offset: fpos_t, +} +pub type FILE = __sFILE; pub type zend_long = i64; pub type zend_ulong = u64; +pub type zend_off_t = i64; pub type zend_bool = bool; pub type zend_uchar = ::std::os::raw::c_uchar; pub const ZEND_RESULT_CODE_SUCCESS: ZEND_RESULT_CODE = 0; @@ -319,6 +485,19 @@ pub struct _zend_llist { pub traverse_ptr: *mut zend_llist_element, } pub type zend_llist = _zend_llist; +pub type zend_llist_position = *mut zend_llist_element; +extern "C" { + pub fn zend_llist_get_next_ex( + l: *mut zend_llist, + pos: *mut zend_llist_position, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn zend_llist_get_prev_ex( + l: *mut zend_llist, + pos: *mut zend_llist_position, + ) -> *mut ::std::os::raw::c_void; +} pub type zend_string_init_interned_func_t = ::std::option::Option< unsafe extern "C" fn( str_: *const ::std::os::raw::c_char, @@ -460,6 +639,29 @@ pub struct _zend_class_iterator_funcs { pub type zend_class_iterator_funcs = _zend_class_iterator_funcs; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct stat { + pub st_dev: dev_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_ino: __darwin_ino64_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_atimespec: timespec, + pub st_mtimespec: timespec, + pub st_ctimespec: timespec, + pub st_birthtimespec: timespec, + pub st_size: off_t, + pub st_blocks: blkcnt_t, + pub st_blksize: blksize_t, + pub st_flags: __uint32_t, + pub st_gen: __uint32_t, + pub st_lspare: __int32_t, + pub st_qspare: [__int64_t; 2usize], +} +pub type zend_stat_t = stat; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct _zend_serialize_data { _unused: [u8; 0], } @@ -1093,6 +1295,9 @@ pub struct _zend_executor_globals { pub get_gc_buffer: zend_get_gc_buffer, pub reserved: [*mut ::std::os::raw::c_void; 6usize], } +extern "C" { + pub fn zend_is_auto_global(name: *mut zend_string) -> zend_bool; +} pub type zend_module_entry = _zend_module_entry; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -1176,6 +1381,15 @@ pub struct _zend_function_entry { pub flags: u32, } pub type zend_function_entry = _zend_function_entry; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_fcall_info_cache { + pub function_handler: *mut zend_function, + pub calling_scope: *mut zend_class_entry, + pub called_scope: *mut zend_class_entry, + pub object: *mut zend_object, +} +pub type zend_fcall_info_cache = _zend_fcall_info_cache; extern "C" { pub fn zend_register_internal_class_ex( class_entry: *mut zend_class_entry, @@ -1270,6 +1484,374 @@ extern "C" { extern "C" { pub fn php_printf(format: *const ::std::os::raw::c_char, ...) -> usize; } +pub type php_stream = _php_stream; +pub type php_stream_wrapper = _php_stream_wrapper; +pub type php_stream_context = _php_stream_context; +pub type php_stream_filter = _php_stream_filter; +pub type php_stream_notification_func = ::std::option::Option< + unsafe extern "C" fn( + context: *mut php_stream_context, + notifycode: ::std::os::raw::c_int, + severity: ::std::os::raw::c_int, + xmsg: *mut ::std::os::raw::c_char, + xcode: ::std::os::raw::c_int, + bytes_sofar: usize, + bytes_max: usize, + ptr: *mut ::std::os::raw::c_void, + ), +>; +pub type php_stream_notifier = _php_stream_notifier; +#[repr(C)] +pub struct _php_stream_notifier { + pub func: php_stream_notification_func, + pub dtor: ::std::option::Option, + pub ptr: zval, + pub mask: ::std::os::raw::c_int, + pub progress: usize, + pub progress_max: usize, +} +#[repr(C)] +pub struct _php_stream_context { + pub notifier: *mut php_stream_notifier, + pub options: zval, + pub res: *mut zend_resource, +} +pub type php_stream_bucket = _php_stream_bucket; +pub type php_stream_bucket_brigade = _php_stream_bucket_brigade; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_bucket { + pub next: *mut php_stream_bucket, + pub prev: *mut php_stream_bucket, + pub brigade: *mut php_stream_bucket_brigade, + pub buf: *mut ::std::os::raw::c_char, + pub buflen: usize, + pub own_buf: u8, + pub is_persistent: u8, + pub refcount: ::std::os::raw::c_int, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_bucket_brigade { + pub head: *mut php_stream_bucket, + pub tail: *mut php_stream_bucket, +} +pub const php_stream_filter_status_t_PSFS_ERR_FATAL: php_stream_filter_status_t = 0; +pub const php_stream_filter_status_t_PSFS_FEED_ME: php_stream_filter_status_t = 1; +pub const php_stream_filter_status_t_PSFS_PASS_ON: php_stream_filter_status_t = 2; +pub type php_stream_filter_status_t = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_filter_ops { + pub filter: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + thisfilter: *mut php_stream_filter, + buckets_in: *mut php_stream_bucket_brigade, + buckets_out: *mut php_stream_bucket_brigade, + bytes_consumed: *mut usize, + flags: ::std::os::raw::c_int, + ) -> php_stream_filter_status_t, + >, + pub dtor: ::std::option::Option, + pub label: *const ::std::os::raw::c_char, +} +pub type php_stream_filter_ops = _php_stream_filter_ops; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_filter_chain { + pub head: *mut php_stream_filter, + pub tail: *mut php_stream_filter, + pub stream: *mut php_stream, +} +pub type php_stream_filter_chain = _php_stream_filter_chain; +#[repr(C)] +pub struct _php_stream_filter { + pub fops: *const php_stream_filter_ops, + pub abstract_: zval, + pub next: *mut php_stream_filter, + pub prev: *mut php_stream_filter, + pub is_persistent: ::std::os::raw::c_int, + pub chain: *mut php_stream_filter_chain, + pub buffer: php_stream_bucket_brigade, + pub res: *mut zend_resource, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_statbuf { + pub sb: zend_stat_t, +} +pub type php_stream_statbuf = _php_stream_statbuf; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_ops { + pub write: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + buf: *const ::std::os::raw::c_char, + count: usize, + ) -> isize, + >, + pub read: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + buf: *mut ::std::os::raw::c_char, + count: usize, + ) -> isize, + >, + pub close: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + close_handle: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >, + pub flush: ::std::option::Option< + unsafe extern "C" fn(stream: *mut php_stream) -> ::std::os::raw::c_int, + >, + pub label: *const ::std::os::raw::c_char, + pub seek: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + offset: zend_off_t, + whence: ::std::os::raw::c_int, + newoffset: *mut zend_off_t, + ) -> ::std::os::raw::c_int, + >, + pub cast: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + castas: ::std::os::raw::c_int, + ret: *mut *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + pub stat: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + ssb: *mut php_stream_statbuf, + ) -> ::std::os::raw::c_int, + >, + pub set_option: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + option: ::std::os::raw::c_int, + value: ::std::os::raw::c_int, + ptrparam: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, +} +pub type php_stream_ops = _php_stream_ops; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_wrapper_ops { + pub stream_opener: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + filename: *const ::std::os::raw::c_char, + mode: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + opened_path: *mut *mut zend_string, + context: *mut php_stream_context, + ) -> *mut php_stream, + >, + pub stream_closer: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + stream: *mut php_stream, + ) -> ::std::os::raw::c_int, + >, + pub stream_stat: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + stream: *mut php_stream, + ssb: *mut php_stream_statbuf, + ) -> ::std::os::raw::c_int, + >, + pub url_stat: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + flags: ::std::os::raw::c_int, + ssb: *mut php_stream_statbuf, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub dir_opener: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + filename: *const ::std::os::raw::c_char, + mode: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + opened_path: *mut *mut zend_string, + context: *mut php_stream_context, + ) -> *mut php_stream, + >, + pub label: *const ::std::os::raw::c_char, + pub unlink: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub rename: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url_from: *const ::std::os::raw::c_char, + url_to: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub stream_mkdir: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + mode: ::std::os::raw::c_int, + options: ::std::os::raw::c_int, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub stream_rmdir: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub stream_metadata: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + value: *mut ::std::os::raw::c_void, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, +} +pub type php_stream_wrapper_ops = _php_stream_wrapper_ops; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_wrapper { + pub wops: *const php_stream_wrapper_ops, + pub abstract_: *mut ::std::os::raw::c_void, + pub is_url: ::std::os::raw::c_int, +} +#[repr(C)] +pub struct _php_stream { + pub ops: *const php_stream_ops, + pub abstract_: *mut ::std::os::raw::c_void, + pub readfilters: php_stream_filter_chain, + pub writefilters: php_stream_filter_chain, + pub wrapper: *mut php_stream_wrapper, + pub wrapperthis: *mut ::std::os::raw::c_void, + pub wrapperdata: zval, + pub _bitfield_align_1: [u8; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize]>, + pub fgetss_state: u8, + pub mode: [::std::os::raw::c_char; 16usize], + pub flags: u32, + pub res: *mut zend_resource, + pub stdiocast: *mut FILE, + pub orig_path: *mut ::std::os::raw::c_char, + pub ctx: *mut zend_resource, + pub position: zend_off_t, + pub readbuf: *mut ::std::os::raw::c_uchar, + pub readbuflen: usize, + pub readpos: zend_off_t, + pub writepos: zend_off_t, + pub chunk_size: usize, + pub enclosing_stream: *mut _php_stream, +} +impl _php_stream { + #[inline] + pub fn is_persistent(&self) -> u8 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u8) } + } + #[inline] + pub fn set_is_persistent(&mut self, val: u8) { + unsafe { + let val: u8 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn in_free(&self) -> u8 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 2u8) as u8) } + } + #[inline] + pub fn set_in_free(&mut self, val: u8) { + unsafe { + let val: u8 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 2u8, val as u64) + } + } + #[inline] + pub fn eof(&self) -> u8 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u8) } + } + #[inline] + pub fn set_eof(&mut self, val: u8) { + unsafe { + let val: u8 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn __exposed(&self) -> u8 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u8) } + } + #[inline] + pub fn set___exposed(&mut self, val: u8) { + unsafe { + let val: u8 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn fclose_stdiocast(&self) -> u8 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 2u8) as u8) } + } + #[inline] + pub fn set_fclose_stdiocast(&mut self, val: u8) { + unsafe { + let val: u8 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 2u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + is_persistent: u8, + in_free: u8, + eof: u8, + __exposed: u8, + fclose_stdiocast: u8, + ) -> __BindgenBitfieldUnit<[u8; 1usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let is_persistent: u8 = unsafe { ::std::mem::transmute(is_persistent) }; + is_persistent as u64 + }); + __bindgen_bitfield_unit.set(1usize, 2u8, { + let in_free: u8 = unsafe { ::std::mem::transmute(in_free) }; + in_free as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let eof: u8 = unsafe { ::std::mem::transmute(eof) }; + eof as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let __exposed: u8 = unsafe { ::std::mem::transmute(__exposed) }; + __exposed as u64 + }); + __bindgen_bitfield_unit.set(5usize, 2u8, { + let fclose_stdiocast: u8 = unsafe { ::std::mem::transmute(fclose_stdiocast) }; + fclose_stdiocast as u64 + }); + __bindgen_bitfield_unit + } +} pub type php_core_globals = _php_core_globals; #[repr(C)] pub struct _php_core_globals { @@ -1516,3 +2098,82 @@ extern "C" { data: *mut zend_unserialize_data, ) -> ::std::os::raw::c_int; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sapi_header_struct { + pub header: *mut ::std::os::raw::c_char, + pub header_len: usize, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sapi_headers_struct { + pub headers: zend_llist, + pub http_response_code: ::std::os::raw::c_int, + pub send_default_content_type: ::std::os::raw::c_uchar, + pub mimetype: *mut ::std::os::raw::c_char, + pub http_status_line: *mut ::std::os::raw::c_char, +} +pub type sapi_post_entry = _sapi_post_entry; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sapi_request_info { + pub request_method: *const ::std::os::raw::c_char, + pub query_string: *mut ::std::os::raw::c_char, + pub cookie_data: *mut ::std::os::raw::c_char, + pub content_length: zend_long, + pub path_translated: *mut ::std::os::raw::c_char, + pub request_uri: *mut ::std::os::raw::c_char, + pub request_body: *mut _php_stream, + pub content_type: *const ::std::os::raw::c_char, + pub headers_only: zend_bool, + pub no_headers: zend_bool, + pub headers_read: zend_bool, + pub post_entry: *mut sapi_post_entry, + pub content_type_dup: *mut ::std::os::raw::c_char, + pub auth_user: *mut ::std::os::raw::c_char, + pub auth_password: *mut ::std::os::raw::c_char, + pub auth_digest: *mut ::std::os::raw::c_char, + pub argv0: *mut ::std::os::raw::c_char, + pub current_user: *mut ::std::os::raw::c_char, + pub current_user_length: ::std::os::raw::c_int, + pub argc: ::std::os::raw::c_int, + pub argv: *mut *mut ::std::os::raw::c_char, + pub proto_num: ::std::os::raw::c_int, +} +#[repr(C)] +pub struct _sapi_globals_struct { + pub server_context: *mut ::std::os::raw::c_void, + pub request_info: sapi_request_info, + pub sapi_headers: sapi_headers_struct, + pub read_post_bytes: i64, + pub post_read: ::std::os::raw::c_uchar, + pub headers_sent: ::std::os::raw::c_uchar, + pub global_stat: zend_stat_t, + pub default_mimetype: *mut ::std::os::raw::c_char, + pub default_charset: *mut ::std::os::raw::c_char, + pub rfc1867_uploaded_files: *mut HashTable, + pub post_max_size: zend_long, + pub options: ::std::os::raw::c_int, + pub sapi_started: zend_bool, + pub global_request_time: f64, + pub known_post_content_types: HashTable, + pub callback_func: zval, + pub fci_cache: zend_fcall_info_cache, +} +pub type sapi_globals_struct = _sapi_globals_struct; +extern "C" { + pub static mut sapi_globals: sapi_globals_struct; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _sapi_post_entry { + pub content_type: *mut ::std::os::raw::c_char, + pub content_type_len: u32, + pub post_reader: ::std::option::Option, + pub post_handler: ::std::option::Option< + unsafe extern "C" fn( + content_type_dup: *mut ::std::os::raw::c_char, + arg: *mut ::std::os::raw::c_void, + ), + >, +} From 8464a255c44e48b24fb6d700de0d3342b543a731 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 21:04:51 +0200 Subject: [PATCH 019/100] Generate with debug --- docsrs_bindings.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 2943262e83..5dba20b724 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -80,7 +80,7 @@ where } } } -pub const ZEND_DEBUG: u32 = 0; +pub const ZEND_DEBUG: u32 = 1; pub const _ZEND_TYPE_NAME_BIT: u32 = 8388608; pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2; pub const HT_MIN_SIZE: u32 = 8; @@ -455,10 +455,22 @@ pub struct _zend_ast_ref { pub gc: zend_refcounted_h, } extern "C" { - pub fn _emalloc(size: usize) -> *mut ::std::os::raw::c_void; + pub fn _emalloc( + size: usize, + __zend_filename: *const ::std::os::raw::c_char, + __zend_lineno: u32, + __zend_orig_filename: *const ::std::os::raw::c_char, + __zend_orig_lineno: u32, + ) -> *mut ::std::os::raw::c_void; } extern "C" { - pub fn _efree(ptr: *mut ::std::os::raw::c_void); + pub fn _efree( + ptr: *mut ::std::os::raw::c_void, + __zend_filename: *const ::std::os::raw::c_char, + __zend_lineno: u32, + __zend_orig_filename: *const ::std::os::raw::c_char, + __zend_orig_lineno: u32, + ); } extern "C" { pub fn __zend_malloc(len: usize) -> *mut ::std::os::raw::c_void; @@ -1651,6 +1663,11 @@ pub struct _php_stream_wrapper_ops { options: ::std::os::raw::c_int, opened_path: *mut *mut zend_string, context: *mut php_stream_context, + __php_stream_call_depth: ::std::os::raw::c_int, + __zend_filename: *const ::std::os::raw::c_char, + __zend_lineno: u32, + __zend_orig_filename: *const ::std::os::raw::c_char, + __zend_orig_lineno: u32, ) -> *mut php_stream, >, pub stream_closer: ::std::option::Option< @@ -1683,6 +1700,11 @@ pub struct _php_stream_wrapper_ops { options: ::std::os::raw::c_int, opened_path: *mut *mut zend_string, context: *mut php_stream_context, + __php_stream_call_depth: ::std::os::raw::c_int, + __zend_filename: *const ::std::os::raw::c_char, + __zend_lineno: u32, + __zend_orig_filename: *const ::std::os::raw::c_char, + __zend_orig_lineno: u32, ) -> *mut php_stream, >, pub label: *const ::std::os::raw::c_char, @@ -1762,6 +1784,8 @@ pub struct _php_stream { pub readpos: zend_off_t, pub writepos: zend_off_t, pub chunk_size: usize, + pub open_filename: *const ::std::os::raw::c_char, + pub open_lineno: u32, pub enclosing_stream: *mut _php_stream, } impl _php_stream { From 48980a1d52d1f806aea081f2e32a7acc0906afa5 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 13 Jul 2023 21:38:04 +0200 Subject: [PATCH 020/100] Bindings from php 8.2-debug --- docsrs_bindings.rs | 239 +++++++++++++++++++++++++++++---------------- 1 file changed, 155 insertions(+), 84 deletions(-) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 5dba20b724..ce10e9aef0 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -81,7 +81,7 @@ where } } pub const ZEND_DEBUG: u32 = 1; -pub const _ZEND_TYPE_NAME_BIT: u32 = 8388608; +pub const _ZEND_TYPE_NAME_BIT: u32 = 16777216; pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2; pub const HT_MIN_SIZE: u32 = 8; pub const IS_UNDEF: u32 = 0; @@ -100,7 +100,7 @@ pub const IS_CALLABLE: u32 = 12; pub const IS_VOID: u32 = 14; pub const IS_MIXED: u32 = 16; pub const IS_PTR: u32 = 13; -pub const _IS_BOOL: u32 = 17; +pub const _IS_BOOL: u32 = 18; pub const Z_TYPE_FLAGS_SHIFT: u32 = 8; pub const IS_TYPE_REFCOUNTED: u32 = 1; pub const IS_TYPE_COLLECTABLE: u32 = 2; @@ -134,13 +134,11 @@ pub const ZEND_ACC_USE_GUARDS: u32 = 2048; pub const ZEND_ACC_CONSTANTS_UPDATED: u32 = 4096; pub const ZEND_ACC_NO_DYNAMIC_PROPERTIES: u32 = 8192; pub const ZEND_HAS_STATIC_IN_METHODS: u32 = 16384; -pub const ZEND_ACC_PROPERTY_TYPES_RESOLVED: u32 = 32768; -pub const ZEND_ACC_REUSE_GET_ITERATOR: u32 = 65536; pub const ZEND_ACC_RESOLVED_PARENT: u32 = 131072; pub const ZEND_ACC_RESOLVED_INTERFACES: u32 = 262144; pub const ZEND_ACC_UNRESOLVED_VARIANCE: u32 = 524288; pub const ZEND_ACC_NEARLY_LINKED: u32 = 1048576; -pub const ZEND_ACC_HAS_UNLINKED_USES: u32 = 2097152; +pub const ZEND_ACC_NOT_SERIALIZABLE: u32 = 536870912; pub const ZEND_ACC_DEPRECATED: u32 = 2048; pub const ZEND_ACC_RETURN_REFERENCE: u32 = 4096; pub const ZEND_ACC_HAS_RETURN_TYPE: u32 = 8192; @@ -159,9 +157,9 @@ pub const ZEND_ACC_DONE_PASS_TWO: u32 = 33554432; pub const ZEND_ACC_HEAP_RT_CACHE: u32 = 67108864; pub const ZEND_ACC_STRICT_TYPES: u32 = 2147483648; pub const ZEND_ISEMPTY: u32 = 1; -pub const _ZEND_SEND_MODE_SHIFT: u32 = 24; -pub const _ZEND_IS_VARIADIC_BIT: u32 = 67108864; -pub const ZEND_MODULE_API_NO: u32 = 20200930; +pub const _ZEND_SEND_MODE_SHIFT: u32 = 25; +pub const _ZEND_IS_VARIADIC_BIT: u32 = 134217728; +pub const ZEND_MODULE_API_NO: u32 = 20220829; pub const USING_ZTS: u32 = 0; pub const MAY_BE_BOOL: u32 = 12; pub const MAY_BE_ANY: u32 = 1022; @@ -264,7 +262,6 @@ pub type FILE = __sFILE; pub type zend_long = i64; pub type zend_ulong = u64; pub type zend_off_t = i64; -pub type zend_bool = bool; pub type zend_uchar = ::std::os::raw::c_uchar; pub const ZEND_RESULT_CODE_SUCCESS: ZEND_RESULT_CODE = 0; pub const ZEND_RESULT_CODE_FAILURE: ZEND_RESULT_CODE = -1; @@ -348,7 +345,6 @@ pub union _zval_struct__bindgen_ty_2 { pub num_args: u32, pub fe_pos: u32, pub fe_iter_idx: u32, - pub access_flags: u32, pub property_guard: u32, pub constant_flags: u32, pub extra: u32, @@ -390,7 +386,7 @@ pub struct _zend_array { pub gc: zend_refcounted_h, pub u: _zend_array__bindgen_ty_1, pub nTableMask: u32, - pub arData: *mut Bucket, + pub __bindgen_anon_1: _zend_array__bindgen_ty_2, pub nNumUsed: u32, pub nNumOfElements: u32, pub nTableSize: u32, @@ -412,6 +408,13 @@ pub struct _zend_array__bindgen_ty_1__bindgen_ty_1 { pub nIteratorsCount: zend_uchar, pub _unused2: zend_uchar, } +#[repr(C)] +#[derive(Copy, Clone)] +pub union _zend_array__bindgen_ty_2 { + pub arHash: *mut u32, + pub arData: *mut Bucket, + pub arPacked: *mut zval, +} pub type HashPosition = u32; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -433,7 +436,7 @@ pub struct _zend_object { #[derive(Copy, Clone)] pub struct _zend_resource { pub gc: zend_refcounted_h, - pub handle: ::std::os::raw::c_int, + pub handle: zend_long, pub type_: ::std::os::raw::c_int, pub ptr: *mut ::std::os::raw::c_void, } @@ -568,7 +571,7 @@ extern "C" { pub fn zend_hash_get_current_key_zval_ex( ht: *const HashTable, key: *mut zval, - pos: *mut HashPosition, + pos: *const HashPosition, ); } extern "C" { @@ -651,6 +654,15 @@ pub struct _zend_class_iterator_funcs { pub type zend_class_iterator_funcs = _zend_class_iterator_funcs; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct _zend_class_arrayaccess_funcs { + pub zf_offsetget: *mut zend_function, + pub zf_offsetexists: *mut zend_function, + pub zf_offsetset: *mut zend_function, + pub zf_offsetunset: *mut zend_function, +} +pub type zend_class_arrayaccess_funcs = _zend_class_arrayaccess_funcs; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct stat { pub st_dev: dev_t, pub st_mode: mode_t, @@ -717,6 +729,44 @@ pub struct _zend_trait_alias { } pub type zend_trait_alias = _zend_trait_alias; #[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_class_mutable_data { + pub default_properties_table: *mut zval, + pub constants_table: *mut HashTable, + pub ce_flags: u32, + pub backed_enum_table: *mut HashTable, +} +pub type zend_class_mutable_data = _zend_class_mutable_data; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_class_dependency { + pub name: *mut zend_string, + pub ce: *mut zend_class_entry, +} +pub type zend_class_dependency = _zend_class_dependency; +pub type zend_inheritance_cache_entry = _zend_inheritance_cache_entry; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_error_info { + pub type_: ::std::os::raw::c_int, + pub lineno: u32, + pub filename: *mut zend_string, + pub message: *mut zend_string, +} +pub type zend_error_info = _zend_error_info; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_inheritance_cache_entry { + pub next: *mut zend_inheritance_cache_entry, + pub ce: *mut zend_class_entry, + pub parent: *mut zend_class_entry, + pub dependencies: *mut zend_class_dependency, + pub dependencies_count: u32, + pub num_warnings: u32, + pub warnings: *mut *mut zend_error_info, + pub traits_and_interfaces: [*mut zend_class_entry; 1usize], +} +#[repr(C)] pub struct _zend_class_entry { pub type_: ::std::os::raw::c_char, pub name: *mut zend_string, @@ -727,10 +777,12 @@ pub struct _zend_class_entry { pub default_static_members_count: ::std::os::raw::c_int, pub default_properties_table: *mut zval, pub default_static_members_table: *mut zval, - pub static_members_table__ptr: *mut *mut zval, + pub static_members_table__ptr: *mut zval, pub function_table: HashTable, pub properties_info: HashTable, pub constants_table: HashTable, + pub mutable_data__ptr: *mut zend_class_mutable_data, + pub inheritance_cache: *mut zend_inheritance_cache_entry, pub properties_info_table: *mut *mut _zend_property_info, pub constructor: *mut zend_function, pub destructor: *mut zend_function, @@ -746,6 +798,7 @@ pub struct _zend_class_entry { pub __serialize: *mut zend_function, pub __unserialize: *mut zend_function, pub iterator_funcs_ptr: *mut zend_class_iterator_funcs, + pub arrayaccess_funcs_ptr: *mut zend_class_arrayaccess_funcs, pub __bindgen_anon_2: _zend_class_entry__bindgen_ty_2, pub get_iterator: ::std::option::Option< unsafe extern "C" fn( @@ -784,6 +837,8 @@ pub struct _zend_class_entry { pub trait_aliases: *mut *mut zend_trait_alias, pub trait_precedences: *mut *mut zend_trait_precedence, pub attributes: *mut HashTable, + pub enum_backing_type: u32, + pub backed_enum_table: *mut HashTable, pub info: _zend_class_entry__bindgen_ty_4, } #[repr(C)] @@ -925,10 +980,10 @@ pub type zend_object_get_method_t = ::std::option::Option< >; pub type zend_object_get_constructor_t = ::std::option::Option *mut zend_function>; -pub type zend_object_dtor_obj_t = - ::std::option::Option; pub type zend_object_free_obj_t = ::std::option::Option; +pub type zend_object_dtor_obj_t = + ::std::option::Option; pub type zend_object_clone_obj_t = ::std::option::Option *mut zend_object>; pub type zend_object_get_class_name_t = @@ -941,10 +996,10 @@ pub type zend_object_cast_t = ::std::option::Option< readobj: *mut zend_object, retval: *mut zval, type_: ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int, + ) -> zend_result, >; pub type zend_object_count_elements_t = ::std::option::Option< - unsafe extern "C" fn(object: *mut zend_object, count: *mut zend_long) -> ::std::os::raw::c_int, + unsafe extern "C" fn(object: *mut zend_object, count: *mut zend_long) -> zend_result, >; pub type zend_object_get_closure_t = ::std::option::Option< unsafe extern "C" fn( @@ -952,8 +1007,8 @@ pub type zend_object_get_closure_t = ::std::option::Option< ce_ptr: *mut *mut zend_class_entry, fptr_ptr: *mut *mut zend_function, obj_ptr: *mut *mut zend_object, - check_only: zend_bool, - ) -> ::std::os::raw::c_int, + check_only: bool, + ) -> zend_result, >; pub type zend_object_get_gc_t = ::std::option::Option< unsafe extern "C" fn( @@ -968,7 +1023,7 @@ pub type zend_object_do_operation_t = ::std::option::Option< result: *mut zval, op1: *mut zval, op2: *mut zval, - ) -> ::std::os::raw::c_int, + ) -> zend_result, >; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -1031,7 +1086,7 @@ extern "C" { ) -> ::std::os::raw::c_int; } extern "C" { - pub fn zend_is_identical(op1: *mut zval, op2: *mut zval) -> zend_bool; + pub fn zend_is_identical(op1: *mut zval, op2: *mut zval) -> bool; } extern "C" { pub fn zend_is_true(op: *mut zval) -> ::std::os::raw::c_int; @@ -1119,13 +1174,13 @@ pub struct _zend_op_array { pub required_num_args: u32, pub arg_info: *mut zend_arg_info, pub attributes: *mut HashTable, + pub T: u32, + pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, pub cache_size: ::std::os::raw::c_int, pub last_var: ::std::os::raw::c_int, - pub T: u32, pub last: u32, pub opcodes: *mut zend_op, - pub run_time_cache__ptr: *mut *mut *mut ::std::os::raw::c_void, - pub static_variables_ptr__ptr: *mut *mut HashTable, + pub static_variables_ptr__ptr: *mut HashTable, pub static_variables: *mut HashTable, pub vars: *mut *mut zend_string, pub refcount: *mut u32, @@ -1138,7 +1193,9 @@ pub struct _zend_op_array { pub line_end: u32, pub doc_comment: *mut zend_string, pub last_literal: ::std::os::raw::c_int, + pub num_dynamic_func_defs: u32, pub literals: *mut zval, + pub dynamic_func_defs: *mut *mut zend_op_array, pub reserved: [*mut ::std::os::raw::c_void; 6usize], } pub type zif_handler = ::std::option::Option< @@ -1157,6 +1214,8 @@ pub struct _zend_internal_function { pub required_num_args: u32, pub arg_info: *mut zend_internal_arg_info, pub attributes: *mut HashTable, + pub T: u32, + pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, pub handler: zif_handler, pub module: *mut _zend_module_entry, pub reserved: [*mut ::std::os::raw::c_void; 6usize], @@ -1184,6 +1243,8 @@ pub struct _zend_function__bindgen_ty_1 { pub required_num_args: u32, pub arg_info: *mut zend_arg_info, pub attributes: *mut HashTable, + pub T: u32, + pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, } #[repr(C)] pub struct _zend_execute_data { @@ -1204,6 +1265,12 @@ extern "C" { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct zend_atomic_bool_s { + pub value: u8, +} +pub type zend_atomic_bool = zend_atomic_bool_s; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct _zend_stack { pub size: ::std::os::raw::c_int, pub top: ::std::os::raw::c_int, @@ -1238,6 +1305,18 @@ extern "C" { pub type zend_vm_stack = *mut _zend_vm_stack; pub type zend_ini_entry = _zend_ini_entry; #[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_fiber_context { + _unused: [u8; 0], +} +pub type zend_fiber_context = _zend_fiber_context; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_fiber { + _unused: [u8; 0], +} +pub type zend_fiber = _zend_fiber; +#[repr(C)] pub struct _zend_executor_globals { pub uninitialized_zval: zval, pub error_zval: zval, @@ -1265,10 +1344,10 @@ pub struct _zend_executor_globals { pub persistent_functions_count: u32, pub persistent_classes_count: u32, pub in_autoload: *mut HashTable, - pub full_tables_cleanup: zend_bool, - pub no_extensions: zend_bool, - pub vm_interrupt: zend_bool, - pub timed_out: zend_bool, + pub full_tables_cleanup: bool, + pub no_extensions: bool, + pub vm_interrupt: zend_atomic_bool, + pub timed_out: zend_atomic_bool, pub hard_timeout: zend_long, pub regular_list: HashTable, pub persistent_list: HashTable, @@ -1281,7 +1360,7 @@ pub struct _zend_executor_globals { pub error_handling: zend_error_handling_t, pub exception_class: *mut zend_class_entry, pub timeout_seconds: zend_long, - pub lambda_count: ::std::os::raw::c_int, + pub capture_warnings_during_sccp: ::std::os::raw::c_int, pub ini_directives: *mut HashTable, pub modified_ini_directives: *mut HashTable, pub error_reporting_ini_entry: *mut zend_ini_entry, @@ -1291,7 +1370,7 @@ pub struct _zend_executor_globals { pub opline_before_exception: *const zend_op, pub exception_op: [zend_op; 3usize], pub current_module: *mut _zend_module_entry, - pub active: zend_bool, + pub active: bool, pub flags: zend_uchar, pub assertions: zend_long, pub ht_iterators_count: u32, @@ -1302,13 +1381,22 @@ pub struct _zend_executor_globals { pub trampoline: zend_function, pub call_trampoline_op: zend_op, pub weakrefs: HashTable, - pub exception_ignore_args: zend_bool, + pub exception_ignore_args: bool, pub exception_string_param_max_len: zend_long, pub get_gc_buffer: zend_get_gc_buffer, + pub main_fiber_context: *mut zend_fiber_context, + pub current_fiber_context: *mut zend_fiber_context, + pub active_fiber: *mut zend_fiber, + pub fiber_stack_size: zend_long, + pub record_errors: bool, + pub num_errors: u32, + pub errors: *mut *mut zend_error_info, + pub filename_override: *mut zend_string, + pub lineno_override: zend_long, pub reserved: [*mut ::std::os::raw::c_void; 6usize], } extern "C" { - pub fn zend_is_auto_global(name: *mut zend_string) -> zend_bool; + pub fn zend_is_auto_global(name: *mut zend_string) -> bool; } pub type zend_module_entry = _zend_module_entry; #[repr(C)] @@ -1413,7 +1501,7 @@ extern "C" { callable: *mut zval, check_flags: u32, callable_name: *mut *mut zend_string, - ) -> zend_bool; + ) -> bool; } extern "C" { pub fn zend_declare_property( @@ -1771,7 +1859,6 @@ pub struct _php_stream { pub wrapperdata: zval, pub _bitfield_align_1: [u8; 0], pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize]>, - pub fgetss_state: u8, pub mode: [::std::os::raw::c_char; 16usize], pub flags: u32, pub res: *mut zend_resource, @@ -1879,21 +1966,20 @@ impl _php_stream { pub type php_core_globals = _php_core_globals; #[repr(C)] pub struct _php_core_globals { - pub implicit_flush: zend_bool, + pub implicit_flush: bool, pub output_buffering: zend_long, - pub enable_dl: zend_bool, + pub enable_dl: bool, pub output_handler: *mut ::std::os::raw::c_char, pub unserialize_callback_func: *mut ::std::os::raw::c_char, pub serialize_precision: zend_long, pub memory_limit: zend_long, pub max_input_time: zend_long, pub display_errors: zend_uchar, - pub display_startup_errors: zend_bool, - pub log_errors: zend_bool, - pub log_errors_max_len: zend_long, - pub ignore_repeated_errors: zend_bool, - pub ignore_repeated_source: zend_bool, - pub report_memleaks: zend_bool, + pub display_startup_errors: bool, + pub log_errors: bool, + pub ignore_repeated_errors: bool, + pub ignore_repeated_source: bool, + pub report_memleaks: bool, pub error_log: *mut ::std::os::raw::c_char, pub doc_root: *mut ::std::os::raw::c_char, pub user_dir: *mut ::std::os::raw::c_char, @@ -1915,45 +2001,47 @@ pub struct _php_core_globals { pub variables_order: *mut ::std::os::raw::c_char, pub rfc1867_protected_variables: HashTable, pub connection_status: ::std::os::raw::c_short, - pub ignore_user_abort: zend_bool, + pub ignore_user_abort: bool, pub header_is_being_sent: ::std::os::raw::c_uchar, pub tick_functions: zend_llist, pub http_globals: [zval; 6usize], - pub expose_php: zend_bool, - pub register_argc_argv: zend_bool, - pub auto_globals_jit: zend_bool, + pub expose_php: bool, + pub register_argc_argv: bool, + pub auto_globals_jit: bool, pub docref_root: *mut ::std::os::raw::c_char, pub docref_ext: *mut ::std::os::raw::c_char, - pub html_errors: zend_bool, - pub xmlrpc_errors: zend_bool, + pub html_errors: bool, + pub xmlrpc_errors: bool, pub xmlrpc_error_number: zend_long, - pub activated_auto_globals: [zend_bool; 8usize], - pub modules_activated: zend_bool, - pub file_uploads: zend_bool, - pub during_request_startup: zend_bool, - pub allow_url_fopen: zend_bool, - pub enable_post_data_reading: zend_bool, - pub report_zend_debug: zend_bool, + pub activated_auto_globals: [bool; 8usize], + pub modules_activated: bool, + pub file_uploads: bool, + pub during_request_startup: bool, + pub allow_url_fopen: bool, + pub enable_post_data_reading: bool, + pub report_zend_debug: bool, pub last_error_type: ::std::os::raw::c_int, pub last_error_message: *mut zend_string, - pub last_error_file: *mut ::std::os::raw::c_char, + pub last_error_file: *mut zend_string, pub last_error_lineno: ::std::os::raw::c_int, pub php_sys_temp_dir: *mut ::std::os::raw::c_char, pub disable_classes: *mut ::std::os::raw::c_char, - pub allow_url_include: zend_bool, + pub allow_url_include: bool, pub max_input_nesting_level: zend_long, pub max_input_vars: zend_long, - pub in_user_include: zend_bool, + pub in_user_include: bool, pub user_ini_filename: *mut ::std::os::raw::c_char, pub user_ini_cache_ttl: zend_long, pub request_order: *mut ::std::os::raw::c_char, - pub mail_x_header: zend_bool, + pub mail_x_header: bool, + pub mail_mixed_lf_and_crlf: bool, pub mail_log: *mut ::std::os::raw::c_char, - pub in_error_log: zend_bool, + pub in_error_log: bool, pub syslog_facility: zend_long, pub syslog_ident: *mut ::std::os::raw::c_char, - pub have_called_openlog: zend_bool, + pub have_called_openlog: bool, pub syslog_filter: zend_long, + pub error_log_mode: zend_long, } extern "C" { pub static mut core_globals: _php_core_globals; @@ -1996,7 +2084,7 @@ extern "C" { pub fn zend_register_bool_constant( name: *const ::std::os::raw::c_char, name_len: usize, - bval: zend_bool, + bval: bool, flags: ::std::os::raw::c_int, module_number: ::std::os::raw::c_int, ); @@ -2105,23 +2193,6 @@ extern "C" { extern "C" { pub static mut zend_ce_stringable: *mut zend_class_entry; } -extern "C" { - pub fn zend_class_serialize_deny( - object: *mut zval, - buffer: *mut *mut ::std::os::raw::c_uchar, - buf_len: *mut usize, - data: *mut zend_serialize_data, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn zend_class_unserialize_deny( - object: *mut zval, - ce: *mut zend_class_entry, - buf: *const ::std::os::raw::c_uchar, - buf_len: usize, - data: *mut zend_unserialize_data, - ) -> ::std::os::raw::c_int; -} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct sapi_header_struct { @@ -2149,9 +2220,9 @@ pub struct sapi_request_info { pub request_uri: *mut ::std::os::raw::c_char, pub request_body: *mut _php_stream, pub content_type: *const ::std::os::raw::c_char, - pub headers_only: zend_bool, - pub no_headers: zend_bool, - pub headers_read: zend_bool, + pub headers_only: bool, + pub no_headers: bool, + pub headers_read: bool, pub post_entry: *mut sapi_post_entry, pub content_type_dup: *mut ::std::os::raw::c_char, pub auth_user: *mut ::std::os::raw::c_char, @@ -2178,7 +2249,7 @@ pub struct _sapi_globals_struct { pub rfc1867_uploaded_files: *mut HashTable, pub post_max_size: zend_long, pub options: ::std::os::raw::c_int, - pub sapi_started: zend_bool, + pub sapi_started: bool, pub global_request_time: f64, pub known_post_content_types: HashTable, pub callback_func: zval, From 5acdfe67c2d66803cf412d891584be2c4d79eb6f Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 10:51:20 +0200 Subject: [PATCH 021/100] Add FileGlobals too --- allowed_bindings.rs | 2 ++ src/wrapper.h | 1 + src/zend/globals.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/zend/mod.rs | 1 + 4 files changed, 45 insertions(+) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 98248f1171..8d12aa03f6 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -234,6 +234,8 @@ bind! { executor_globals_offset, core_globals_offset, sapi_globals_offset, + php_file_globals, + file_globals, TRACK_VARS_POST, TRACK_VARS_GET, TRACK_VARS_COOKIE, diff --git a/src/wrapper.h b/src/wrapper.h index cd8542cdc8..e8d4e3bec6 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -17,6 +17,7 @@ #include "php.h" #include "ext/standard/info.h" +#include "ext/standard/file.h" #include "zend_exceptions.h" #include "zend_inheritance.h" #include "zend_interfaces.h" diff --git a/src/zend/globals.rs b/src/zend/globals.rs index f7a1e1b68a..b3cdccad29 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -12,6 +12,7 @@ use crate::ffi::{ ext_php_rs_sapi_globals, php_core_globals, sapi_globals_struct, sapi_header_struct, sapi_headers_struct, sapi_request_info, zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, + php_file_globals, file_globals }; use crate::types::{ZendHashTable, ZendObject, ZendStr}; @@ -358,6 +359,45 @@ impl SapiRequestInfo { } } +/// Stores global variables used in the SAPI. +pub type FileGlobals = php_file_globals; + +impl FileGlobals { + /// Returns a reference to the PHP process globals. + /// + /// The process globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get() -> GlobalReadGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { &file_globals }; + let guard = FILE_GLOBALS_LOCK.read(); + GlobalReadGuard { globals, guard } + } + + /// Returns a mutable reference to the PHP executor globals. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get_mut() -> GlobalWriteGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { &mut file_globals }; + let guard = SAPI_GLOBALS_LOCK.write(); + GlobalWriteGuard { globals, guard } + } + + pub fn stream_wrappers(&self) -> Option<&'static ZendHashTable> { + unsafe { self.stream_wrappers.as_ref() } + } +} + /// Executor globals rwlock. /// /// PHP provides no indication if the executor globals are being accessed so @@ -365,6 +405,7 @@ impl SapiRequestInfo { static GLOBALS_LOCK: RwLock<()> = const_rwlock(()); static PROCESS_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); static SAPI_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); +static FILE_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); /// Wrapper guard that contains a reference to a given type `T`. Dropping a /// guard releases the lock on the relevant rwlock. diff --git a/src/zend/mod.rs b/src/zend/mod.rs index dfaa0ef9e0..ddd815e0b5 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -20,6 +20,7 @@ pub use function::FunctionEntry; pub use globals::ExecutorGlobals; pub use globals::ProcessGlobals; pub use globals::SapiGlobals; +pub use globals::FileGlobals; pub use handlers::ZendObjectHandlers; pub use linked_list::ZendLinkedList; pub use module::ModuleEntry; From 1c550852abd59af8886827ae131511330aaaa5ca Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 10:59:32 +0200 Subject: [PATCH 022/100] Add StreamWrapper API This allows extensions to register and unregister custom stream wrappers --- allowed_bindings.rs | 10 ++++- docsrs_bindings.rs | 30 +++++++++++++ src/zend/ex.rs | 14 +++++- src/zend/function.rs | 10 ++++- src/zend/mod.rs | 2 + src/zend/streams.rs | 100 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 src/zend/streams.rs diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 85784801cb..5b96e76080 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -263,5 +263,13 @@ bind! { sapi_header_struct, zend_is_auto_global, zend_llist_get_next_ex, - zend_llist_get_prev_ex + zend_llist_get_prev_ex, + php_register_url_stream_wrapper, + php_stream_locate_url_wrapper, + php_unregister_url_stream_wrapper, + php_unregister_url_stream_wrapper_volatile, + php_register_url_stream_wrapper_volatile, + php_stream_wrapper, + zend_llist_get_prev_ex, + php_stream_stdio_ops } diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 22817acf8f..5a5fbd33ee 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -1986,6 +1986,36 @@ impl _php_stream { __bindgen_bitfield_unit } } +extern "C" { + pub static mut php_stream_stdio_ops: php_stream_ops; +} +extern "C" { + pub fn php_register_url_stream_wrapper( + protocol: *const ::std::os::raw::c_char, + wrapper: *const php_stream_wrapper, + ) -> zend_result; +} +extern "C" { + pub fn php_unregister_url_stream_wrapper( + protocol: *const ::std::os::raw::c_char, + ) -> zend_result; +} +extern "C" { + pub fn php_register_url_stream_wrapper_volatile( + protocol: *mut zend_string, + wrapper: *mut php_stream_wrapper, + ) -> zend_result; +} +extern "C" { + pub fn php_unregister_url_stream_wrapper_volatile(protocol: *mut zend_string) -> zend_result; +} +extern "C" { + pub fn php_stream_locate_url_wrapper( + path: *const ::std::os::raw::c_char, + path_for_open: *mut *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + ) -> *mut php_stream_wrapper; +} pub type php_core_globals = _php_core_globals; #[repr(C)] pub struct _php_core_globals { diff --git a/src/zend/ex.rs b/src/zend/ex.rs index dada00e0c8..a90a2f4f9a 100644 --- a/src/zend/ex.rs +++ b/src/zend/ex.rs @@ -1,4 +1,4 @@ -use crate::ffi::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}; +use crate::ffi::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK, _zend_function}; use crate::{ args::ArgParser, @@ -231,6 +231,18 @@ impl ExecuteData { let size = std::mem::size_of::(); ((size as isize) + ZEND_MM_ALIGNMENT as isize - 1) & ZEND_MM_ALIGNMENT_MASK as isize } + + pub fn previous(&self) -> Option<&Self> { + unsafe { + self.prev_execute_data.as_ref() + } + } + + pub fn function(&self) -> Option<&_zend_function> { + unsafe { + self.func.as_ref() + } + } } #[cfg(test)] diff --git a/src/zend/function.rs b/src/zend/function.rs index ba889d775f..51d3e8c79c 100644 --- a/src/zend/function.rs +++ b/src/zend/function.rs @@ -2,7 +2,7 @@ use std::{fmt::Debug, os::raw::c_char, ptr}; -use crate::ffi::zend_function_entry; +use crate::{ffi::{zend_function_entry, zend_function}, flags::FunctionType}; /// A Zend function entry. pub type FunctionEntry = zend_function_entry; @@ -36,3 +36,11 @@ impl FunctionEntry { Box::into_raw(Box::new(self)) } } + +pub type Function = zend_function; + +impl Function { + pub fn type_(&self) -> FunctionType { + FunctionType::from(unsafe { self.type_ }) + } +} diff --git a/src/zend/mod.rs b/src/zend/mod.rs index ddd815e0b5..d2ff753928 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -9,6 +9,7 @@ mod globals; mod handlers; mod linked_list; mod module; +mod streams; use crate::{error::Result, ffi::php_printf}; use std::ffi::CString; @@ -24,6 +25,7 @@ pub use globals::FileGlobals; pub use handlers::ZendObjectHandlers; pub use linked_list::ZendLinkedList; pub use module::ModuleEntry; +pub use streams::*; // Used as the format string for `php_printf`. const FORMAT_STR: &[u8] = b"%s\0"; diff --git a/src/zend/streams.rs b/src/zend/streams.rs new file mode 100644 index 0000000000..1045a0c03c --- /dev/null +++ b/src/zend/streams.rs @@ -0,0 +1,100 @@ +use std::ptr::{self, NonNull}; + +use crate::{ + ffi::{ + php_register_url_stream_wrapper, php_register_url_stream_wrapper_volatile, php_stream, + php_stream_context, php_stream_locate_url_wrapper, php_stream_wrapper, + php_stream_wrapper_ops, php_unregister_url_stream_wrapper, + php_unregister_url_stream_wrapper_volatile, zend_string, + }, + types::ZendStr, +}; + +pub type StreamWrapper = php_stream_wrapper; + +pub type StreamOpener = unsafe extern "C" fn( + *mut StreamWrapper, + *const std::ffi::c_char, + *const std::ffi::c_char, + i32, + *mut *mut zend_string, + *mut php_stream_context, + i32, + *const std::ffi::c_char, + u32, + *const std::ffi::c_char, + u32, +) -> *mut Stream; + +impl StreamWrapper { + pub fn get(name: &str) -> Option<&Self> { + unsafe { + let result = php_stream_locate_url_wrapper(name.as_ptr().cast(), ptr::null_mut(), 0); + Some(NonNull::new(result)?.as_ref()) + } + } + + pub fn get_mut(name: &str) -> Option<&mut Self> { + unsafe { + let result = php_stream_locate_url_wrapper(name.as_ptr().cast(), ptr::null_mut(), 0); + Some(NonNull::new(result)?.as_mut()) + } + } + + pub fn register(self, name: &str) -> Result { + // We have to convert it to a static so owned streamwrapper doesn't get dropped. + let copy = Box::new(self); + let copy = Box::leak(copy); + let name = std::ffi::CString::new(name).unwrap(); + let result = unsafe { php_register_url_stream_wrapper(name.as_ptr(), copy) }; + if result == 0 { + Ok(*copy) + } else { + Err(()) + } + } + + pub fn register_volatile(self, name: &str) -> Result { + // We have to convert it to a static so owned streamwrapper doesn't get dropped. + let copy = Box::new(self); + let copy = Box::leak(copy); + let name = ZendStr::new(name, false); + let result = + unsafe { php_register_url_stream_wrapper_volatile((*name).as_ptr() as _, copy) }; + if result == 0 { + Ok(*copy) + } else { + Err(()) + } + } + + pub fn unregister(name: &str) -> Result<(), ()> { + let name = std::ffi::CString::new(name).unwrap(); + match unsafe { php_unregister_url_stream_wrapper(name.as_ptr()) } { + 0 => Ok(()), + _ => Err(()), + } + } + + pub fn unregister_volatile(name: &str) -> Result<(), ()> { + let name = ZendStr::new(name, false); + match unsafe { php_unregister_url_stream_wrapper_volatile((*name).as_ptr() as _) } { + 0 => Ok(()), + _ => Err(()), + } + } + + pub fn wops(&self) -> &php_stream_wrapper_ops { + unsafe { &*self.wops } + } + + pub fn wops_mut(&mut self) -> &mut php_stream_wrapper_ops { + unsafe { &mut *(self.wops as *mut php_stream_wrapper_ops) } + } +} + +pub type Stream = php_stream; + +pub type StreamWrapperOps = php_stream_wrapper_ops; + +impl StreamWrapperOps {} From a13829623aecfe0f8d2a48d17a73f8948ae91c8f Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 11:00:46 +0200 Subject: [PATCH 023/100] Add php_var include for auto globals --- src/wrapper.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wrapper.h b/src/wrapper.h index e8d4e3bec6..62e6fa9e64 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -17,6 +17,7 @@ #include "php.h" #include "ext/standard/info.h" +#include "ext/standard/php_var.h" #include "ext/standard/file.h" #include "zend_exceptions.h" #include "zend_inheritance.h" From cb89c7b4d895a8f4d6b09ebbc610a6829d4b2bed Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 11:02:56 +0200 Subject: [PATCH 024/100] Revert change to function --- src/zend/function.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/zend/function.rs b/src/zend/function.rs index 51d3e8c79c..ba889d775f 100644 --- a/src/zend/function.rs +++ b/src/zend/function.rs @@ -2,7 +2,7 @@ use std::{fmt::Debug, os::raw::c_char, ptr}; -use crate::{ffi::{zend_function_entry, zend_function}, flags::FunctionType}; +use crate::ffi::zend_function_entry; /// A Zend function entry. pub type FunctionEntry = zend_function_entry; @@ -36,11 +36,3 @@ impl FunctionEntry { Box::into_raw(Box::new(self)) } } - -pub type Function = zend_function; - -impl Function { - pub fn type_(&self) -> FunctionType { - FunctionType::from(unsafe { self.type_ }) - } -} From 06e190e08a55668ed0838b5bed7ec864fd97e82e Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 11:34:42 +0200 Subject: [PATCH 025/100] fmt --- src/builders/module.rs | 3 ++- src/zend/globals.rs | 8 ++++---- src/zend/mod.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/builders/module.rs b/src/builders/module.rs index 01bc9e4eb8..ee2b219fdd 100644 --- a/src/builders/module.rs +++ b/src/builders/module.rs @@ -131,7 +131,8 @@ impl ModuleBuilder { /// This function can be useful if you need to do any final cleanup at the /// very end of a request, after all other resources have been released. For /// example, if your extension creates any persistent resources that last - /// beyond a single request, you could use this function to clean those up. # Arguments + /// beyond a single request, you could use this function to clean those up. + /// # Arguments /// /// * `func` - The function to be called when shutdown is requested. pub fn post_deactivate_function(mut self, func: extern "C" fn() -> i32) -> Self { diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 6e9fa94a84..fee0d09fdc 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -9,10 +9,10 @@ use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; use crate::ffi::{ _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_process_globals, - ext_php_rs_sapi_globals, php_core_globals, sapi_globals_struct, sapi_header_struct, - sapi_headers_struct, sapi_request_info, zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, - TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, - php_file_globals, file_globals + ext_php_rs_sapi_globals, file_globals, php_core_globals, php_file_globals, sapi_globals_struct, + sapi_header_struct, sapi_headers_struct, sapi_request_info, zend_is_auto_global, + TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, + TRACK_VARS_REQUEST, TRACK_VARS_SERVER, }; use crate::types::{ZendHashTable, ZendObject, ZendStr}; diff --git a/src/zend/mod.rs b/src/zend/mod.rs index ddd815e0b5..9426aae571 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -18,9 +18,9 @@ pub use class::ClassEntry; pub use ex::ExecuteData; pub use function::FunctionEntry; pub use globals::ExecutorGlobals; +pub use globals::FileGlobals; pub use globals::ProcessGlobals; pub use globals::SapiGlobals; -pub use globals::FileGlobals; pub use handlers::ZendObjectHandlers; pub use linked_list::ZendLinkedList; pub use module::ModuleEntry; From 52525f249f84ae0ecb85abf0dff3d0fd6533fa46 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 11:36:08 +0200 Subject: [PATCH 026/100] Fix allowed bindings duplication --- allowed_bindings.rs | 1 - src/zend/ex.rs | 10 +++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 5b96e76080..d67347a10c 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -270,6 +270,5 @@ bind! { php_unregister_url_stream_wrapper_volatile, php_register_url_stream_wrapper_volatile, php_stream_wrapper, - zend_llist_get_prev_ex, php_stream_stdio_ops } diff --git a/src/zend/ex.rs b/src/zend/ex.rs index a90a2f4f9a..fac1b939d9 100644 --- a/src/zend/ex.rs +++ b/src/zend/ex.rs @@ -1,4 +1,4 @@ -use crate::ffi::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK, _zend_function}; +use crate::ffi::{_zend_function, zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}; use crate::{ args::ArgParser, @@ -233,15 +233,11 @@ impl ExecuteData { } pub fn previous(&self) -> Option<&Self> { - unsafe { - self.prev_execute_data.as_ref() - } + unsafe { self.prev_execute_data.as_ref() } } pub fn function(&self) -> Option<&_zend_function> { - unsafe { - self.func.as_ref() - } + unsafe { self.func.as_ref() } } } From a616c4b4db95a0c635c07ea71c37e7eeec9278f5 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 12:27:11 +0200 Subject: [PATCH 027/100] ZTS support for file globals --- src/ffi.rs | 1 + src/wrapper.c | 9 +++++++++ src/zend/globals.rs | 15 ++++++++------- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index cd3a81c9cb..465b509504 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -28,6 +28,7 @@ extern "C" { pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals; pub fn ext_php_rs_process_globals() -> *mut php_core_globals; pub fn ext_php_rs_sapi_globals() -> *mut sapi_globals_struct; + pub fn ext_php_rs_file_globals() -> *mut php_file_globals; } include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/src/wrapper.c b/src/wrapper.c index 514882b6f6..c88e2dab82 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -64,3 +64,12 @@ sapi_globals_struct *ext_php_rs_sapi_globals() { return &sapi_globals; #endif } + + +php_file_globals *ext_php_rs_file_globals() { +#ifdef ZTS + return TSRMG_FAST_BULK(file_globals_id, php_file_globals *); +#else + return &file_globals; +#endif +} diff --git a/src/zend/globals.rs b/src/zend/globals.rs index fee0d09fdc..bf68e240d3 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -8,11 +8,11 @@ use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; use crate::ffi::{ - _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_process_globals, - ext_php_rs_sapi_globals, file_globals, php_core_globals, php_file_globals, sapi_globals_struct, - sapi_header_struct, sapi_headers_struct, sapi_request_info, zend_is_auto_global, - TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, - TRACK_VARS_REQUEST, TRACK_VARS_SERVER, + _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_file_globals, + ext_php_rs_process_globals, ext_php_rs_sapi_globals, php_core_globals, php_file_globals, + sapi_globals_struct, sapi_header_struct, sapi_headers_struct, sapi_request_info, + zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, + TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, }; use crate::types::{ZendHashTable, ZendObject, ZendStr}; @@ -378,7 +378,8 @@ impl FileGlobals { pub fn get() -> GlobalReadGuard { // SAFETY: PHP executor globals are statically declared therefore should never // return an invalid pointer. - let globals = unsafe { &file_globals }; + let globals = unsafe { ext_php_rs_file_globals().as_ref() } + .expect("Static file globals were invalid"); let guard = FILE_GLOBALS_LOCK.read(); GlobalReadGuard { globals, guard } } @@ -393,7 +394,7 @@ impl FileGlobals { pub fn get_mut() -> GlobalWriteGuard { // SAFETY: PHP executor globals are statically declared therefore should never // return an invalid pointer. - let globals = unsafe { &mut file_globals }; + let globals = unsafe { &mut *ext_php_rs_file_globals() }; let guard = SAPI_GLOBALS_LOCK.write(); GlobalWriteGuard { globals, guard } } From 0a2ecbcc39496f95e1e86bd7bd9073f02dd8d502 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 13:00:24 +0200 Subject: [PATCH 028/100] Add helper function --- src/wrapper.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wrapper.h b/src/wrapper.h index 62e6fa9e64..85d8d21761 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -24,6 +24,7 @@ #include "zend_interfaces.h" #include "SAPI.h" #include "php_variables.h" +#include "file.h" zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent); void ext_php_rs_zend_string_release(zend_string *zs); @@ -36,3 +37,4 @@ void ext_php_rs_zend_object_release(zend_object *obj); zend_executor_globals *ext_php_rs_executor_globals(); php_core_globals *ext_php_rs_process_globals(); sapi_globals_struct *ext_php_rs_sapi_globals(); +php_file_globals *ext_php_rs_file_globals(); From ef52ef60e3b81ec0ca11a8272fe5b33cdc6fb349 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 13:04:55 +0200 Subject: [PATCH 029/100] Remove file.h --- src/wrapper.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wrapper.h b/src/wrapper.h index 85d8d21761..f2747b7a5d 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -24,7 +24,6 @@ #include "zend_interfaces.h" #include "SAPI.h" #include "php_variables.h" -#include "file.h" zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent); void ext_php_rs_zend_string_release(zend_string *zs); From e30f1272da7c9e5e4913063fcbc67db0c03a2606 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 13:27:10 +0200 Subject: [PATCH 030/100] Add file_globals_id to allowed bindings --- allowed_bindings.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 85784801cb..d9996ec62c 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -252,6 +252,7 @@ bind! { sapi_globals_offset, php_file_globals, file_globals, + file_globals_id, TRACK_VARS_POST, TRACK_VARS_GET, TRACK_VARS_COOKIE, From 89c77585e743901acfdfab3a8ddb50b186ead635 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 15:13:50 +0200 Subject: [PATCH 031/100] Add helper method for function table --- src/zend/globals.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/zend/globals.rs b/src/zend/globals.rs index c9a7a0d91c..ac8701abdd 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -51,6 +51,16 @@ impl ExecutorGlobals { unsafe { self.class_table.as_ref() } } + /// Attempts to retrieve the global functions hash table. + pub fn function_table(&self) -> Option<&ZendHashTable> { + unsafe { self.function_table.as_ref() } + } + + /// Attempts to retrieve the global functions hash table as mutable. + pub fn function_table_mut(&self) -> Option<&mut ZendHashTable> { + unsafe { self.function_table.as_mut() } + } + /// Attempts to retrieve the global constants table. pub fn constants(&self) -> Option<&ZendHashTable> { unsafe { self.zend_constants.as_ref() } From 38cbba0457e637cc4e5410fb29a7964c2dfc40c0 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 15:30:49 +0200 Subject: [PATCH 032/100] Add function type helper function Follow up to #254 --- src/zend/ex.rs | 12 ++++++++++++ src/zend/function.rs | 13 ++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/zend/ex.rs b/src/zend/ex.rs index dada00e0c8..cb389cb2e7 100644 --- a/src/zend/ex.rs +++ b/src/zend/ex.rs @@ -6,6 +6,8 @@ use crate::{ types::{ZendClassObject, ZendObject, Zval}, }; +use super::function::Function; + /// Execute data passed when a function is called from PHP. /// /// This generally contains things related to the call, including but not @@ -194,6 +196,16 @@ impl ExecuteData { self.This.object_mut() } + /// Attempt to retrieve the function that is being called. + pub fn function(&self) -> Option<&Function> { + unsafe { self.func.as_ref() } + } + + /// Attempt to retrieve the previous execute data on the call stack. + pub fn previous(&self) -> Option<&Self> { + unsafe { self.prev_execute_data.as_ref() } + } + /// Translation of macro `ZEND_CALL_ARG(call, n)` /// zend_compile.h:578 /// diff --git a/src/zend/function.rs b/src/zend/function.rs index ba889d775f..9793e3e05c 100644 --- a/src/zend/function.rs +++ b/src/zend/function.rs @@ -2,7 +2,10 @@ use std::{fmt::Debug, os::raw::c_char, ptr}; -use crate::ffi::zend_function_entry; +use crate::{ + ffi::{zend_function, zend_function_entry}, + flags::FunctionType, +}; /// A Zend function entry. pub type FunctionEntry = zend_function_entry; @@ -36,3 +39,11 @@ impl FunctionEntry { Box::into_raw(Box::new(self)) } } + +pub type Function = zend_function; + +impl Function { + pub fn type_(&self) -> FunctionType { + FunctionType::from(unsafe { self.type_ }) + } +} From 87ab71846c80bf04e05cc095faad7d9758159872 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 15:35:19 +0200 Subject: [PATCH 033/100] Remove additional unrelated functions --- src/zend/ex.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/zend/ex.rs b/src/zend/ex.rs index fac1b939d9..0baf564936 100644 --- a/src/zend/ex.rs +++ b/src/zend/ex.rs @@ -231,14 +231,6 @@ impl ExecuteData { let size = std::mem::size_of::(); ((size as isize) + ZEND_MM_ALIGNMENT as isize - 1) & ZEND_MM_ALIGNMENT_MASK as isize } - - pub fn previous(&self) -> Option<&Self> { - unsafe { self.prev_execute_data.as_ref() } - } - - pub fn function(&self) -> Option<&_zend_function> { - unsafe { self.func.as_ref() } - } } #[cfg(test)] From e7bbcfed5e45b9ed8ccc484c3f88a503a07b1055 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 19 Jul 2023 15:58:53 +0200 Subject: [PATCH 034/100] Add helper to get the sapi name Useful to determine if the context is CLI / FPM etc --- allowed_bindings.rs | 1 + docsrs_bindings.rs | 169 ++++++++++++++++++++++++++++++++++++++++++++ src/wrapper.h | 1 + src/zend/mod.rs | 11 ++- 4 files changed, 181 insertions(+), 1 deletion(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 7252beaa63..c715636e97 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -248,5 +248,6 @@ bind! { tsrm_get_ls_cache, executor_globals_offset, zend_atomic_bool_store, + sapi_module, zend_interrupt_function } diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 1ba4175991..7fb99ce90f 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -323,6 +323,28 @@ extern "C" { extern "C" { pub fn __zend_malloc(len: usize) -> *mut ::std::os::raw::c_void; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_llist_element { + pub next: *mut _zend_llist_element, + pub prev: *mut _zend_llist_element, + pub data: [::std::os::raw::c_char; 1usize], +} +pub type zend_llist_element = _zend_llist_element; +pub type llist_dtor_func_t = + ::std::option::Option; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_llist { + pub head: *mut zend_llist_element, + pub tail: *mut zend_llist_element, + pub count: usize, + pub size: usize, + pub dtor: llist_dtor_func_t, + pub persistent: ::std::os::raw::c_uchar, + pub traverse_ptr: *mut zend_llist_element, +} +pub type zend_llist = _zend_llist; pub type zend_string_init_interned_func_t = ::std::option::Option< unsafe extern "C" fn( str_: *const ::std::os::raw::c_char, @@ -473,6 +495,29 @@ pub struct _zend_class_arrayaccess_funcs { pub type zend_class_arrayaccess_funcs = _zend_class_arrayaccess_funcs; #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct stat { + pub st_dev: dev_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_ino: __darwin_ino64_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_atimespec: timespec, + pub st_mtimespec: timespec, + pub st_ctimespec: timespec, + pub st_birthtimespec: timespec, + pub st_size: off_t, + pub st_blocks: blkcnt_t, + pub st_blksize: blksize_t, + pub st_flags: __uint32_t, + pub st_gen: __uint32_t, + pub st_lspare: __int32_t, + pub st_qspare: [__int64_t; 2usize], +} +pub type zend_stat_t = stat; +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct _zend_serialize_data { _unused: [u8; 0], } @@ -1514,3 +1559,127 @@ extern "C" { extern "C" { pub static mut zend_ce_stringable: *mut zend_class_entry; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sapi_header_struct { + pub header: *mut ::std::os::raw::c_char, + pub header_len: usize, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sapi_headers_struct { + pub headers: zend_llist, + pub http_response_code: ::std::os::raw::c_int, + pub send_default_content_type: ::std::os::raw::c_uchar, + pub mimetype: *mut ::std::os::raw::c_char, + pub http_status_line: *mut ::std::os::raw::c_char, +} +pub type sapi_module_struct = _sapi_module_struct; +extern "C" { + pub static mut sapi_module: sapi_module_struct; +} +pub const sapi_header_op_enum_SAPI_HEADER_REPLACE: sapi_header_op_enum = 0; +pub const sapi_header_op_enum_SAPI_HEADER_ADD: sapi_header_op_enum = 1; +pub const sapi_header_op_enum_SAPI_HEADER_DELETE: sapi_header_op_enum = 2; +pub const sapi_header_op_enum_SAPI_HEADER_DELETE_ALL: sapi_header_op_enum = 3; +pub const sapi_header_op_enum_SAPI_HEADER_SET_STATUS: sapi_header_op_enum = 4; +pub type sapi_header_op_enum = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _sapi_module_struct { + pub name: *mut ::std::os::raw::c_char, + pub pretty_name: *mut ::std::os::raw::c_char, + pub startup: ::std::option::Option< + unsafe extern "C" fn(sapi_module: *mut _sapi_module_struct) -> ::std::os::raw::c_int, + >, + pub shutdown: ::std::option::Option< + unsafe extern "C" fn(sapi_module: *mut _sapi_module_struct) -> ::std::os::raw::c_int, + >, + pub activate: ::std::option::Option ::std::os::raw::c_int>, + pub deactivate: ::std::option::Option ::std::os::raw::c_int>, + pub ub_write: ::std::option::Option< + unsafe extern "C" fn(str_: *const ::std::os::raw::c_char, str_length: usize) -> usize, + >, + pub flush: + ::std::option::Option, + pub get_stat: ::std::option::Option *mut zend_stat_t>, + pub getenv: ::std::option::Option< + unsafe extern "C" fn( + name: *const ::std::os::raw::c_char, + name_len: usize, + ) -> *mut ::std::os::raw::c_char, + >, + pub sapi_error: ::std::option::Option< + unsafe extern "C" fn( + type_: ::std::os::raw::c_int, + error_msg: *const ::std::os::raw::c_char, + ... + ), + >, + pub header_handler: ::std::option::Option< + unsafe extern "C" fn( + sapi_header: *mut sapi_header_struct, + op: sapi_header_op_enum, + sapi_headers: *mut sapi_headers_struct, + ) -> ::std::os::raw::c_int, + >, + pub send_headers: ::std::option::Option< + unsafe extern "C" fn(sapi_headers: *mut sapi_headers_struct) -> ::std::os::raw::c_int, + >, + pub send_header: ::std::option::Option< + unsafe extern "C" fn( + sapi_header: *mut sapi_header_struct, + server_context: *mut ::std::os::raw::c_void, + ), + >, + pub read_post: ::std::option::Option< + unsafe extern "C" fn(buffer: *mut ::std::os::raw::c_char, count_bytes: usize) -> usize, + >, + pub read_cookies: ::std::option::Option *mut ::std::os::raw::c_char>, + pub register_server_variables: + ::std::option::Option, + pub log_message: ::std::option::Option< + unsafe extern "C" fn( + message: *const ::std::os::raw::c_char, + syslog_type_int: ::std::os::raw::c_int, + ), + >, + pub get_request_time: + ::std::option::Option zend_result>, + pub terminate_process: ::std::option::Option, + pub php_ini_path_override: *mut ::std::os::raw::c_char, + pub default_post_reader: ::std::option::Option, + pub treat_data: ::std::option::Option< + unsafe extern "C" fn( + arg: ::std::os::raw::c_int, + str_: *mut ::std::os::raw::c_char, + destArray: *mut zval, + ), + >, + pub executable_location: *mut ::std::os::raw::c_char, + pub php_ini_ignore: ::std::os::raw::c_int, + pub php_ini_ignore_cwd: ::std::os::raw::c_int, + pub get_fd: ::std::option::Option< + unsafe extern "C" fn(fd: *mut ::std::os::raw::c_int) -> ::std::os::raw::c_int, + >, + pub force_http_10: ::std::option::Option ::std::os::raw::c_int>, + pub get_target_uid: + ::std::option::Option ::std::os::raw::c_int>, + pub get_target_gid: + ::std::option::Option ::std::os::raw::c_int>, + pub input_filter: ::std::option::Option< + unsafe extern "C" fn( + arg: ::std::os::raw::c_int, + var: *const ::std::os::raw::c_char, + val: *mut *mut ::std::os::raw::c_char, + val_len: usize, + new_val_len: *mut usize, + ) -> ::std::os::raw::c_uint, + >, + pub ini_defaults: + ::std::option::Option, + pub phpinfo_as_text: ::std::os::raw::c_int, + pub ini_entries: *mut ::std::os::raw::c_char, + pub additional_functions: *const zend_function_entry, + pub input_filter_init: ::std::option::Option ::std::os::raw::c_uint>, +} diff --git a/src/wrapper.h b/src/wrapper.h index 2813263676..02572c786f 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -20,6 +20,7 @@ #include "zend_exceptions.h" #include "zend_inheritance.h" #include "zend_interfaces.h" +#include "SAPI.h" zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent); void ext_php_rs_zend_string_release(zend_string *zs); diff --git a/src/zend/mod.rs b/src/zend/mod.rs index 74547f044a..b124f52ae9 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -9,7 +9,10 @@ mod globals; mod handlers; mod module; -use crate::{error::Result, ffi::php_printf}; +use crate::{ + error::Result, + ffi::{php_printf, sapi_module}, +}; use std::ffi::CString; pub use _type::ZendType; @@ -42,3 +45,9 @@ pub fn printf(message: &str) -> Result<()> { }; Ok(()) } + +/// Get the name of the SAPI module. +pub fn php_sapi_name() -> String { + let c_str = unsafe { std::ffi::CStr::from_ptr(sapi_module.name) }; + c_str.to_str().expect("Unable to parse CStr").to_string() +} From 0bf6424b2995a46e827a5c2988b7184467788f90 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 17 Oct 2023 11:42:33 +0200 Subject: [PATCH 035/100] feat(iterator): add helper for zend_object_iterator --- src/types/iterator.rs | 106 ++++++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 1 + src/types/object.rs | 11 +++++ 3 files changed, 118 insertions(+) create mode 100644 src/types/iterator.rs diff --git a/src/types/iterator.rs b/src/types/iterator.rs new file mode 100644 index 0000000000..7703f0eac4 --- /dev/null +++ b/src/types/iterator.rs @@ -0,0 +1,106 @@ +use crate::convert::FromZvalMut; +use crate::ffi::zend_object_iterator; +use crate::flags::DataType; +use crate::types::{ZendLong, Zval}; +use std::convert::TryInto; + +pub type ZendIterator = zend_object_iterator; + +pub struct Iter<'a> { + zi: &'a mut ZendIterator, +} + +impl ZendIterator { + pub fn iter(&mut self) -> Iter { + self.index = 0; + self.rewind(); + + Iter { zi: self } + } + + pub fn valid(&mut self) -> bool { + if let Some(valid) = unsafe { (*self.funcs).valid } { + unsafe { valid(&mut *self) != 0 } + } else { + true + } + } + + pub fn rewind(&mut self) { + if let Some(rewind) = unsafe { (*self.funcs).rewind } { + unsafe { + rewind(&mut *self); + } + } + } + + pub fn move_forward(&mut self) { + if let Some(move_forward) = unsafe { (*self.funcs).move_forward } { + unsafe { + move_forward(&mut *self); + } + } + } + + pub fn get_current_data<'a>(&mut self) -> Option<&'a Zval> { + let get_current_data = unsafe { (*self.funcs).get_current_data }?; + let value = unsafe { &*get_current_data(&mut *self) }; + + Some(value) + } + + pub fn get_current_key(&mut self) -> Option { + let get_current_key = unsafe { (*self.funcs).get_current_key }?; + let mut key = Zval::new(); + unsafe { + get_current_key(&mut *self, &mut key); + } + + Some(key) + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = (u64, Option, &'a Zval); + + fn next(&mut self) -> Option { + // Call next when index > 0, so next is really called at the start of each iteration, which allow to work better with generator iterator + if self.zi.index > 0 { + self.zi.move_forward(); + + if !self.zi.valid() { + return None; + } + } + + self.zi.index += 1; + + let key = self.zi.get_current_key(); + let value = self.zi.get_current_data()?; + let real_index = self.zi.index - 1; + + Some(match key { + Some(key) => match key.is_long() { + false => (real_index, key.try_into().ok(), value), + true => ( + key.long().unwrap_or(real_index as ZendLong) as u64, + None, + value, + ), + }, + None => (real_index, None, value), + }) + } +} + +impl<'a> FromZvalMut<'a> for &'a mut ZendIterator { + const TYPE: DataType = DataType::Object(Some("Traversable")); + + fn from_zval_mut(zval: &'a mut Zval) -> Option { + let zend_object = zval.object()?; + let ce = zend_object.get_class_entry_mut(); + let iterator = unsafe { ce.get_iterator?(&mut *ce, &mut *zval, 0) }; + + unsafe { iterator.as_mut() } + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 888e056e78..84785ebddb 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -6,6 +6,7 @@ mod array; mod callable; mod class_object; +mod iterator; mod long; mod object; mod string; diff --git a/src/types/object.rs b/src/types/object.rs index ca8e526ebe..0b8379646b 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -103,6 +103,17 @@ impl ZendObject { unsafe { self.ce.as_ref() }.expect("Could not retrieve class entry.") } + /// Returns the [`ClassEntry`] associated with this object. + /// + /// # Panics + /// + /// Panics if the class entry is invalid. + pub fn get_class_entry_mut(&self) -> &'static mut ClassEntry { + // SAFETY: it is OK to panic here since PHP would segfault anyway + // when encountering an object with no class entry. + unsafe { self.ce.as_mut() }.expect("Could not retrieve class entry.") + } + /// Attempts to retrieve the class name of the object. pub fn get_class_name(&self) -> Result { unsafe { From 8f425fe5f1c5613f81d8d5c07b74364fc4c9b16f Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 17 Oct 2023 15:19:13 +0200 Subject: [PATCH 036/100] feat(iterator): use an iter key for both array and iterator --- allowed_bindings.rs | 1 + src/types/array.rs | 42 ++++++++++++++++++++++--------------- src/types/iterator.rs | 48 ++++++++++++++++++++++++++++++++----------- src/types/object.rs | 4 ++-- src/zend/globals.rs | 30 +++++++++++++-------------- 5 files changed, 79 insertions(+), 46 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 49b15856d8..6d7ddd9cd1 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -92,6 +92,7 @@ bind! { zend_internal_arg_info, zend_is_callable, zend_is_identical, + zend_is_iterable, zend_long, zend_lookup_class_ex, zend_module_entry, diff --git a/src/types/array.rs b/src/types/array.rs index c40b8f3993..0920f8e58c 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -25,6 +25,8 @@ use crate::{ flags::DataType, types::Zval, }; +use crate::types::iterator::IterKey; +use crate::types::ZendLong; /// A PHP hashtable. /// @@ -463,7 +465,7 @@ impl ZendHashTable { /// assert!(!ht.has_numerical_keys()); /// ``` pub fn has_numerical_keys(&self) -> bool { - !self.iter().any(|(_, k, _)| k.is_some()) + !self.iter().any(|(k, _)| !k.is_numerical()) } /// Checks if the hashtable has numerical, sequential keys. @@ -492,7 +494,7 @@ impl ZendHashTable { !self .iter() .enumerate() - .any(|(i, (k, strk, _))| i as u64 != k || strk.is_some()) + .any(|(i, (k, _))| IterKey::Long(i as u64) != k) } /// Returns an iterator over the key(s) and value contained inside the @@ -505,7 +507,7 @@ impl ZendHashTable { /// /// let mut ht = ZendHashTable::new(); /// - /// for (idx, key, val) in ht.iter() { + /// for (key, val) in ht.iter() { /// // ^ Index if inserted at an index. /// // ^ Optional string key, if inserted like a hashtable. /// // ^ Inserted value. @@ -548,7 +550,7 @@ impl Debug for ZendHashTable { f.debug_map() .entries( self.iter() - .map(|(k, k2, v)| (k2.unwrap_or_else(|| k.to_string()), v)), + .map(|(k, v)| (k.to_string(), v)), ) .finish() } @@ -594,7 +596,7 @@ impl<'a> Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = (u64, Option, &'a Zval); + type Item = (IterKey, &'a Zval); fn next(&mut self) -> Option { let key_type = unsafe { @@ -622,9 +624,13 @@ impl<'a> Iterator for Iter<'a> { &mut self.pos as *mut HashPosition, ) }; - let r: (u64, Option, &Zval) = match key.is_long() { - true => (key.long().unwrap_or(0) as u64, None, value), - false => (self.current_num, key.try_into().ok(), value), + + let r = match key.is_long() { + true => (IterKey::Long(key.long().unwrap_or(self.current_num as ZendLong) as u64), value), + false => match key.try_into() { + Ok(key) => (IterKey::String(key), value), + Err(_) => (IterKey::Long(self.current_num), value), + }, }; unsafe { @@ -679,9 +685,13 @@ impl<'a> DoubleEndedIterator for Iter<'a> { &mut self.pos as *mut HashPosition, ) }; - let r: (u64, Option, &Zval) = match key.is_long() { - true => (key.long().unwrap_or(0) as u64, None, value), - false => (self.current_num, key.try_into().ok(), value), + + let r = match key.is_long() { + true => (IterKey::Long(key.long().unwrap_or(self.current_num as ZendLong) as u64), value), + false => match key.try_into() { + Ok(key) => (IterKey::String(key), value), + Err(_) => (IterKey::Long(self.current_num), value), + }, }; unsafe { @@ -715,7 +725,7 @@ impl<'a> Iterator for Values<'a> { type Item = &'a Zval; fn next(&mut self) -> Option { - self.0.next().map(|(_, _, zval)| zval) + self.0.next().map(|(_, zval)| zval) } fn count(self) -> usize @@ -734,7 +744,7 @@ impl<'a> ExactSizeIterator for Values<'a> { impl<'a> DoubleEndedIterator for Values<'a> { fn next_back(&mut self) -> Option { - self.0.next_back().map(|(_, _, zval)| zval) + self.0.next_back().map(|(_, zval)| zval) } } @@ -780,9 +790,9 @@ where fn try_from(value: &'a ZendHashTable) -> Result { let mut hm = HashMap::with_capacity(value.len()); - for (idx, key, val) in value.iter() { + for (key, val) in value.iter() { hm.insert( - key.unwrap_or_else(|| idx.to_string()), + key.to_string(), V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?, ); } @@ -849,7 +859,7 @@ where fn try_from(value: &'a ZendHashTable) -> Result { let mut vec = Vec::with_capacity(value.len()); - for (_, _, val) in value.iter() { + for (_, val) in value.iter() { vec.push(T::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?); } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 7703f0eac4..de07ca807d 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -3,13 +3,10 @@ use crate::ffi::zend_object_iterator; use crate::flags::DataType; use crate::types::{ZendLong, Zval}; use std::convert::TryInto; +use std::fmt::Display; pub type ZendIterator = zend_object_iterator; -pub struct Iter<'a> { - zi: &'a mut ZendIterator, -} - impl ZendIterator { pub fn iter(&mut self) -> Iter { self.index = 0; @@ -60,8 +57,36 @@ impl ZendIterator { } } +#[derive(PartialEq)] +pub enum IterKey { + Long(u64), + String(String), +} + +impl IterKey { + pub fn is_numerical(&self) -> bool { + match self { + IterKey::Long(_) => true, + IterKey::String(_) => false, + } + } +} + +impl Display for IterKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IterKey::Long(key) => write!(f, "{}", key), + IterKey::String(key) => write!(f, "{}", key), + } + } +} + +pub struct Iter<'a> { + zi: &'a mut ZendIterator, +} + impl<'a> Iterator for Iter<'a> { - type Item = (u64, Option, &'a Zval); + type Item = (IterKey, &'a Zval); fn next(&mut self) -> Option { // Call next when index > 0, so next is really called at the start of each iteration, which allow to work better with generator iterator @@ -81,14 +106,13 @@ impl<'a> Iterator for Iter<'a> { Some(match key { Some(key) => match key.is_long() { - false => (real_index, key.try_into().ok(), value), - true => ( - key.long().unwrap_or(real_index as ZendLong) as u64, - None, - value, - ), + false => match key.try_into() { + Ok(key) => (IterKey::String(key), value), + Err(_) => (IterKey::Long(real_index), value), + }, + true => (IterKey::Long(key.long().unwrap_or(real_index as ZendLong) as u64), value), }, - None => (real_index, None, value), + None => (IterKey::Long(real_index), value), }) } } diff --git a/src/types/object.rs b/src/types/object.rs index 0b8379646b..ef1ccc6305 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -328,8 +328,8 @@ impl Debug for ZendObject { ); if let Ok(props) = self.get_properties() { - for (id, key, val) in props.iter() { - dbg.field(key.unwrap_or_else(|| id.to_string()).as_str(), val); + for (key, val) in props.iter() { + dbg.field(key.to_string().as_str(), val); } } diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 9761ab91d5..d1d047f96a 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -57,22 +57,20 @@ impl ExecutorGlobals { pub fn ini_values(&self) -> HashMap> { let hash_table = unsafe { &*self.ini_directives }; let mut ini_hash_map: HashMap> = HashMap::new(); - for (_index, key, value) in hash_table.iter() { - if let Some(key) = key { - ini_hash_map.insert(key, unsafe { - let ini_entry = &*value.ptr::().expect("Invalid ini entry"); - if ini_entry.value.is_null() { - None - } else { - Some( - (*ini_entry.value) - .as_str() - .expect("Ini value is not a string") - .to_owned(), - ) - } - }); - } + for (key, value) in hash_table.iter() { + ini_hash_map.insert(key.to_string(), unsafe { + let ini_entry = &*value.ptr::().expect("Invalid ini entry"); + if ini_entry.value.is_null() { + None + } else { + Some( + (*ini_entry.value) + .as_str() + .expect("Ini value is not a string") + .to_owned(), + ) + } + }); } ini_hash_map } From e5b1ebd99eb17a532a9d7ae56e0ca676d9017411 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 17 Oct 2023 20:30:24 +0200 Subject: [PATCH 037/100] feat(iterator): add iterable, add method to get traversable or iterable --- allowed_bindings.rs | 1 + src/describe/stub.rs | 1 + src/flags.rs | 20 +++++++++------- src/types/array.rs | 21 +++++++++-------- src/types/iterable.rs | 53 +++++++++++++++++++++++++++++++++++++++++++ src/types/iterator.rs | 29 +++++++++++++++-------- src/types/mod.rs | 2 ++ src/types/object.rs | 20 ++++++++-------- src/types/zval.rs | 35 +++++++++++++++++++++++++++- src/zend/class.rs | 19 ++++++++++++++++ 10 files changed, 163 insertions(+), 38 deletions(-) create mode 100644 src/types/iterable.rs diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 6d7ddd9cd1..0ef1b33107 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -160,6 +160,7 @@ bind! { IS_UNDEF, IS_VOID, IS_PTR, + IS_ITERABLE, MAY_BE_ANY, MAY_BE_BOOL, PHP_INI_USER, diff --git a/src/describe/stub.rs b/src/describe/stub.rs index 065b27549a..17cfbfde36 100644 --- a/src/describe/stub.rs +++ b/src/describe/stub.rs @@ -172,6 +172,7 @@ impl ToStub for DataType { DataType::Reference => "reference", DataType::Callable => "callable", DataType::Bool => "bool", + DataType::Iterable => "iterable", _ => "mixed", } ) diff --git a/src/flags.rs b/src/flags.rs index 60897a0654..473d8237fe 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -8,14 +8,14 @@ use crate::ffi::{ CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, E_COMPILE_ERROR, E_COMPILE_WARNING, E_CORE_ERROR, E_CORE_WARNING, E_DEPRECATED, E_ERROR, E_NOTICE, E_PARSE, E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING, - E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_MIXED, - IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, - IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, PHP_INI_PERDIR, PHP_INI_SYSTEM, - PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, - ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, - ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, - ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE, - ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE, + E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_ITERABLE, IS_LONG, + IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, + IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, PHP_INI_PERDIR, + PHP_INI_SYSTEM, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, + ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, + ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, + ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, + ZEND_ACC_HAS_RETURN_TYPE, ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE, ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, ZEND_ACC_NEARLY_LINKED, ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE, ZEND_ACC_PROMOTED, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES, @@ -49,6 +49,7 @@ bitflags! { const ConstantExpression = IS_CONSTANT_AST; const Void = IS_VOID; const Ptr = IS_PTR; + const Iterable = IS_ITERABLE; const InternedStringEx = Self::String.bits(); const StringEx = Self::String.bits() | Self::RefCounted.bits(); @@ -237,6 +238,7 @@ pub enum DataType { Double, String, Array, + Iterable, Object(Option<&'static str>), Resource, Reference, @@ -275,6 +277,7 @@ impl DataType { DataType::Mixed => IS_MIXED, DataType::Bool => _IS_BOOL, DataType::Ptr => IS_PTR, + DataType::Iterable => IS_ITERABLE, } } } @@ -379,6 +382,7 @@ impl Display for DataType { DataType::Bool => write!(f, "Bool"), DataType::Mixed => write!(f, "Mixed"), DataType::Ptr => write!(f, "Pointer"), + DataType::Iterable => write!(f, "Iterable"), } } } diff --git a/src/types/array.rs b/src/types/array.rs index 0920f8e58c..974272ee6a 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -10,6 +10,8 @@ use std::{ u64, }; +use crate::types::iterator::IterKey; +use crate::types::ZendLong; use crate::{ boxed::{ZBox, ZBoxable}, convert::{FromZval, IntoZval}, @@ -25,8 +27,6 @@ use crate::{ flags::DataType, types::Zval, }; -use crate::types::iterator::IterKey; -use crate::types::ZendLong; /// A PHP hashtable. /// @@ -512,7 +512,7 @@ impl ZendHashTable { /// // ^ Optional string key, if inserted like a hashtable. /// // ^ Inserted value. /// - /// dbg!(idx, key, val); + /// dbg!(key, val); /// } #[inline] pub fn iter(&self) -> Iter { @@ -548,10 +548,7 @@ unsafe impl ZBoxable for ZendHashTable { impl Debug for ZendHashTable { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_map() - .entries( - self.iter() - .map(|(k, v)| (k.to_string(), v)), - ) + .entries(self.iter().map(|(k, v)| (k.to_string(), v))) .finish() } } @@ -626,7 +623,10 @@ impl<'a> Iterator for Iter<'a> { }; let r = match key.is_long() { - true => (IterKey::Long(key.long().unwrap_or(self.current_num as ZendLong) as u64), value), + true => ( + IterKey::Long(key.long().unwrap_or(self.current_num as ZendLong) as u64), + value, + ), false => match key.try_into() { Ok(key) => (IterKey::String(key), value), Err(_) => (IterKey::Long(self.current_num), value), @@ -687,7 +687,10 @@ impl<'a> DoubleEndedIterator for Iter<'a> { }; let r = match key.is_long() { - true => (IterKey::Long(key.long().unwrap_or(self.current_num as ZendLong) as u64), value), + true => ( + IterKey::Long(key.long().unwrap_or(self.current_num as ZendLong) as u64), + value, + ), false => match key.try_into() { Ok(key) => (IterKey::String(key), value), Err(_) => (IterKey::Long(self.current_num), value), diff --git a/src/types/iterable.rs b/src/types/iterable.rs new file mode 100644 index 0000000000..2333302433 --- /dev/null +++ b/src/types/iterable.rs @@ -0,0 +1,53 @@ +use super::array::Iter as ZendHashTableIter; +use super::iterator::Iter as ZendIteratorIter; +use crate::convert::FromZval; +use crate::flags::DataType; +use crate::types::iterator::IterKey; +use crate::types::{ZendHashTable, ZendIterator, Zval}; + +#[derive(Debug)] +pub enum Iterable<'a> { + Array(&'a ZendHashTable), + Traversable(&'a mut ZendIterator), +} + +impl<'a> Iterable<'a> { + pub fn iter(&mut self) -> Iter { + match self { + Iterable::Array(array) => Iter::Array(array.iter()), + Iterable::Traversable(traversable) => Iter::Traversable(traversable.iter()), + } + } +} + +impl<'a> FromZval<'a> for Iterable<'a> { + const TYPE: DataType = DataType::Iterable; + + fn from_zval(zval: &'a Zval) -> Option { + if let Some(array) = zval.array() { + return Some(Iterable::Array(array)); + } + + if let Some(traversable) = zval.traversable() { + return Some(Iterable::Traversable(traversable)); + } + + None + } +} + +pub enum Iter<'a> { + Array(ZendHashTableIter<'a>), + Traversable(ZendIteratorIter<'a>), +} + +impl<'a> Iterator for Iter<'a> { + type Item = (IterKey, &'a Zval); + + fn next(&mut self) -> Option { + match self { + Iter::Array(array) => array.next(), + Iter::Traversable(traversable) => traversable.next(), + } + } +} diff --git a/src/types/iterator.rs b/src/types/iterator.rs index de07ca807d..f3a4a29082 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -3,8 +3,14 @@ use crate::ffi::zend_object_iterator; use crate::flags::DataType; use crate::types::{ZendLong, Zval}; use std::convert::TryInto; -use std::fmt::Display; - +use std::fmt::{Debug, Display, Formatter}; + +/// A PHP Iterator. +/// +/// In PHP, iterators are represented as zend_object_iterator. This allow user to iterate +/// over object implementing Traversable interface using foreach. +/// +/// ``` pub type ZendIterator = zend_object_iterator; impl ZendIterator { @@ -57,7 +63,13 @@ impl ZendIterator { } } -#[derive(PartialEq)] +impl Debug for ZendIterator { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ZendIterator").finish() + } +} + +#[derive(Debug, PartialEq)] pub enum IterKey { Long(u64), String(String), @@ -110,7 +122,10 @@ impl<'a> Iterator for Iter<'a> { Ok(key) => (IterKey::String(key), value), Err(_) => (IterKey::Long(real_index), value), }, - true => (IterKey::Long(key.long().unwrap_or(real_index as ZendLong) as u64), value), + true => ( + IterKey::Long(key.long().unwrap_or(real_index as ZendLong) as u64), + value, + ), }, None => (IterKey::Long(real_index), value), }) @@ -121,10 +136,6 @@ impl<'a> FromZvalMut<'a> for &'a mut ZendIterator { const TYPE: DataType = DataType::Object(Some("Traversable")); fn from_zval_mut(zval: &'a mut Zval) -> Option { - let zend_object = zval.object()?; - let ce = zend_object.get_class_entry_mut(); - let iterator = unsafe { ce.get_iterator?(&mut *ce, &mut *zval, 0) }; - - unsafe { iterator.as_mut() } + zval.object()?.get_class_entry().get_iterator(zval, false) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 84785ebddb..99bfdb3c00 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -6,6 +6,7 @@ mod array; mod callable; mod class_object; +mod iterable; mod iterator; mod long; mod object; @@ -15,6 +16,7 @@ mod zval; pub use array::ZendHashTable; pub use callable::ZendCallable; pub use class_object::ZendClassObject; +pub use iterator::ZendIterator; pub use long::ZendLong; pub use object::{PropertyQuery, ZendObject}; pub use string::ZendStr; diff --git a/src/types/object.rs b/src/types/object.rs index ef1ccc6305..138f31add3 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -103,17 +103,6 @@ impl ZendObject { unsafe { self.ce.as_ref() }.expect("Could not retrieve class entry.") } - /// Returns the [`ClassEntry`] associated with this object. - /// - /// # Panics - /// - /// Panics if the class entry is invalid. - pub fn get_class_entry_mut(&self) -> &'static mut ClassEntry { - // SAFETY: it is OK to panic here since PHP would segfault anyway - // when encountering an object with no class entry. - unsafe { self.ce.as_mut() }.expect("Could not retrieve class entry.") - } - /// Attempts to retrieve the class name of the object. pub fn get_class_name(&self) -> Result { unsafe { @@ -144,6 +133,15 @@ impl ZendObject { (self.ce as *const ClassEntry).eq(&(T::get_metadata().ce() as *const _)) } + /// Returns whether this object is an instance of \Traversable + /// + /// # Panics + /// + /// Panics if the class entry is invalid. + pub fn is_traversable(&self) -> bool { + self.instance_of(ce::traversable()) + } + #[inline(always)] pub fn try_call_method(&self, name: &str, params: Vec<&dyn IntoZvalDyn>) -> Result { let mut retval = Zval::new(); diff --git a/src/types/zval.rs b/src/types/zval.rs index 94b62aa985..ce4e55dded 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -4,6 +4,8 @@ use std::{convert::TryInto, ffi::c_void, fmt::Debug, ptr}; +use crate::types::iterable::Iterable; +use crate::types::ZendIterator; use crate::{ binary::Pack, binary_slice::PackSlice, @@ -12,7 +14,7 @@ use crate::{ error::{Error, Result}, ffi::{ _zval_struct__bindgen_ty_1, _zval_struct__bindgen_ty_2, zend_is_callable, - zend_is_identical, zend_resource, zend_value, zval, zval_ptr_dtor, + zend_is_identical, zend_is_iterable, zend_resource, zend_value, zval, zval_ptr_dtor, }, flags::DataType, flags::ZvalTypeFlags, @@ -237,6 +239,22 @@ impl Zval { ZendCallable::new(self).ok() } + pub fn traversable(&self) -> Option<&mut ZendIterator> { + if self.is_traversable() { + self.object()?.get_class_entry().get_iterator(self, false) + } else { + None + } + } + + pub fn iterable(&self) -> Option { + if self.is_iterable() { + Iterable::from_zval(self) + } else { + None + } + } + /// Returns the value of the zval if it is a pointer. /// /// # Safety @@ -347,6 +365,20 @@ impl Zval { unsafe { zend_is_identical(self_p as *mut Self, other_p as *mut Self) } } + /// Returns true if the zval is traversable, false otherwise. + pub fn is_traversable(&self) -> bool { + match self.object() { + None => false, + Some(obj) => obj.is_traversable(), + } + } + + /// Returns true if the zval is iterable (array or traversable), false otherwise. + pub fn is_iterable(&self) -> bool { + let ptr: *const Self = self; + unsafe { zend_is_iterable(ptr as *mut Self) } + } + /// Returns true if the zval contains a pointer, false otherwise. pub fn is_ptr(&self) -> bool { self.get_type() == DataType::Ptr @@ -601,6 +633,7 @@ impl Debug for Zval { DataType::ConstantExpression => field!(Option::<()>::None), DataType::Void => field!(Option::<()>::None), DataType::Bool => field!(self.bool()), + DataType::Iterable => field!(self.iterable()), // SAFETY: We are not accessing the pointer. DataType::Ptr => field!(unsafe { self.ptr::() }), }; diff --git a/src/zend/class.rs b/src/zend/class.rs index 4c3c23dcd8..d9765a3c50 100644 --- a/src/zend/class.rs +++ b/src/zend/class.rs @@ -1,5 +1,6 @@ //! Builder and objects for creating classes in the PHP world. +use crate::types::{ZendIterator, Zval}; use crate::{ boxed::ZBox, ffi::zend_class_entry, @@ -97,6 +98,24 @@ impl ClassEntry { } } + /// Returns the iterator for the class for a specific instance + /// + /// Returns [`None`] if there is no associated iterator for the class. + pub fn get_iterator<'a>(&self, zval: &'a Zval, by_ref: bool) -> Option<&'a mut ZendIterator> { + let ptr: *const Self = self; + let zval_ptr: *const Zval = zval; + + let iterator = unsafe { + (*ptr).get_iterator?( + ptr as *mut ClassEntry, + zval_ptr as *mut Zval, + if by_ref { 1 } else { 0 }, + ) + }; + + unsafe { iterator.as_mut() } + } + pub fn name(&self) -> Option<&str> { unsafe { self.name.as_ref().and_then(|s| s.as_str().ok()) } } From 26e06ad90f68a00915f710bcb9155bf2a4287d7e Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 17 Oct 2023 20:45:23 +0200 Subject: [PATCH 038/100] reduce duplicated code for iter key --- src/types/array.rs | 25 ++++++------------------- src/types/iterator.rs | 28 ++++++++++++++++------------ 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/types/array.rs b/src/types/array.rs index 974272ee6a..29e5c6efd8 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -11,7 +11,6 @@ use std::{ }; use crate::types::iterator::IterKey; -use crate::types::ZendLong; use crate::{ boxed::{ZBox, ZBoxable}, convert::{FromZval, IntoZval}, @@ -622,15 +621,9 @@ impl<'a> Iterator for Iter<'a> { ) }; - let r = match key.is_long() { - true => ( - IterKey::Long(key.long().unwrap_or(self.current_num as ZendLong) as u64), - value, - ), - false => match key.try_into() { - Ok(key) => (IterKey::String(key), value), - Err(_) => (IterKey::Long(self.current_num), value), - }, + let r = match IterKey::from_zval(&key) { + Some(key) => (key, value), + None => (IterKey::Long(self.current_num), value), }; unsafe { @@ -686,15 +679,9 @@ impl<'a> DoubleEndedIterator for Iter<'a> { ) }; - let r = match key.is_long() { - true => ( - IterKey::Long(key.long().unwrap_or(self.current_num as ZendLong) as u64), - value, - ), - false => match key.try_into() { - Ok(key) => (IterKey::String(key), value), - Err(_) => (IterKey::Long(self.current_num), value), - }, + let r = match IterKey::from_zval(&key) { + Some(key) => (key, value), + None => (IterKey::Long(self.current_num), value), }; unsafe { diff --git a/src/types/iterator.rs b/src/types/iterator.rs index f3a4a29082..5598076355 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -1,8 +1,7 @@ -use crate::convert::FromZvalMut; +use crate::convert::{FromZval, FromZvalMut}; use crate::ffi::zend_object_iterator; use crate::flags::DataType; -use crate::types::{ZendLong, Zval}; -use std::convert::TryInto; +use crate::types::Zval; use std::fmt::{Debug, Display, Formatter}; /// A PHP Iterator. @@ -93,6 +92,17 @@ impl Display for IterKey { } } +impl FromZval<'_> for IterKey { + const TYPE: DataType = DataType::String; + + fn from_zval(zval: &Zval) -> Option { + match zval.long() { + Some(key) => Some(IterKey::Long(key as u64)), + None => zval.string().map(|key| IterKey::String(key)), + } + } +} + pub struct Iter<'a> { zi: &'a mut ZendIterator, } @@ -117,15 +127,9 @@ impl<'a> Iterator for Iter<'a> { let real_index = self.zi.index - 1; Some(match key { - Some(key) => match key.is_long() { - false => match key.try_into() { - Ok(key) => (IterKey::String(key), value), - Err(_) => (IterKey::Long(real_index), value), - }, - true => ( - IterKey::Long(key.long().unwrap_or(real_index as ZendLong) as u64), - value, - ), + Some(key) => match IterKey::from_zval(&key) { + Some(key) => (key, value), + None => (IterKey::Long(real_index), value), }, None => (IterKey::Long(real_index), value), }) From b95bade2e2c95fec45be93b35db4b1c78bf280ed Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Wed, 18 Oct 2023 10:07:19 +0200 Subject: [PATCH 039/100] fix clippy feedback --- src/types/iterator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 5598076355..03132f2b21 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -98,7 +98,7 @@ impl FromZval<'_> for IterKey { fn from_zval(zval: &Zval) -> Option { match zval.long() { Some(key) => Some(IterKey::Long(key as u64)), - None => zval.string().map(|key| IterKey::String(key)), + None => zval.string().map(IterKey::String), } } } From 524a18ddb584001beaf87333b4833b75458a7c20 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Wed, 18 Oct 2023 10:39:27 +0200 Subject: [PATCH 040/100] feat(iterator): add documentation --- guide/src/types/iterable.md | 59 +++++++++++++++++++++++++++++++++++++ guide/src/types/iterator.md | 50 +++++++++++++++++++++++++++++++ src/types/iterable.rs | 4 +++ src/types/iterator.rs | 36 ++++++++++++++++++++++ src/types/mod.rs | 1 + 5 files changed, 150 insertions(+) create mode 100644 guide/src/types/iterable.md create mode 100644 guide/src/types/iterator.md diff --git a/guide/src/types/iterable.md b/guide/src/types/iterable.md new file mode 100644 index 0000000000..f79024f760 --- /dev/null +++ b/guide/src/types/iterable.md @@ -0,0 +1,59 @@ +# `Iterable` + +`Iterable`s are represented either by an `array` or `Traversable` type. + +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +|---------------|----------------|-----------------| ---------------- |----------------------------------| +| Yes | No | No | No | `ZendHashTable` or `ZendIterator` | + +Converting from a zval to a `Iterable` is valid when the value is either an array or an object +that implements the `Traversable` interface. This means that any value that can be used in a +`foreach` loop can be converted into a `Iterable`. + +## Rust example + +```rust,no_run +# #![cfg_attr(windows, feature(abi_vectorcall))] +# extern crate ext_php_rs; +# use ext_php_rs::prelude::*; +# use ext_php_rs::types::Iterable; +#[php_function] +pub fn test_iterable(mut iterable: Iterable) { + for (k, v) in iterable.iter() { + println!("k: {} v: {}", k, v.string().unwrap()); + } +} +# fn main() {} +``` + +## PHP example + +```php + 'world'; + yield 'rust' => 'php'; + yield 'okk'; +}; + +$array = [ + 'hello' => 'world', + 'rust' => 'php', + 'okk', +]; + +test_iterable($generator()); +test_iterable($array); +``` + +Output: + +```text +k: hello v: world +k: rust v: php +k: 0 v: okk +k: hello v: world +k: rust v: php +k: 0 v: okk +``` diff --git a/guide/src/types/iterator.md b/guide/src/types/iterator.md new file mode 100644 index 0000000000..fc187c13e4 --- /dev/null +++ b/guide/src/types/iterator.md @@ -0,0 +1,50 @@ +# `ZendIterator` + +`ZendIterator`s are represented by the `Traversable` type. + +| `T` parameter | `&T` parameter | `T` Return type | `&T` Return type | PHP representation | +|---------------| -------------- |-----------------| ---------------- | ------------------ | +| No | Yes | No | No | `ZendIterator` | + +Converting from a zval to a `ZendIterator` is valid when there is an associated iterator to +the variable. This means that any value, at the exception of an `array`, that can be used in +a `foreach` loop can be converted into a `ZendIterator`. As an example, a `Generator` can be +used but also a the result of a `query` call with `PDO`. + +## Rust example + +```rust,no_run +# #![cfg_attr(windows, feature(abi_vectorcall))] +# extern crate ext_php_rs; +# use ext_php_rs::prelude::*; +# use ext_php_rs::types::ZendIterator; +#[php_function] +pub fn test_iterator(iterator: &mut ZendIterator) { + for (k, v) in iterator.iter() { + println!("k: {} v: {}", k, v.string().unwrap()); + } +} +# fn main() {} +``` + +## PHP example + +```php + 'world'; + yield 'rust' => 'php'; + yield 'okk'; +}; + +test_iterator($generator()); +``` + +Output: + +```text +k: hello v: world +k: rust v: php +k: 0 v: okk +``` diff --git a/src/types/iterable.rs b/src/types/iterable.rs index 2333302433..f4a02083c4 100644 --- a/src/types/iterable.rs +++ b/src/types/iterable.rs @@ -5,6 +5,8 @@ use crate::flags::DataType; use crate::types::iterator::IterKey; use crate::types::{ZendHashTable, ZendIterator, Zval}; +/// This type represents a PHP iterable, which can be either an array or an object implementing +/// the Traversable interface. #[derive(Debug)] pub enum Iterable<'a> { Array(&'a ZendHashTable), @@ -12,6 +14,7 @@ pub enum Iterable<'a> { } impl<'a> Iterable<'a> { + /// Creates a new rust iterator from a PHP iterable. pub fn iter(&mut self) -> Iter { match self { Iterable::Array(array) => Iter::Array(array.iter()), @@ -36,6 +39,7 @@ impl<'a> FromZval<'a> for Iterable<'a> { } } +/// Rust iterator over a PHP iterable. pub enum Iter<'a> { Array(ZendHashTableIter<'a>), Traversable(ZendIteratorIter<'a>), diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 03132f2b21..3d3dc2dbe7 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -13,6 +13,11 @@ use std::fmt::{Debug, Display, Formatter}; pub type ZendIterator = zend_object_iterator; impl ZendIterator { + /// Creates a new rust iterator from a zend_object_iterator. + /// + /// # Returns + /// + /// Returns a iterator over the zend_object_iterator. pub fn iter(&mut self) -> Iter { self.index = 0; self.rewind(); @@ -20,6 +25,10 @@ impl ZendIterator { Iter { zi: self } } + /// Check if the current position of the iterator is valid. + /// + /// As an example this will call the user defined valid method of the ['\Iterator'] interface. + /// see https://www.php.net/manual/en/iterator.valid.php pub fn valid(&mut self) -> bool { if let Some(valid) = unsafe { (*self.funcs).valid } { unsafe { valid(&mut *self) != 0 } @@ -28,6 +37,10 @@ impl ZendIterator { } } + /// Rewind the iterator to the first element. + /// + /// As an example this will call the user defined rewind method of the ['\Iterator'] interface. + /// see https://www.php.net/manual/en/iterator.rewind.php pub fn rewind(&mut self) { if let Some(rewind) = unsafe { (*self.funcs).rewind } { unsafe { @@ -36,6 +49,10 @@ impl ZendIterator { } } + /// Move the iterator forward to the next element. + /// + /// As an example this will call the user defined next method of the ['\Iterator'] interface. + /// see https://www.php.net/manual/en/iterator.next.php pub fn move_forward(&mut self) { if let Some(move_forward) = unsafe { (*self.funcs).move_forward } { unsafe { @@ -44,6 +61,12 @@ impl ZendIterator { } } + /// Get the current data of the iterator. + /// + /// # Returns + /// + /// Returns a reference to the current data of the iterator if available + /// , ['None'] otherwise. pub fn get_current_data<'a>(&mut self) -> Option<&'a Zval> { let get_current_data = unsafe { (*self.funcs).get_current_data }?; let value = unsafe { &*get_current_data(&mut *self) }; @@ -51,6 +74,12 @@ impl ZendIterator { Some(value) } + /// Get the current key of the iterator. + /// + /// # Returns + /// + /// Returns a new ['Zval'] containing the current key of the iterator if available + /// , ['None'] otherwise. pub fn get_current_key(&mut self) -> Option { let get_current_key = unsafe { (*self.funcs).get_current_key }?; let mut key = Zval::new(); @@ -74,7 +103,13 @@ pub enum IterKey { String(String), } +/// Represent the key of a PHP iterator, which can be either a long or a string. impl IterKey { + /// Check if the key is numerical. + /// + /// # Returns + /// + /// Returns true if the key is numerical, false otherwise. pub fn is_numerical(&self) -> bool { match self { IterKey::Long(_) => true, @@ -103,6 +138,7 @@ impl FromZval<'_> for IterKey { } } +/// Immutable iterator upon a reference to a PHP iterator. pub struct Iter<'a> { zi: &'a mut ZendIterator, } diff --git a/src/types/mod.rs b/src/types/mod.rs index 99bfdb3c00..846c8d1bb4 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -16,6 +16,7 @@ mod zval; pub use array::ZendHashTable; pub use callable::ZendCallable; pub use class_object::ZendClassObject; +pub use iterable::Iterable; pub use iterator::ZendIterator; pub use long::ZendLong; pub use object::{PropertyQuery, ZendObject}; From 57577b687c2747fca9b2d0ac8e1891d1825726b9 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Wed, 18 Oct 2023 12:11:53 +0200 Subject: [PATCH 041/100] fix doc --- src/types/iterator.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 3d3dc2dbe7..6aa71a83a8 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -8,8 +8,6 @@ use std::fmt::{Debug, Display, Formatter}; /// /// In PHP, iterators are represented as zend_object_iterator. This allow user to iterate /// over object implementing Traversable interface using foreach. -/// -/// ``` pub type ZendIterator = zend_object_iterator; impl ZendIterator { @@ -28,7 +26,7 @@ impl ZendIterator { /// Check if the current position of the iterator is valid. /// /// As an example this will call the user defined valid method of the ['\Iterator'] interface. - /// see https://www.php.net/manual/en/iterator.valid.php + /// see pub fn valid(&mut self) -> bool { if let Some(valid) = unsafe { (*self.funcs).valid } { unsafe { valid(&mut *self) != 0 } @@ -40,7 +38,7 @@ impl ZendIterator { /// Rewind the iterator to the first element. /// /// As an example this will call the user defined rewind method of the ['\Iterator'] interface. - /// see https://www.php.net/manual/en/iterator.rewind.php + /// see pub fn rewind(&mut self) { if let Some(rewind) = unsafe { (*self.funcs).rewind } { unsafe { @@ -52,7 +50,7 @@ impl ZendIterator { /// Move the iterator forward to the next element. /// /// As an example this will call the user defined next method of the ['\Iterator'] interface. - /// see https://www.php.net/manual/en/iterator.next.php + /// see pub fn move_forward(&mut self) { if let Some(move_forward) = unsafe { (*self.funcs).move_forward } { unsafe { From a8f15d8f53e5672383ff684954d822bdb4a964d9 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Thu, 19 Oct 2023 08:54:22 +0200 Subject: [PATCH 042/100] feat(iterator): handle exception --- src/types/iterable.rs | 11 +++--- src/types/iterator.rs | 87 +++++++++++++++++++++++++++++-------------- src/zend/globals.rs | 22 +++++++++++ 3 files changed, 87 insertions(+), 33 deletions(-) diff --git a/src/types/iterable.rs b/src/types/iterable.rs index f4a02083c4..7eb81e610d 100644 --- a/src/types/iterable.rs +++ b/src/types/iterable.rs @@ -1,6 +1,7 @@ use super::array::Iter as ZendHashTableIter; use super::iterator::Iter as ZendIteratorIter; use crate::convert::FromZval; +use crate::exception::PhpResult; use crate::flags::DataType; use crate::types::iterator::IterKey; use crate::types::{ZendHashTable, ZendIterator, Zval}; @@ -15,10 +16,10 @@ pub enum Iterable<'a> { impl<'a> Iterable<'a> { /// Creates a new rust iterator from a PHP iterable. - pub fn iter(&mut self) -> Iter { + pub fn iter(&mut self) -> PhpResult { match self { - Iterable::Array(array) => Iter::Array(array.iter()), - Iterable::Traversable(traversable) => Iter::Traversable(traversable.iter()), + Iterable::Array(array) => Ok(Iter::Array(array.iter())), + Iterable::Traversable(traversable) => Ok(Iter::Traversable(traversable.iter()?)), } } } @@ -46,11 +47,11 @@ pub enum Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = (IterKey, &'a Zval); + type Item = PhpResult<(IterKey, &'a Zval)>; fn next(&mut self) -> Option { match self { - Iter::Array(array) => array.next(), + Iter::Array(array) => array.next().map(Ok), Iter::Traversable(traversable) => traversable.next(), } } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 6aa71a83a8..57d48bc8c8 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -1,7 +1,9 @@ use crate::convert::{FromZval, FromZvalMut}; -use crate::ffi::zend_object_iterator; +use crate::ffi::{zend_object_iterator, ZEND_RESULT_CODE_SUCCESS}; use crate::flags::DataType; +use crate::prelude::PhpResult; use crate::types::Zval; +use crate::zend::ExecutorGlobals; use std::fmt::{Debug, Display, Formatter}; /// A PHP Iterator. @@ -16,22 +18,26 @@ impl ZendIterator { /// # Returns /// /// Returns a iterator over the zend_object_iterator. - pub fn iter(&mut self) -> Iter { + pub fn iter(&mut self) -> PhpResult { self.index = 0; - self.rewind(); + self.rewind()?; - Iter { zi: self } + Ok(Iter { zi: self }) } /// Check if the current position of the iterator is valid. /// /// As an example this will call the user defined valid method of the ['\Iterator'] interface. /// see - pub fn valid(&mut self) -> bool { + pub fn valid(&mut self) -> PhpResult { if let Some(valid) = unsafe { (*self.funcs).valid } { - unsafe { valid(&mut *self) != 0 } + let valid = unsafe { valid(&mut *self) == ZEND_RESULT_CODE_SUCCESS }; + + ExecutorGlobals::throw_if_exception()?; + + Ok(valid) } else { - true + Ok(true) } } @@ -39,24 +45,28 @@ impl ZendIterator { /// /// As an example this will call the user defined rewind method of the ['\Iterator'] interface. /// see - pub fn rewind(&mut self) { + pub fn rewind(&mut self) -> PhpResult<()> { if let Some(rewind) = unsafe { (*self.funcs).rewind } { unsafe { rewind(&mut *self); } } + + ExecutorGlobals::throw_if_exception() } /// Move the iterator forward to the next element. /// /// As an example this will call the user defined next method of the ['\Iterator'] interface. /// see - pub fn move_forward(&mut self) { + pub fn move_forward(&mut self) -> PhpResult<()> { if let Some(move_forward) = unsafe { (*self.funcs).move_forward } { unsafe { move_forward(&mut *self); } } + + ExecutorGlobals::throw_if_exception() } /// Get the current data of the iterator. @@ -65,11 +75,16 @@ impl ZendIterator { /// /// Returns a reference to the current data of the iterator if available /// , ['None'] otherwise. - pub fn get_current_data<'a>(&mut self) -> Option<&'a Zval> { - let get_current_data = unsafe { (*self.funcs).get_current_data }?; + pub fn get_current_data<'a>(&mut self) -> PhpResult> { + let get_current_data = match unsafe { (*self.funcs).get_current_data } { + Some(get_current_data) => get_current_data, + None => return Ok(None), + }; let value = unsafe { &*get_current_data(&mut *self) }; - Some(value) + ExecutorGlobals::throw_if_exception()?; + + Ok(Some(value)) } /// Get the current key of the iterator. @@ -78,14 +93,21 @@ impl ZendIterator { /// /// Returns a new ['Zval'] containing the current key of the iterator if available /// , ['None'] otherwise. - pub fn get_current_key(&mut self) -> Option { - let get_current_key = unsafe { (*self.funcs).get_current_key }?; + pub fn get_current_key(&mut self) -> PhpResult> { + let get_current_key = match unsafe { (*self.funcs).get_current_key } { + Some(get_current_key) => get_current_key, + None => return Ok(None), + }; + let mut key = Zval::new(); + unsafe { get_current_key(&mut *self, &mut key); } - Some(key) + ExecutorGlobals::throw_if_exception()?; + + Ok(Some(key)) } } @@ -142,31 +164,40 @@ pub struct Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = (IterKey, &'a Zval); + type Item = PhpResult<(IterKey, &'a Zval)>; fn next(&mut self) -> Option { // Call next when index > 0, so next is really called at the start of each iteration, which allow to work better with generator iterator if self.zi.index > 0 { - self.zi.move_forward(); - - if !self.zi.valid() { - return None; + if let Err(err) = self.zi.move_forward() { + return Some(Err(err)); } } + match self.zi.valid() { + Err(err) => return Some(Err(err)), + Ok(false) => return None, + Ok(true) => (), + } + self.zi.index += 1; - let key = self.zi.get_current_key(); - let value = self.zi.get_current_data()?; let real_index = self.zi.index - 1; - Some(match key { - Some(key) => match IterKey::from_zval(&key) { - Some(key) => (key, value), - None => (IterKey::Long(real_index), value), + let key = match self.zi.get_current_key() { + Err(err) => return Some(Err(err)), + Ok(None) => IterKey::Long(real_index), + Ok(Some(key)) => match IterKey::from_zval(&key) { + Some(key) => key, + None => IterKey::Long(real_index), }, - None => (IterKey::Long(real_index), value), - }) + }; + + match self.zi.get_current_data() { + Err(err) => Some(Err(err)), + Ok(None) => None, + Ok(Some(value)) => Some(Ok((key, value))), + } } } diff --git a/src/zend/globals.rs b/src/zend/globals.rs index d1d047f96a..c3883faf9a 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -6,6 +6,7 @@ use std::ops::{Deref, DerefMut}; use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; +use crate::exception::PhpResult; #[cfg(php82)] use crate::ffi::zend_atomic_bool_store; use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals, zend_ini_entry}; @@ -87,6 +88,13 @@ impl ExecutorGlobals { /// could lead to a deadlock if the globals are already borrowed immutably /// or mutably. pub fn take_exception() -> Option> { + { + // This avoid a write lock if there is no exception. + if Self::get().exception.is_null() { + return None; + } + } + let mut globals = Self::get_mut(); let mut exception_ptr = std::ptr::null_mut(); @@ -96,6 +104,20 @@ impl ExecutorGlobals { Some(unsafe { ZBox::from_raw(exception_ptr.as_mut()?) }) } + /// Attempts to extract the last PHP exception captured by the interpreter. + /// Returned inside a [`PhpResult`]. + /// + /// This function requires the executor globals to be mutably held, which + /// could lead to a deadlock if the globals are already borrowed immutably + /// or mutably. + pub fn throw_if_exception() -> PhpResult<()> { + if let Some(e) = Self::take_exception() { + Err(crate::error::Error::Exception(e).into()) + } else { + Ok(()) + } + } + /// Request an interrupt of the PHP VM. This will call the registered /// interrupt handler function. /// set with [`crate::ffi::zend_interrupt_function`]. From cf6c362c00ef5b02dcfb43b6fdf776bb1488bff0 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Fri, 20 Oct 2023 16:50:52 +0200 Subject: [PATCH 043/100] feat(iterator): simply stop when there is an exception, do not take it from EG to avoid segfault --- docsrs_bindings.rs | 4 + guide/src/types/iterable.md | 2 +- guide/src/types/iterator.md | 2 +- src/types/iterable.rs | 11 +- src/types/iterator.rs | 220 ++++++++++++++++++++++++++++-------- src/types/iterator.test.php | 49 ++++++++ src/types/zval.rs | 2 + src/zend/globals.rs | 5 + 8 files changed, 243 insertions(+), 52 deletions(-) create mode 100644 src/types/iterator.test.php diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 2d224908c6..8b77bbfc97 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -17,6 +17,7 @@ pub const IS_RESOURCE: u32 = 9; pub const IS_REFERENCE: u32 = 10; pub const IS_CONSTANT_AST: u32 = 11; pub const IS_CALLABLE: u32 = 12; +pub const IS_ITERABLE: u32 = 13; pub const IS_VOID: u32 = 14; pub const IS_MIXED: u32 = 16; pub const IS_PTR: u32 = 13; @@ -1455,6 +1456,9 @@ extern "C" { named_params: *mut HashTable, ); } +extern "C" { + pub fn zend_is_iterable(iterable: *mut zval) -> bool; +} pub const _zend_expected_type_Z_EXPECTED_LONG: _zend_expected_type = 0; pub const _zend_expected_type_Z_EXPECTED_LONG_OR_NULL: _zend_expected_type = 1; pub const _zend_expected_type_Z_EXPECTED_BOOL: _zend_expected_type = 2; diff --git a/guide/src/types/iterable.md b/guide/src/types/iterable.md index f79024f760..0e689c1d91 100644 --- a/guide/src/types/iterable.md +++ b/guide/src/types/iterable.md @@ -19,7 +19,7 @@ that implements the `Traversable` interface. This means that any value that can # use ext_php_rs::types::Iterable; #[php_function] pub fn test_iterable(mut iterable: Iterable) { - for (k, v) in iterable.iter() { + for (k, v) in iterable.iter().expect("cannot get iterable") { println!("k: {} v: {}", k, v.string().unwrap()); } } diff --git a/guide/src/types/iterator.md b/guide/src/types/iterator.md index fc187c13e4..af7d09f194 100644 --- a/guide/src/types/iterator.md +++ b/guide/src/types/iterator.md @@ -20,7 +20,7 @@ used but also a the result of a `query` call with `PDO`. # use ext_php_rs::types::ZendIterator; #[php_function] pub fn test_iterator(iterator: &mut ZendIterator) { - for (k, v) in iterator.iter() { + for (k, v) in iterator.iter().expect("cannot get iterator") { println!("k: {} v: {}", k, v.string().unwrap()); } } diff --git a/src/types/iterable.rs b/src/types/iterable.rs index 7eb81e610d..dd5c13efe7 100644 --- a/src/types/iterable.rs +++ b/src/types/iterable.rs @@ -1,7 +1,6 @@ use super::array::Iter as ZendHashTableIter; use super::iterator::Iter as ZendIteratorIter; use crate::convert::FromZval; -use crate::exception::PhpResult; use crate::flags::DataType; use crate::types::iterator::IterKey; use crate::types::{ZendHashTable, ZendIterator, Zval}; @@ -16,10 +15,10 @@ pub enum Iterable<'a> { impl<'a> Iterable<'a> { /// Creates a new rust iterator from a PHP iterable. - pub fn iter(&mut self) -> PhpResult { + pub fn iter(&mut self) -> Option { match self { - Iterable::Array(array) => Ok(Iter::Array(array.iter())), - Iterable::Traversable(traversable) => Ok(Iter::Traversable(traversable.iter()?)), + Iterable::Array(array) => Some(Iter::Array(array.iter())), + Iterable::Traversable(traversable) => Some(Iter::Traversable(traversable.iter()?)), } } } @@ -47,11 +46,11 @@ pub enum Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = PhpResult<(IterKey, &'a Zval)>; + type Item = (IterKey, &'a Zval); fn next(&mut self) -> Option { match self { - Iter::Array(array) => array.next().map(Ok), + Iter::Array(array) => array.next(), Iter::Traversable(traversable) => traversable.next(), } } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 57d48bc8c8..1dbfa0b054 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -1,7 +1,6 @@ use crate::convert::{FromZval, FromZvalMut}; use crate::ffi::{zend_object_iterator, ZEND_RESULT_CODE_SUCCESS}; use crate::flags::DataType; -use crate::prelude::PhpResult; use crate::types::Zval; use crate::zend::ExecutorGlobals; use std::fmt::{Debug, Display, Formatter}; @@ -18,26 +17,31 @@ impl ZendIterator { /// # Returns /// /// Returns a iterator over the zend_object_iterator. - pub fn iter(&mut self) -> PhpResult { + pub fn iter(&mut self) -> Option { self.index = 0; - self.rewind()?; - Ok(Iter { zi: self }) + if self.rewind() { + return Some(Iter { zi: self }); + } + + None } /// Check if the current position of the iterator is valid. /// /// As an example this will call the user defined valid method of the ['\Iterator'] interface. /// see - pub fn valid(&mut self) -> PhpResult { + pub fn valid(&mut self) -> bool { if let Some(valid) = unsafe { (*self.funcs).valid } { let valid = unsafe { valid(&mut *self) == ZEND_RESULT_CODE_SUCCESS }; - ExecutorGlobals::throw_if_exception()?; + if ExecutorGlobals::has_exception() { + return false; + } - Ok(valid) + valid } else { - Ok(true) + true } } @@ -45,28 +49,38 @@ impl ZendIterator { /// /// As an example this will call the user defined rewind method of the ['\Iterator'] interface. /// see - pub fn rewind(&mut self) -> PhpResult<()> { + /// + /// # Returns + /// + /// Returns true if the iterator was successfully rewind, false otherwise. (when there is + /// an exception during rewind) + pub fn rewind(&mut self) -> bool { if let Some(rewind) = unsafe { (*self.funcs).rewind } { unsafe { rewind(&mut *self); } } - ExecutorGlobals::throw_if_exception() + !ExecutorGlobals::has_exception() } /// Move the iterator forward to the next element. /// /// As an example this will call the user defined next method of the ['\Iterator'] interface. /// see - pub fn move_forward(&mut self) -> PhpResult<()> { + /// + /// # Returns + /// + /// Returns true if the iterator was successfully move, false otherwise. (when there is + /// an exception during next) + pub fn move_forward(&mut self) -> bool { if let Some(move_forward) = unsafe { (*self.funcs).move_forward } { unsafe { move_forward(&mut *self); } } - ExecutorGlobals::throw_if_exception() + !ExecutorGlobals::has_exception() } /// Get the current data of the iterator. @@ -75,16 +89,15 @@ impl ZendIterator { /// /// Returns a reference to the current data of the iterator if available /// , ['None'] otherwise. - pub fn get_current_data<'a>(&mut self) -> PhpResult> { - let get_current_data = match unsafe { (*self.funcs).get_current_data } { - Some(get_current_data) => get_current_data, - None => return Ok(None), - }; + pub fn get_current_data<'a>(&mut self) -> Option<&'a Zval> { + let get_current_data = unsafe { (*self.funcs).get_current_data }?; let value = unsafe { &*get_current_data(&mut *self) }; - ExecutorGlobals::throw_if_exception()?; + if ExecutorGlobals::has_exception() { + return None; + } - Ok(Some(value)) + Some(value) } /// Get the current key of the iterator. @@ -93,21 +106,19 @@ impl ZendIterator { /// /// Returns a new ['Zval'] containing the current key of the iterator if available /// , ['None'] otherwise. - pub fn get_current_key(&mut self) -> PhpResult> { - let get_current_key = match unsafe { (*self.funcs).get_current_key } { - Some(get_current_key) => get_current_key, - None => return Ok(None), - }; - + pub fn get_current_key(&mut self) -> Option { + let get_current_key = unsafe { (*self.funcs).get_current_key? }; let mut key = Zval::new(); unsafe { get_current_key(&mut *self, &mut key); } - ExecutorGlobals::throw_if_exception()?; + if ExecutorGlobals::has_exception() { + return None; + } - Ok(Some(key)) + Some(key) } } @@ -164,20 +175,16 @@ pub struct Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = PhpResult<(IterKey, &'a Zval)>; + type Item = (IterKey, &'a Zval); fn next(&mut self) -> Option { // Call next when index > 0, so next is really called at the start of each iteration, which allow to work better with generator iterator - if self.zi.index > 0 { - if let Err(err) = self.zi.move_forward() { - return Some(Err(err)); - } + if self.zi.index > 0 && !self.zi.move_forward() { + return None; } - match self.zi.valid() { - Err(err) => return Some(Err(err)), - Ok(false) => return None, - Ok(true) => (), + if !self.zi.valid() { + return None; } self.zi.index += 1; @@ -185,19 +192,14 @@ impl<'a> Iterator for Iter<'a> { let real_index = self.zi.index - 1; let key = match self.zi.get_current_key() { - Err(err) => return Some(Err(err)), - Ok(None) => IterKey::Long(real_index), - Ok(Some(key)) => match IterKey::from_zval(&key) { + None => IterKey::Long(real_index), + Some(key) => match IterKey::from_zval(&key) { Some(key) => key, None => IterKey::Long(real_index), }, }; - match self.zi.get_current_data() { - Err(err) => Some(Err(err)), - Ok(None) => None, - Ok(Some(value)) => Some(Ok((key, value))), - } + self.zi.get_current_data().map(|value| (key, value)) } } @@ -208,3 +210,133 @@ impl<'a> FromZvalMut<'a> for &'a mut ZendIterator { zval.object()?.get_class_entry().get_iterator(zval, false) } } + +#[cfg(test)] +#[cfg(feature = "embed")] +mod tests { + use crate::embed::Embed; + use crate::types::iterator::IterKey; + + #[test] + fn test_generator() { + Embed::run(|| { + let result = Embed::run_script("src/types/iterator.test.php"); + + assert!(result.is_ok()); + + let generator = Embed::eval("$generator;"); + + assert!(generator.is_ok()); + + let zval = generator.unwrap(); + + assert!(zval.is_traversable()); + + let iterator = zval.traversable().unwrap(); + + assert!(iterator.valid()); + + { + let mut iter = iterator.iter().unwrap(); + + let (key, value) = iter.next().unwrap(); + + assert_eq!(key, IterKey::Long(0)); + assert!(value.is_long()); + assert_eq!(value.long().unwrap(), 1); + + let (key, value) = iter.next().unwrap(); + + assert_eq!(key, IterKey::Long(1)); + assert!(value.is_long()); + assert_eq!(value.long().unwrap(), 2); + + let (key, value) = iter.next().unwrap(); + + assert_eq!(key, IterKey::Long(2)); + assert!(value.is_long()); + assert_eq!(value.long().unwrap(), 3); + + let next = iter.next(); + + assert!(next.is_none()); + } + }); + } + + #[test] + fn test_iterator() { + Embed::run(|| { + let result = Embed::run_script("src/types/iterator.test.php"); + + assert!(result.is_ok()); + + let generator = Embed::eval("$iterator;"); + + assert!(generator.is_ok()); + + let zval = generator.unwrap(); + + assert!(zval.is_traversable()); + + let iterator = zval.traversable().unwrap(); + + assert!(iterator.valid()); + + { + let mut iter = iterator.iter().unwrap(); + + let (key, value) = iter.next().unwrap(); + + assert!(!key.is_numerical()); + assert_eq!(key, IterKey::String("key".to_string())); + assert!(value.is_string()); + assert_eq!(value.string().unwrap(), "foo"); + + let (key, value) = iter.next().unwrap(); + + assert!(key.is_numerical()); + assert_eq!(key, IterKey::Long(10)); + assert!(value.is_string()); + assert_eq!(value.string().unwrap(), "bar"); + + let (key, value) = iter.next().unwrap(); + + assert_eq!(key, IterKey::Long(2)); + assert!(value.is_string()); + assert_eq!(value.string().unwrap(), "baz"); + + let next = iter.next(); + + assert!(next.is_none()); + } + + // Test rewind + { + let mut iter = iterator.iter().unwrap(); + + let (key, value) = iter.next().unwrap(); + + assert_eq!(key, IterKey::String("key".to_string())); + assert!(value.is_string()); + assert_eq!(value.string().unwrap(), "foo"); + + let (key, value) = iter.next().unwrap(); + + assert_eq!(key, IterKey::Long(10)); + assert!(value.is_string()); + assert_eq!(value.string().unwrap(), "bar"); + + let (key, value) = iter.next().unwrap(); + + assert_eq!(key, IterKey::Long(2)); + assert!(value.is_string()); + assert_eq!(value.string().unwrap(), "baz"); + + let next = iter.next(); + + assert!(next.is_none()); + } + }); + } +} diff --git a/src/types/iterator.test.php b/src/types/iterator.test.php new file mode 100644 index 0000000000..216d663c70 --- /dev/null +++ b/src/types/iterator.test.php @@ -0,0 +1,49 @@ +count) { + 0 => 'foo', + 1 => 'bar', + 2 => 'baz', + default => null, + }; + } + + public function next() + { + $this->count++; + } + + public function key() + { + return match ($this->count) { + 0 => 'key', + 1 => 10, + 2 => 2, + default => null, + }; + } + + public function valid() + { + return $this->count < 3; + } + + public function rewind() + { + $this->count = 0; + } +} + +$generator = create_generator(); +$iterator = new TestIterator(); diff --git a/src/types/zval.rs b/src/types/zval.rs index ce4e55dded..24f45654b2 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -239,6 +239,7 @@ impl Zval { ZendCallable::new(self).ok() } + /// Returns an iterator over the zval if it is traversable. pub fn traversable(&self) -> Option<&mut ZendIterator> { if self.is_traversable() { self.object()?.get_class_entry().get_iterator(self, false) @@ -247,6 +248,7 @@ impl Zval { } } + /// Returns an iterable over the zval if it is an array or traversable. (is iterable) pub fn iterable(&self) -> Option { if self.is_iterable() { Iterable::from_zval(self) diff --git a/src/zend/globals.rs b/src/zend/globals.rs index c3883faf9a..93aff168b8 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -104,6 +104,11 @@ impl ExecutorGlobals { Some(unsafe { ZBox::from_raw(exception_ptr.as_mut()?) }) } + /// Checks if the executor globals contain an exception. + pub fn has_exception() -> bool { + !Self::get().exception.is_null() + } + /// Attempts to extract the last PHP exception captured by the interpreter. /// Returned inside a [`PhpResult`]. /// From 1b55652b7ab51f37168970f68832412550ebea30 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Fri, 20 Oct 2023 21:52:52 +0200 Subject: [PATCH 044/100] feat(embed): correctly handle panic inside embed (#272) --- src/embed/embed.c | 10 +++++++--- src/embed/embed.h | 2 +- src/embed/ffi.rs | 4 ++-- src/embed/mod.rs | 36 +++++++++++++++++++++++++++++++----- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/embed/embed.c b/src/embed/embed.c index 6aaa30c986..d8b3a78311 100644 --- a/src/embed/embed.c +++ b/src/embed/embed.c @@ -2,10 +2,14 @@ // We actually use the PHP embed API to run PHP code in test // At some point we might want to use our own SAPI to do that -void ext_php_rs_embed_callback(int argc, char** argv, void (*callback)(void *), void *ctx) { +void* ext_php_rs_embed_callback(int argc, char** argv, void* (*callback)(void *), void *ctx) { + void *result; + PHP_EMBED_START_BLOCK(argc, argv) - callback(ctx); + result = callback(ctx); PHP_EMBED_END_BLOCK() -} \ No newline at end of file + + return result; +} diff --git a/src/embed/embed.h b/src/embed/embed.h index 910dcd987e..beabffbb3a 100644 --- a/src/embed/embed.h +++ b/src/embed/embed.h @@ -1,4 +1,4 @@ #include "zend.h" #include "sapi/embed/php_embed.h" -void ext_php_rs_embed_callback(int argc, char** argv, void (*callback)(void *), void *ctx); +void* ext_php_rs_embed_callback(int argc, char** argv, void* (*callback)(void *), void *ctx); diff --git a/src/embed/ffi.rs b/src/embed/ffi.rs index 74124a9a9b..3a1f6d7413 100644 --- a/src/embed/ffi.rs +++ b/src/embed/ffi.rs @@ -10,7 +10,7 @@ extern "C" { pub fn ext_php_rs_embed_callback( argc: c_int, argv: *mut *mut c_char, - func: unsafe extern "C" fn(*const c_void), + func: unsafe extern "C" fn(*const c_void) -> *mut c_void, ctx: *const c_void, - ); + ) -> *mut c_void; } diff --git a/src/embed/mod.rs b/src/embed/mod.rs index 93e973b00c..581e385370 100644 --- a/src/embed/mod.rs +++ b/src/embed/mod.rs @@ -16,6 +16,7 @@ use crate::types::{ZendObject, Zval}; use crate::zend::ExecutorGlobals; use parking_lot::{const_rwlock, RwLock}; use std::ffi::{c_char, c_void, CString, NulError}; +use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe}; use std::path::Path; use std::ptr::null_mut; @@ -104,24 +105,41 @@ impl Embed { /// assert_eq!(foo.unwrap().string().unwrap(), "foo"); /// }); /// ``` - pub fn run(func: F) { + pub fn run(func: F) { // @TODO handle php thread safe // // This is to prevent multiple threads from running php at the same time // At some point we should detect if php is compiled with thread safety and avoid doing that in this case let _guard = RUN_FN_LOCK.write(); - unsafe extern "C" fn wrapper(ctx: *const c_void) { - (*(ctx as *const F))(); + unsafe extern "C" fn wrapper(ctx: *const c_void) -> *mut c_void { + // we try to catch panic here so we correctly shutdown php if it happens + // mandatory when we do assert on test as other test would not run correctly + let panic = catch_unwind(|| { + (*(ctx as *const F))(); + }); + + let panic_ptr = Box::into_raw(Box::new(panic)); + + panic_ptr as *mut c_void } - unsafe { + let panic = unsafe { ext_php_rs_embed_callback( 0, null_mut(), wrapper::, &func as *const F as *const c_void, - ); + ) + }; + + if panic.is_null() { + return; + } + + if let Err(err) = unsafe { *Box::from_raw(panic as *mut std::thread::Result<()>) } { + // we resume the panic here so it can be catched correctly by the test framework + resume_unwind(err); } } @@ -218,4 +236,12 @@ mod tests { assert!(!result.is_ok()); }); } + + #[test] + #[should_panic] + fn test_panic() { + Embed::run(|| { + panic!("test panic"); + }); + } } From 55724c94724c73e4d6841a052877c7426b1408c8 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 21:47:38 +0200 Subject: [PATCH 045/100] Add async support using php-tokio --- Cargo.toml | 1 + guide/src/macros/impl.md | 131 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 44 +++++++++++++ 3 files changed, 176 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index f2af9623a4..dd826bf991 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ cfg-if = "1.0" once_cell = "1.17" anyhow = { version = "1", optional = true } ext-php-rs-derive = { version = "=0.10.1", path = "./crates/macros" } +php-tokio = "0.1.4" [dev-dependencies] skeptic = "0.13" diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index 7214ab9394..522d17a06e 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -8,6 +8,8 @@ implementations cannot be exported to PHP. If you do not want a function exported to PHP, you should place it in a separate `impl` block. +If you want to use async Rust, use `#[php_async_impl]`, instead: see [here »](#async) for more info. + ## Methods Methods basically follow the same rules as functions, so read about the @@ -63,6 +65,20 @@ the attribute, the function is not exported to PHP like a regular method. Constructors cannot use the visibility or rename attributes listed above. +### Async + +Using `#[php_async_impl]` instead of `#[php_impl]` allows us to expose any async Rust library to PHP, using [PHP fibers](https://www.php.net/manual/en/language.fibers.php), [php-tokio](https://github.com/danog/php-tokio) and the [PHP Revolt event loop](https://revolt.run) under the hood to handle async interoperability. + +This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). + +Traits annotated with `#[php_impl]` can freely expose any async function, using `await` and any async Rust library. + +Make sure to also expose the `php_tokio::EventLoop::init` and `php_tokio::EventLoop::wakeup` functions to PHP in order to initialize the event loop, as specified in the full example [here &rauquo;](#async-example). + +Also, make sure to invoke `EventLoop::shutdown` in the request shutdown handler to clean up the tokio event loop before finishing the request. + +See [here &rauquo;](#async-example) for the full example. + ## Constants Constants are defined as regular Rust `impl` constants. Any type that implements @@ -162,4 +178,119 @@ var_dump(Human::get_max_age()); // int(100) var_dump(Human::MAX_AGE); // int(100) ``` +### Async example + +In this example, we're exposing an async Rust HTTP client library called [reqwest](https://docs.rs/reqwest/latest/reqwest/) to PHP, using [PHP fibers](https://www.php.net/manual/en/language.fibers.php), [php-tokio](https://github.com/danog/php-tokio) and the [PHP Revolt event loop](https://revolt.run) under the hood to handle async interoperability. + +This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). + +```rust,no_run +use ext_php_rs::prelude::*; +use php_tokio::EventLoop; + +#[php_class] +struct Client {} + +#[php_async_impl] +impl Client { + pub fn init() -> PhpResult { + EventLoop::init() + } + pub fn wakeup() -> PhpResult<()> { + EventLoop::wakeup() + } + pub async fn get(url: &str) -> anyhow::Result { + Ok(reqwest::get(url).await?.text().await?) + } +} + +pub extern "C" fn request_shutdown(_type: i32, _module_number: i32) -> i32 { + EventLoop::shutdown(); + 0 +} + +#[php_module] +pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { + module.request_shutdown_function(request_shutdown) +} +``` + +Here's the async PHP code we use to interact with the Rust class we just exposed. + +The `Client::init` method needs to be called only once in order to initialize the Revolt event loop and link it to the Tokio event loop, as shwon by the following code. + +See [here »](https://amphp.org) for more info on async PHP using [amphp](https://amphp.org) + [revolt](https://revolt.run). + +```php + \Client::wakeup()); + } + + public static function reference(): void + { + EventLoop::reference(self::$id); + } + public static function unreference(): void + { + EventLoop::unreference(self::$id); + } + + public static function __callStatic(string $name, array $args): mixed + { + return \Client::$name(...$args); + } +} + + +Client::init(); + +function test(int $delay): void +{ + $url = "https://httpbin.org/delay/$delay"; + $t = time(); + echo "Making async reqwest to $url that will return after $delay seconds...".PHP_EOL; + Client::get($url); + $t = time() - $t; + echo "Got response from $url after ~".$t." seconds!".PHP_EOL; +}; + +$futures = []; +$futures []= async(test(...), 5); +$futures []= async(test(...), 5); +$futures []= async(test(...), 5); + +await($futures); +``` + +Result: + +``` +Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... +Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... +Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... +Got response from https://httpbin.org/delay/5 after ~5 seconds! +Got response from https://httpbin.org/delay/5 after ~5 seconds! +Got response from https://httpbin.org/delay/5 after ~5 seconds! +``` + [`php_function`]: ./function.md diff --git a/src/lib.rs b/src/lib.rs index ee1d68fc80..82375a9576 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ pub mod prelude { pub use crate::php_extern; pub use crate::php_function; pub use crate::php_impl; + pub use crate::php_async_impl; pub use crate::php_module; pub use crate::php_print; pub use crate::php_println; @@ -390,6 +391,49 @@ pub use ext_php_rs_derive::php_function; /// ``` pub use ext_php_rs_derive::php_impl; +/// Just like php_impl, annotates a structs `impl` block, declaring that +/// all methods and constants declared inside the `impl` block will be declared +/// as PHP methods and constants. +/// +/// This variant of php_impl supports async Rust methods, using [php-tokio](https://github.com/danog/php-tokio) +/// to integrate [tokio](https://tokio.rs) with PHP fibers and the [Revolt](https://revolt.run) event loop, +/// compatible with [Amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl) and any other async PHP library based on Revolt. +/// +/// # Example +/// +/// ```no_run +/// # #![cfg_attr(windows, feature(abi_vectorcall))] +/// # use ext_php_rs::prelude::*; +/// use php_tokio::EventLoop; +/// +/// #[php_class] +/// struct Client {} +/// +/// #[php_async_impl] +/// impl Client { +/// pub fn init() -> PhpResult { +/// EventLoop::init() +/// } +/// pub fn wakeup() -> PhpResult<()> { +/// EventLoop::wakeup() +/// } +/// pub async fn get(url: &str) -> anyhow::Result { +/// Ok(reqwest::get(url).await?.text().await?) +/// } +/// } +/// +/// pub extern "C" fn request_shutdown(_type: i32, _module_number: i32) -> i32 { +/// EventLoop::shutdown(); +/// 0 +/// } +/// +/// #[php_module] +/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { +/// module.request_shutdown_function(request_shutdown) +/// } +/// ``` +pub use php_tokio::php_async_impl; + /// Annotates a function that will be used by PHP to retrieve information about /// the module. /// From 596768c7df12b9fcaf7c01b18e71ee274b8f6038 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 21:49:39 +0200 Subject: [PATCH 046/100] Fix typo --- guide/src/macros/impl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index 522d17a06e..e1adc503e6 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -217,7 +217,7 @@ pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { Here's the async PHP code we use to interact with the Rust class we just exposed. -The `Client::init` method needs to be called only once in order to initialize the Revolt event loop and link it to the Tokio event loop, as shwon by the following code. +The `Client::init` method needs to be called only once in order to initialize the Revolt event loop and link it to the Tokio event loop, as shown by the following code. See [here »](https://amphp.org) for more info on async PHP using [amphp](https://amphp.org) + [revolt](https://revolt.run). From e4a450f03af394cd7244d8e249585f3f2464f1a5 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 22:08:21 +0200 Subject: [PATCH 047/100] Add reqwest to dev deps --- Cargo.toml | 1 + guide/src/SUMMARY.md | 1 + guide/src/macros/impl.md | 2 ++ 3 files changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index dd826bf991..092effff25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ php-tokio = "0.1.4" [dev-dependencies] skeptic = "0.13" +reqwest = "0.11.22" [build-dependencies] anyhow = "1" diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 2ca6c5f778..4108d1bda7 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -24,6 +24,7 @@ - [Class Object](./types/class_object.md) - [Closure](./types/closure.md) - [Functions & methods](./types/functions.md) + - [Async futures](./macros/impl.md#async) - [Macros](./macros/index.md) - [Module](./macros/module.md) - [Module Startup Function](./macros/module_startup.md) diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index e1adc503e6..61b0e702d3 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -213,6 +213,8 @@ pub extern "C" fn request_shutdown(_type: i32, _module_number: i32) -> i32 { pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { module.request_shutdown_function(request_shutdown) } + +# fn main() {} ``` Here's the async PHP code we use to interact with the Rust class we just exposed. From 942ec64219552448c9c3873f44e8a34c0837aa45 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 22:16:46 +0200 Subject: [PATCH 048/100] Test --- guide/src/macros/impl.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index 61b0e702d3..25ed8f1531 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -185,7 +185,9 @@ In this example, we're exposing an async Rust HTTP client library called [reqwes This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). ```rust,no_run -use ext_php_rs::prelude::*; +# #![cfg_attr(windows, feature(abi_vectorcall))] +# extern crate ext_php_rs; +# use ext_php_rs::prelude::*; use php_tokio::EventLoop; #[php_class] From ea078bbd959b3bedd44186fa158869597cbcb8a6 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 22:21:50 +0200 Subject: [PATCH 049/100] Test --- guide/src/macros/impl.md | 6 ++++-- src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index 25ed8f1531..7ac826c385 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -182,10 +182,12 @@ var_dump(Human::MAX_AGE); // int(100) In this example, we're exposing an async Rust HTTP client library called [reqwest](https://docs.rs/reqwest/latest/reqwest/) to PHP, using [PHP fibers](https://www.php.net/manual/en/language.fibers.php), [php-tokio](https://github.com/danog/php-tokio) and the [PHP Revolt event loop](https://revolt.run) under the hood to handle async interoperability. -This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). +This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). + +Currently, only POSIX platforms are supported by php-tokio (Linux & Mac OS). ```rust,no_run -# #![cfg_attr(windows, feature(abi_vectorcall))] +# #![cfg(unix)] # extern crate ext_php_rs; # use ext_php_rs::prelude::*; use php_tokio::EventLoop; diff --git a/src/lib.rs b/src/lib.rs index 82375a9576..e232bca5af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -402,7 +402,7 @@ pub use ext_php_rs_derive::php_impl; /// # Example /// /// ```no_run -/// # #![cfg_attr(windows, feature(abi_vectorcall))] +/// # #![cfg(unix))] /// # use ext_php_rs::prelude::*; /// use php_tokio::EventLoop; /// From 0dae466e82b9647a697aed2ad01ea69eafd1a4a9 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 22:27:05 +0200 Subject: [PATCH 050/100] Bump --- Cargo.toml | 2 +- guide/src/macros/impl.md | 4 ++-- src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 092effff25..c551a8fed4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/davidcole1340/ext-php-rs" homepage = "https://github.com/davidcole1340/ext-php-rs" license = "MIT OR Apache-2.0" keywords = ["php", "ffi", "zend"] -version = "0.10.3" +version = "0.10.4" authors = ["David Cole "] edition = "2018" categories = ["api-bindings"] diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index 7ac826c385..2b1dce2080 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -184,10 +184,10 @@ In this example, we're exposing an async Rust HTTP client library called [reqwes This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). -Currently, only POSIX platforms are supported by php-tokio (Linux & Mac OS). +Currently, only Linux is supported by php-tokio. ```rust,no_run -# #![cfg(unix)] +# #![cfg(linux)] # extern crate ext_php_rs; # use ext_php_rs::prelude::*; use php_tokio::EventLoop; diff --git a/src/lib.rs b/src/lib.rs index e232bca5af..4d2abc85e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -402,7 +402,7 @@ pub use ext_php_rs_derive::php_impl; /// # Example /// /// ```no_run -/// # #![cfg(unix))] +/// # #![cfg(linux))] /// # use ext_php_rs::prelude::*; /// use php_tokio::EventLoop; /// From c0206ce6bc2833de9760510045b33d03ef047f9b Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 22:51:48 +0200 Subject: [PATCH 051/100] Add async feature --- .github/workflows/build.yml | 4 ++-- Cargo.toml | 1 + src/lib.rs | 6 +++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe68b07ed7..22c1761173 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -83,10 +83,10 @@ jobs: - name: Build env: EXT_PHP_RS_TEST: "" - run: cargo build --release --features closure,anyhow --all + run: cargo build --release --features closure,anyhow,async --all # Test & lint - name: Test inline examples - run: cargo test --release --all --features closure,anyhow + run: cargo test --release --all --features closure,anyhow,async - name: Run rustfmt if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.2' run: cargo fmt --all -- --check diff --git a/Cargo.toml b/Cargo.toml index c551a8fed4..af62e17ede 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ zip = "0.6" [features] closure = [] embed = [] +async = [] [workspace] members = ["crates/macros", "crates/cli"] diff --git a/src/lib.rs b/src/lib.rs index 4d2abc85e9..87220fd68c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ pub mod prelude { pub use crate::php_extern; pub use crate::php_function; pub use crate::php_impl; + #[cfg(any(docs, feature = "async"))] pub use crate::php_async_impl; pub use crate::php_module; pub use crate::php_print; @@ -402,7 +403,7 @@ pub use ext_php_rs_derive::php_impl; /// # Example /// /// ```no_run -/// # #![cfg(linux))] +/// # #![cfg(linux)] /// # use ext_php_rs::prelude::*; /// use php_tokio::EventLoop; /// @@ -431,7 +432,10 @@ pub use ext_php_rs_derive::php_impl; /// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { /// module.request_shutdown_function(request_shutdown) /// } +/// +/// pub fn main() {} /// ``` +#[cfg(any(docs, feature = "async"))] pub use php_tokio::php_async_impl; /// Annotates a function that will be used by PHP to retrieve information about From ce4ca0664bbe207f08815c96e496d0677ae2b6d0 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 22:58:38 +0200 Subject: [PATCH 052/100] Make dependency optional --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index af62e17ede..7a1c55119b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ cfg-if = "1.0" once_cell = "1.17" anyhow = { version = "1", optional = true } ext-php-rs-derive = { version = "=0.10.1", path = "./crates/macros" } -php-tokio = "0.1.4" +php-tokio = { version = "=0.1.4", optional = true } [dev-dependencies] skeptic = "0.13" @@ -41,7 +41,7 @@ zip = "0.6" [features] closure = [] embed = [] -async = [] +async = ["dep:php-tokio"] [workspace] members = ["crates/macros", "crates/cli"] From 924a99e73b70db4a6ac66ac8fd1da6bcad6e3cad Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 23:04:03 +0200 Subject: [PATCH 053/100] Platform-specific dependencies --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7a1c55119b..c3a5949681 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ cfg-if = "1.0" once_cell = "1.17" anyhow = { version = "1", optional = true } ext-php-rs-derive = { version = "=0.10.1", path = "./crates/macros" } + +[target.'cfg(linux)'.dependencies] php-tokio = { version = "=0.1.4", optional = true } [dev-dependencies] From 8890767ba92c43ac8141d32f2b5f588f1456face Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 23:21:23 +0200 Subject: [PATCH 054/100] Just add docs --- .github/workflows/build.yml | 4 ++-- Cargo.toml | 7 ++---- guide/src/macros/impl.md | 12 +++------- src/lib.rs | 48 ------------------------------------- 4 files changed, 7 insertions(+), 64 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22c1761173..fe68b07ed7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -83,10 +83,10 @@ jobs: - name: Build env: EXT_PHP_RS_TEST: "" - run: cargo build --release --features closure,anyhow,async --all + run: cargo build --release --features closure,anyhow --all # Test & lint - name: Test inline examples - run: cargo test --release --all --features closure,anyhow,async + run: cargo test --release --all --features closure,anyhow - name: Run rustfmt if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.2' run: cargo fmt --all -- --check diff --git a/Cargo.toml b/Cargo.toml index c3a5949681..727fb37260 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/davidcole1340/ext-php-rs" homepage = "https://github.com/davidcole1340/ext-php-rs" license = "MIT OR Apache-2.0" keywords = ["php", "ffi", "zend"] -version = "0.10.4" +version = "0.10.3" authors = ["David Cole "] edition = "2018" categories = ["api-bindings"] @@ -19,12 +19,10 @@ once_cell = "1.17" anyhow = { version = "1", optional = true } ext-php-rs-derive = { version = "=0.10.1", path = "./crates/macros" } -[target.'cfg(linux)'.dependencies] -php-tokio = { version = "=0.1.4", optional = true } - [dev-dependencies] skeptic = "0.13" reqwest = "0.11.22" +php-tokio = "0.1.4" [build-dependencies] anyhow = "1" @@ -43,7 +41,6 @@ zip = "0.6" [features] closure = [] embed = [] -async = ["dep:php-tokio"] [workspace] members = ["crates/macros", "crates/cli"] diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index 2b1dce2080..660f3745da 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -184,13 +184,9 @@ In this example, we're exposing an async Rust HTTP client library called [reqwes This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). -Currently, only Linux is supported by php-tokio. - -```rust,no_run -# #![cfg(linux)] -# extern crate ext_php_rs; -# use ext_php_rs::prelude::*; -use php_tokio::EventLoop; +```rust,ignore +use ext_php_rs::prelude::*; +use php_tokio::{php_async_impl, EventLoop}; #[php_class] struct Client {} @@ -217,8 +213,6 @@ pub extern "C" fn request_shutdown(_type: i32, _module_number: i32) -> i32 { pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { module.request_shutdown_function(request_shutdown) } - -# fn main() {} ``` Here's the async PHP code we use to interact with the Rust class we just exposed. diff --git a/src/lib.rs b/src/lib.rs index 87220fd68c..ee1d68fc80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,8 +48,6 @@ pub mod prelude { pub use crate::php_extern; pub use crate::php_function; pub use crate::php_impl; - #[cfg(any(docs, feature = "async"))] - pub use crate::php_async_impl; pub use crate::php_module; pub use crate::php_print; pub use crate::php_println; @@ -392,52 +390,6 @@ pub use ext_php_rs_derive::php_function; /// ``` pub use ext_php_rs_derive::php_impl; -/// Just like php_impl, annotates a structs `impl` block, declaring that -/// all methods and constants declared inside the `impl` block will be declared -/// as PHP methods and constants. -/// -/// This variant of php_impl supports async Rust methods, using [php-tokio](https://github.com/danog/php-tokio) -/// to integrate [tokio](https://tokio.rs) with PHP fibers and the [Revolt](https://revolt.run) event loop, -/// compatible with [Amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl) and any other async PHP library based on Revolt. -/// -/// # Example -/// -/// ```no_run -/// # #![cfg(linux)] -/// # use ext_php_rs::prelude::*; -/// use php_tokio::EventLoop; -/// -/// #[php_class] -/// struct Client {} -/// -/// #[php_async_impl] -/// impl Client { -/// pub fn init() -> PhpResult { -/// EventLoop::init() -/// } -/// pub fn wakeup() -> PhpResult<()> { -/// EventLoop::wakeup() -/// } -/// pub async fn get(url: &str) -> anyhow::Result { -/// Ok(reqwest::get(url).await?.text().await?) -/// } -/// } -/// -/// pub extern "C" fn request_shutdown(_type: i32, _module_number: i32) -> i32 { -/// EventLoop::shutdown(); -/// 0 -/// } -/// -/// #[php_module] -/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { -/// module.request_shutdown_function(request_shutdown) -/// } -/// -/// pub fn main() {} -/// ``` -#[cfg(any(docs, feature = "async"))] -pub use php_tokio::php_async_impl; - /// Annotates a function that will be used by PHP to retrieve information about /// the module. /// From a595e1d4371d7ca075dabe10a799823595b34737 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 23:21:51 +0200 Subject: [PATCH 055/100] Cleanup --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 727fb37260..f2af9623a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,6 @@ ext-php-rs-derive = { version = "=0.10.1", path = "./crates/macros" } [dev-dependencies] skeptic = "0.13" -reqwest = "0.11.22" -php-tokio = "0.1.4" [build-dependencies] anyhow = "1" From e80bebfc4c422f9313bb31d5f6d62b2a93dd9da2 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 23:22:47 +0200 Subject: [PATCH 056/100] Cleanup --- guide/src/macros/impl.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index 660f3745da..fa9c7307d3 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -184,6 +184,8 @@ In this example, we're exposing an async Rust HTTP client library called [reqwes This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). +Make sure to require [php-tokio](https://github.com/danog/php-tokio) as a dependency before proceeding. + ```rust,ignore use ext_php_rs::prelude::*; use php_tokio::{php_async_impl, EventLoop}; From 8462005af491c26cea47f0ab04be77d5c97f590d Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 21 Oct 2023 23:23:08 +0200 Subject: [PATCH 057/100] Cleanup --- guide/src/macros/impl.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index fa9c7307d3..1b872f1944 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -73,11 +73,11 @@ This allows full compatibility with [amphp](https://amphp.org), [PSL](https://gi Traits annotated with `#[php_impl]` can freely expose any async function, using `await` and any async Rust library. -Make sure to also expose the `php_tokio::EventLoop::init` and `php_tokio::EventLoop::wakeup` functions to PHP in order to initialize the event loop, as specified in the full example [here &rauquo;](#async-example). +Make sure to also expose the `php_tokio::EventLoop::init` and `php_tokio::EventLoop::wakeup` functions to PHP in order to initialize the event loop, as specified in the full example [here »](#async-example). Also, make sure to invoke `EventLoop::shutdown` in the request shutdown handler to clean up the tokio event loop before finishing the request. -See [here &rauquo;](#async-example) for the full example. +See [here »](#async-example) for the full example. ## Constants From dddc07f587cd5d40f3b3ff64b9a59fba8299d953 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 23 Oct 2023 11:42:01 +0200 Subject: [PATCH 058/100] feat(test): add an example on how to test a module (#276) --- allowed_bindings.rs | 3 ++- tests/module.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/module.rs diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 49b15856d8..4a5dbfc4d7 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -260,5 +260,6 @@ bind! { zend_eval_string, zend_file_handle, zend_stream_init_filename, - php_execute_script + php_execute_script, + zend_register_module_ex } diff --git a/tests/module.rs b/tests/module.rs new file mode 100644 index 0000000000..f1ef22b852 --- /dev/null +++ b/tests/module.rs @@ -0,0 +1,44 @@ +#![cfg_attr(windows, feature(abi_vectorcall))] +extern crate ext_php_rs; + +#[cfg(feature = "embed")] +use ext_php_rs::embed::Embed; +#[cfg(feature = "embed")] +use ext_php_rs::ffi::zend_register_module_ex; +use ext_php_rs::prelude::*; + +#[test] +#[cfg(feature = "embed")] +fn test_module() { + Embed::run(|| { + // Allow to load the module + unsafe { zend_register_module_ex(get_module()) }; + + let result = Embed::eval("$foo = hello_world('foo');"); + + assert!(result.is_ok()); + + let zval = result.unwrap(); + + assert!(zval.is_string()); + + let string = zval.string().unwrap(); + + assert_eq!(string.to_string(), "Hello, foo!"); + }); +} + +/// Gives you a nice greeting! +/// +/// @param string $name Your name. +/// +/// @return string Nice greeting! +#[php_function] +pub fn hello_world(name: String) -> String { + format!("Hello, {}!", name) +} + +#[php_module] +pub fn module(module: ModuleBuilder) -> ModuleBuilder { + module +} From fbb0b41fdc029cc182102cd39806341d78a91ee3 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Tue, 24 Oct 2023 20:16:53 +0200 Subject: [PATCH 059/100] Forward ClassEntry in create_object See #138 --- src/builders/class.rs | 4 ++-- src/types/class_object.rs | 14 +++++++------- src/zend/handlers.rs | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/builders/class.rs b/src/builders/class.rs index 91a6a010fc..28e8463841 100644 --- a/src/builders/class.rs +++ b/src/builders/class.rs @@ -161,10 +161,10 @@ impl ClassBuilder { /// Panics if the class name associated with `T` is not the same as the /// class name specified when creating the builder. pub fn object_override(mut self) -> Self { - extern "C" fn create_object(_: *mut ClassEntry) -> *mut ZendObject { + extern "C" fn create_object(ce: *mut ClassEntry) -> *mut ZendObject { // SAFETY: After calling this function, PHP will always call the constructor // defined below, which assumes that the object is uninitialized. - let obj = unsafe { ZendClassObject::::new_uninit() }; + let obj = unsafe { ZendClassObject::::new_uninit(ce.as_ref()) }; obj.into_raw().get_mut_zend_obj() } diff --git a/src/types/class_object.rs b/src/types/class_object.rs index e9b116529b..c9b8b482c2 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -15,10 +15,10 @@ use crate::{ error::{Error, Result}, ffi::{ ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, object_properties_init, - zend_object, zend_object_std_init, zend_objects_clone_members, + zend_object, zend_object_std_init, zend_objects_clone_members, _zend_class_entry, }, flags::DataType, - types::{ZendObject, Zval}, + types::{ZendObject, Zval}, zend::ClassEntry, }; /// Representation of a Zend class object in memory. @@ -43,7 +43,7 @@ impl ZendClassObject { /// Panics if memory was unable to be allocated for the new object. pub fn new(val: T) -> ZBox { // SAFETY: We are providing a value to initialize the object with. - unsafe { Self::internal_new(Some(val)) } + unsafe { Self::internal_new(Some(val), None) } } /// Creates a new [`ZendClassObject`] of type `T`, with an uninitialized @@ -67,8 +67,8 @@ impl ZendClassObject { /// # Panics /// /// Panics if memory was unable to be allocated for the new object. - pub unsafe fn new_uninit() -> ZBox { - Self::internal_new(None) + pub unsafe fn new_uninit(ce: Option<&'static ClassEntry>) -> ZBox { + Self::internal_new(None, ce) } /// Creates a new [`ZendObject`] of type `T`, storing the given (and @@ -102,10 +102,10 @@ impl ZendClassObject { /// # Panics /// /// Panics if memory was unable to be allocated for the new object. - unsafe fn internal_new(val: Option) -> ZBox { + unsafe fn internal_new(val: Option, ce: Option<&'static ClassEntry>) -> ZBox { let size = mem::size_of::>(); let meta = T::get_metadata(); - let ce = meta.ce() as *const _ as *mut _; + let ce = ce.unwrap_or_else(||meta.ce()) as *const _ as *mut _; let obj = ext_php_rs_zend_object_alloc(size as _, ce) as *mut ZendClassObject; let obj = obj .as_mut() diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index 11d220ba08..f2d71b0ae5 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -52,13 +52,13 @@ impl ZendObjectHandlers { } unsafe extern "C" fn free_obj(object: *mut ZendObject) { - let obj = object + object .as_mut() .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .expect("Invalid object pointer given for `free_obj`"); + .map(|obj|ptr::drop_in_place(&mut obj.obj)); // Manually drop the object as we don't want to free the underlying memory. - ptr::drop_in_place(&mut obj.obj); + zend_object_std_dtor(object) } From 5fdd8fac4481273e1e651a9433556a4785824744 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Wed, 25 Oct 2023 15:15:43 +0200 Subject: [PATCH 060/100] feat(zend): add helper for try catch and bailout in PHP (#275) * feat(zend): add helper for try catch and bailout in PHP * feat(try): add bindings for bailout * fix(try): add missing feature flag for test * feat(try): add a test that expose memory leak problem * feat(try): make bailout unsafe and explain why * feat(bailout): flag bailout as a panic function * feat(embed): add try catch on script / eval --- allowed_bindings.rs | 3 +- build.rs | 2 + docsrs_bindings.rs | 3 + src/embed/embed.c | 2 +- src/embed/ffi.rs | 2 +- src/embed/mod.rs | 92 +++++++++++++++--------- src/ffi.rs | 6 ++ src/wrapper.c | 14 ++++ src/wrapper.h | 4 +- src/zend/mod.rs | 4 ++ src/zend/try_catch.rs | 164 ++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 259 insertions(+), 37 deletions(-) create mode 100644 src/zend/try_catch.rs diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 4a5dbfc4d7..ca2195bde0 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -261,5 +261,6 @@ bind! { zend_file_handle, zend_stream_init_filename, php_execute_script, - zend_register_module_ex + zend_register_module_ex, + _zend_bailout } diff --git a/build.rs b/build.rs index 4ee804171f..78c9cf3226 100644 --- a/build.rs +++ b/build.rs @@ -248,6 +248,8 @@ fn main() -> Result<()> { for path in [ manifest.join("src").join("wrapper.h"), manifest.join("src").join("wrapper.c"), + manifest.join("src").join("embed").join("embed.h"), + manifest.join("src").join("embed").join("embed.c"), manifest.join("allowed_bindings.rs"), manifest.join("windows_build.rs"), manifest.join("unix_build.rs"), diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 2d224908c6..f7e54f1e25 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -789,6 +789,9 @@ pub struct _zend_class_entry__bindgen_ty_4__bindgen_ty_2 { pub builtin_functions: *const _zend_function_entry, pub module: *mut _zend_module_entry, } +extern "C" { + pub fn _zend_bailout(filename: *const ::std::os::raw::c_char, lineno: u32) -> !; +} extern "C" { pub static mut zend_interrupt_function: ::std::option::Option; diff --git a/src/embed/embed.c b/src/embed/embed.c index d8b3a78311..ae7d8bc625 100644 --- a/src/embed/embed.c +++ b/src/embed/embed.c @@ -3,7 +3,7 @@ // We actually use the PHP embed API to run PHP code in test // At some point we might want to use our own SAPI to do that void* ext_php_rs_embed_callback(int argc, char** argv, void* (*callback)(void *), void *ctx) { - void *result; + void *result = NULL; PHP_EMBED_START_BLOCK(argc, argv) diff --git a/src/embed/ffi.rs b/src/embed/ffi.rs index 3a1f6d7413..b52ce6a92a 100644 --- a/src/embed/ffi.rs +++ b/src/embed/ffi.rs @@ -10,7 +10,7 @@ extern "C" { pub fn ext_php_rs_embed_callback( argc: c_int, argv: *mut *mut c_char, - func: unsafe extern "C" fn(*const c_void) -> *mut c_void, + func: unsafe extern "C" fn(*const c_void) -> *const c_void, ctx: *const c_void, ) -> *mut c_void; } diff --git a/src/embed/mod.rs b/src/embed/mod.rs index 581e385370..0ad64a6159 100644 --- a/src/embed/mod.rs +++ b/src/embed/mod.rs @@ -13,10 +13,10 @@ use crate::ffi::{ zend_stream_init_filename, ZEND_RESULT_CODE_SUCCESS, }; use crate::types::{ZendObject, Zval}; -use crate::zend::ExecutorGlobals; +use crate::zend::{panic_wrapper, try_catch, ExecutorGlobals}; use parking_lot::{const_rwlock, RwLock}; use std::ffi::{c_char, c_void, CString, NulError}; -use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe}; +use std::panic::{resume_unwind, RefUnwindSafe}; use std::path::Path; use std::ptr::null_mut; @@ -29,6 +29,13 @@ pub enum EmbedError { ExecuteScriptError, InvalidEvalString(NulError), InvalidPath, + CatchError, +} + +impl EmbedError { + pub fn is_bailout(&self) -> bool { + matches!(self, EmbedError::CatchError) + } } static RUN_FN_LOCK: RwLock<()> = const_rwlock(()); @@ -79,10 +86,12 @@ impl Embed { zend_stream_init_filename(&mut file_handle, path.as_ptr()); } - if unsafe { php_execute_script(&mut file_handle) } { - Ok(()) - } else { - Err(EmbedError::ExecuteScriptError) + let exec_result = try_catch(|| unsafe { php_execute_script(&mut file_handle) }); + + match exec_result { + Err(_) => Err(EmbedError::CatchError), + Ok(true) => Ok(()), + Ok(false) => Err(EmbedError::ExecuteScriptError), } } @@ -93,6 +102,12 @@ impl Embed { /// Which means subsequent calls to `Embed::eval` or `Embed::run_script` will be able to access /// variables defined in previous calls /// + /// # Returns + /// + /// * R - The result of the function passed to this method + /// + /// R must implement [`Default`] so it can be returned in case of a bailout + /// /// # Example /// /// ``` @@ -105,41 +120,36 @@ impl Embed { /// assert_eq!(foo.unwrap().string().unwrap(), "foo"); /// }); /// ``` - pub fn run(func: F) { + pub fn run R + RefUnwindSafe>(func: F) -> R + where + R: Default, + { // @TODO handle php thread safe // // This is to prevent multiple threads from running php at the same time // At some point we should detect if php is compiled with thread safety and avoid doing that in this case let _guard = RUN_FN_LOCK.write(); - unsafe extern "C" fn wrapper(ctx: *const c_void) -> *mut c_void { - // we try to catch panic here so we correctly shutdown php if it happens - // mandatory when we do assert on test as other test would not run correctly - let panic = catch_unwind(|| { - (*(ctx as *const F))(); - }); - - let panic_ptr = Box::into_raw(Box::new(panic)); - - panic_ptr as *mut c_void - } - let panic = unsafe { ext_php_rs_embed_callback( 0, null_mut(), - wrapper::, + panic_wrapper::, &func as *const F as *const c_void, ) }; + // This can happen if there is a bailout if panic.is_null() { - return; + return R::default(); } - if let Err(err) = unsafe { *Box::from_raw(panic as *mut std::thread::Result<()>) } { - // we resume the panic here so it can be catched correctly by the test framework - resume_unwind(err); + match unsafe { *Box::from_raw(panic as *mut std::thread::Result) } { + Ok(r) => r, + Err(err) => { + // we resume the panic here so it can be catched correctly by the test framework + resume_unwind(err); + } } } @@ -170,21 +180,18 @@ impl Embed { let mut result = Zval::new(); - // this eval is very limited as it only allow simple code, it's the same eval used by php -r - let exec_result = unsafe { + let exec_result = try_catch(|| unsafe { zend_eval_string( cstr.as_ptr() as *const c_char, &mut result, b"run\0".as_ptr() as *const _, ) - }; - - let exception = ExecutorGlobals::take_exception(); + }); - if exec_result != ZEND_RESULT_CODE_SUCCESS { - Err(EmbedError::ExecuteError(exception)) - } else { - Ok(result) + match exec_result { + Err(_) => Err(EmbedError::CatchError), + Ok(ZEND_RESULT_CODE_SUCCESS) => Ok(result), + Ok(_) => Err(EmbedError::ExecuteError(ExecutorGlobals::take_exception())), } } } @@ -244,4 +251,23 @@ mod tests { panic!("test panic"); }); } + + #[test] + fn test_return() { + let foo = Embed::run(|| { + return "foo"; + }); + + assert_eq!(foo, "foo"); + } + + #[test] + fn test_eval_bailout() { + Embed::run(|| { + let result = Embed::eval("str_repeat('a', 100_000_000_000_000);"); + + assert!(result.is_err()); + assert!(result.unwrap_err().is_bailout()); + }); + } } diff --git a/src/ffi.rs b/src/ffi.rs index 92614c475e..a6c3d94865 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -26,6 +26,12 @@ extern "C" { pub fn ext_php_rs_zend_object_alloc(obj_size: usize, ce: *mut zend_class_entry) -> *mut c_void; pub fn ext_php_rs_zend_object_release(obj: *mut zend_object); pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals; + pub fn ext_php_rs_zend_try_catch( + func: unsafe extern "C" fn(*const c_void) -> *const c_void, + ctx: *const c_void, + result: *mut *mut c_void, + ) -> bool; + pub fn ext_php_rs_zend_bailout() -> !; } include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/src/wrapper.c b/src/wrapper.c index faf585e417..a1fd900ff5 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -39,3 +39,17 @@ zend_executor_globals *ext_php_rs_executor_globals() { return &executor_globals; #endif } + +bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result) { + zend_try { + *result = callback(ctx); + } zend_catch { + return true; + } zend_end_try(); + + return false; +} + +void ext_php_rs_zend_bailout() { + zend_bailout(); +} diff --git a/src/wrapper.h b/src/wrapper.h index ed9dea6294..e4e55517ca 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -30,4 +30,6 @@ void ext_php_rs_set_known_valid_utf8(zend_string *zs); const char *ext_php_rs_php_build_id(); void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce); void ext_php_rs_zend_object_release(zend_object *obj); -zend_executor_globals *ext_php_rs_executor_globals(); \ No newline at end of file +zend_executor_globals *ext_php_rs_executor_globals(); +bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result); +void ext_php_rs_zend_bailout(); diff --git a/src/zend/mod.rs b/src/zend/mod.rs index b3b1cfbdb3..af8a5c2d8e 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -9,6 +9,7 @@ mod globals; mod handlers; mod ini_entry_def; mod module; +mod try_catch; use crate::{error::Result, ffi::php_printf}; use std::ffi::CString; @@ -22,6 +23,9 @@ pub use globals::ExecutorGlobals; pub use handlers::ZendObjectHandlers; pub use ini_entry_def::IniEntryDef; pub use module::ModuleEntry; +#[cfg(feature = "embed")] +pub(crate) use try_catch::panic_wrapper; +pub use try_catch::{bailout, try_catch}; // Used as the format string for `php_printf`. const FORMAT_STR: &[u8] = b"%s\0"; diff --git a/src/zend/try_catch.rs b/src/zend/try_catch.rs new file mode 100644 index 0000000000..f74b427a59 --- /dev/null +++ b/src/zend/try_catch.rs @@ -0,0 +1,164 @@ +use crate::ffi::{ext_php_rs_zend_bailout, ext_php_rs_zend_try_catch}; +use std::ffi::c_void; +use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe}; +use std::ptr::null_mut; + +#[derive(Debug)] +pub struct CatchError; + +pub(crate) unsafe extern "C" fn panic_wrapper R + RefUnwindSafe>( + ctx: *const c_void, +) -> *const c_void { + // we try to catch panic here so we correctly shutdown php if it happens + // mandatory when we do assert on test as other test would not run correctly + let panic = catch_unwind(|| (*(ctx as *mut F))()); + + Box::into_raw(Box::new(panic)) as *mut c_void +} + +/// PHP propose a try catch mechanism in C using setjmp and longjmp (bailout) +/// It store the arg of setjmp into the bailout field of the global executor +/// If a bailout is triggered, the executor will jump to the setjmp and restore the previous setjmp +/// +/// try_catch allow to use this mechanism +/// +/// # Returns +/// +/// * `Ok(R)` - The result of the function +/// * `Err(CatchError)` - A bailout occurred during the execution +pub fn try_catch R + RefUnwindSafe>(func: F) -> Result { + let mut panic_ptr = null_mut(); + let has_bailout = unsafe { + ext_php_rs_zend_try_catch( + panic_wrapper::, + &func as *const F as *const c_void, + (&mut panic_ptr) as *mut *mut c_void, + ) + }; + + let panic = panic_ptr as *mut std::thread::Result; + + // can be null if there is a bailout + if panic.is_null() || has_bailout { + return Err(CatchError); + } + + match unsafe { *Box::from_raw(panic as *mut std::thread::Result) } { + Ok(r) => Ok(r), + Err(err) => { + // we resume the panic here so it can be catched correctly by the test framework + resume_unwind(err); + } + } +} + +/// Trigger a bailout +/// +/// This function will stop the execution of the current script +/// and jump to the last try catch block +/// +/// # Safety +/// +/// This function is unsafe because it can cause memory leaks +/// Since it will jump to the last try catch block, it will not call the destructor of the current scope +/// +/// When using this function you should ensure that all the memory allocated in the current scope is released +/// +pub unsafe fn bailout() -> ! { + ext_php_rs_zend_bailout(); +} + +#[cfg(feature = "embed")] +#[cfg(test)] +mod tests { + use crate::embed::Embed; + use crate::zend::{bailout, try_catch}; + use std::ptr::null_mut; + + #[test] + fn test_catch() { + Embed::run(|| { + let catch = try_catch(|| { + unsafe { + bailout(); + } + + #[allow(unreachable_code)] + { + assert!(false); + } + }); + + assert!(catch.is_err()); + }); + } + + #[test] + fn test_no_catch() { + Embed::run(|| { + let catch = try_catch(|| { + assert!(true); + }); + + assert!(catch.is_ok()); + }); + } + + #[test] + fn test_bailout() { + Embed::run(|| { + unsafe { + bailout(); + } + + #[allow(unreachable_code)] + { + assert!(false); + } + }); + } + + #[test] + #[should_panic] + fn test_panic() { + Embed::run(|| { + let _ = try_catch(|| { + panic!("should panic"); + }); + }); + } + + #[test] + fn test_return() { + let foo = Embed::run(|| { + let result = try_catch(|| { + return "foo"; + }); + + assert!(result.is_ok()); + + result.unwrap() + }); + + assert_eq!(foo, "foo"); + } + + #[test] + fn test_memory_leak() { + let mut ptr = null_mut(); + + let _ = try_catch(|| { + let mut result = "foo".to_string(); + ptr = &mut result; + + unsafe { + bailout(); + } + }); + + // Check that the string is never released + let result = unsafe { &*ptr as &str }; + + assert_eq!(result, "foo"); + } +} From 3688aac183369bb948e53230612c4571c2654fbd Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 25 Oct 2023 20:34:49 +0200 Subject: [PATCH 061/100] Check instance_of() so subclasses also work --- src/types/class_object.rs | 2 +- src/zend/handlers.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/types/class_object.rs b/src/types/class_object.rs index c9b8b482c2..099b72673b 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -167,7 +167,7 @@ impl ZendClassObject { (ptr as *mut Self).as_mut()? }; - if ptr.std.is_instance::() { + if ptr.std.instance_of(T::get_metadata().ce()) { Some(ptr) } else { None diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index f2d71b0ae5..11d220ba08 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -52,13 +52,13 @@ impl ZendObjectHandlers { } unsafe extern "C" fn free_obj(object: *mut ZendObject) { - object + let obj = object .as_mut() .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .map(|obj|ptr::drop_in_place(&mut obj.obj)); + .expect("Invalid object pointer given for `free_obj`"); // Manually drop the object as we don't want to free the underlying memory. - + ptr::drop_in_place(&mut obj.obj); zend_object_std_dtor(object) } From 8009a91e1389ec1c0598b85db8b120998bf8c28f Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 25 Oct 2023 20:40:11 +0200 Subject: [PATCH 062/100] Cargo fmt --- src/types/class_object.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/types/class_object.rs b/src/types/class_object.rs index 099b72673b..811d446cbc 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -14,11 +14,12 @@ use crate::{ convert::{FromZendObject, FromZendObjectMut, FromZval, FromZvalMut, IntoZval}, error::{Error, Result}, ffi::{ - ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, object_properties_init, - zend_object, zend_object_std_init, zend_objects_clone_members, _zend_class_entry, + _zend_class_entry, ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, + object_properties_init, zend_object, zend_object_std_init, zend_objects_clone_members, }, flags::DataType, - types::{ZendObject, Zval}, zend::ClassEntry, + types::{ZendObject, Zval}, + zend::ClassEntry, }; /// Representation of a Zend class object in memory. @@ -105,7 +106,7 @@ impl ZendClassObject { unsafe fn internal_new(val: Option, ce: Option<&'static ClassEntry>) -> ZBox { let size = mem::size_of::>(); let meta = T::get_metadata(); - let ce = ce.unwrap_or_else(||meta.ce()) as *const _ as *mut _; + let ce = ce.unwrap_or_else(|| meta.ce()) as *const _ as *mut _; let obj = ext_php_rs_zend_object_alloc(size as _, ce) as *mut ZendClassObject; let obj = obj .as_mut() From 20c0d9c8cc66d139241e20a8a524657b3982b523 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 26 Oct 2023 16:05:55 +0200 Subject: [PATCH 063/100] Remove unused class --- src/types/class_object.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/class_object.rs b/src/types/class_object.rs index 811d446cbc..f6124e6062 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -14,8 +14,8 @@ use crate::{ convert::{FromZendObject, FromZendObjectMut, FromZval, FromZvalMut, IntoZval}, error::{Error, Result}, ffi::{ - _zend_class_entry, ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, - object_properties_init, zend_object, zend_object_std_init, zend_objects_clone_members, + ext_php_rs_zend_object_alloc, ext_php_rs_zend_object_release, object_properties_init, + zend_object, zend_object_std_init, zend_objects_clone_members, }, flags::DataType, types::{ZendObject, Zval}, From bd54b3879f5b67ba0b43d1d0ecb7362b2b5f1974 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 17 Nov 2023 18:38:06 +0100 Subject: [PATCH 064/100] Fix #279 --- src/types/class_object.rs | 4 ++-- src/types/object.rs | 4 ++-- src/zend/function.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/class_object.rs b/src/types/class_object.rs index e9b116529b..753933d393 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -5,7 +5,7 @@ use std::{ fmt::Debug, mem, ops::{Deref, DerefMut}, - ptr::{self, NonNull}, + ptr::{self, NonNull}, os::raw::c_char, }; use crate::{ @@ -161,7 +161,7 @@ impl ZendClassObject { } fn _from_zend_obj(std: &zend_object) -> Option<&mut Self> { - let std = std as *const zend_object as *const i8; + let std = std as *const zend_object as *const c_char; let ptr = unsafe { let ptr = std.offset(0 - Self::std_offset() as isize) as *const Self; (ptr as *mut Self).as_mut()? diff --git a/src/types/object.rs b/src/types/object.rs index ca8e526ebe..f17b65fd23 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -1,7 +1,7 @@ //! Represents an object in PHP. Allows for overriding the internal object used //! by classes, allowing users to store Rust data inside a PHP object. -use std::{convert::TryInto, fmt::Debug, ops::DerefMut}; +use std::{convert::TryInto, fmt::Debug, ops::DerefMut, os::raw::c_char}; use crate::{ boxed::{ZBox, ZBoxable}, @@ -146,7 +146,7 @@ impl ZendObject { unsafe { let res = zend_hash_str_find_ptr_lc( &(*self.ce).function_table, - name.as_ptr() as *const i8, + name.as_ptr() as *const c_char, name.len(), ) as *mut zend_function; if res.is_null() { diff --git a/src/zend/function.rs b/src/zend/function.rs index a1ef8663cc..fb5c4d4bd2 100644 --- a/src/zend/function.rs +++ b/src/zend/function.rs @@ -52,7 +52,7 @@ pub type Function = zend_function; impl Function { pub fn try_from_function(name: &str) -> Option { unsafe { - let res = zend_fetch_function_str(name.as_ptr() as *const i8, name.len()); + let res = zend_fetch_function_str(name.as_ptr() as *const c_char, name.len()); if res.is_null() { return None; } @@ -65,7 +65,7 @@ impl Function { Some(ce) => unsafe { let res = zend_hash_str_find_ptr_lc( &ce.function_table, - name.as_ptr() as *const i8, + name.as_ptr() as *const c_char, name.len(), ) as *mut zend_function; if res.is_null() { From 602a5830ae19787e27f1e669ba02a897b4a88d94 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 17 Nov 2023 18:39:02 +0100 Subject: [PATCH 065/100] Bump --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f2af9623a4..a484175510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/davidcole1340/ext-php-rs" homepage = "https://github.com/davidcole1340/ext-php-rs" license = "MIT OR Apache-2.0" keywords = ["php", "ffi", "zend"] -version = "0.10.3" +version = "0.10.4" authors = ["David Cole "] edition = "2018" categories = ["api-bindings"] From c87dc4b9d980e72eb760dcda3c869f7882c3c204 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 17 Nov 2023 18:42:03 +0100 Subject: [PATCH 066/100] Fmt --- src/embed/mod.rs | 26 +++++++++++++++----------- src/types/class_object.rs | 3 ++- src/zend/ini_entry_def.rs | 5 +++-- src/zend/try_catch.rs | 10 ++++++---- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/embed/mod.rs b/src/embed/mod.rs index 0ad64a6159..d8f914ff78 100644 --- a/src/embed/mod.rs +++ b/src/embed/mod.rs @@ -1,8 +1,9 @@ //! Provides implementations for running php code from rust. //! It only works on linux for now and you should have `php-embed` installed //! -//! This crate was only test with PHP 8.2 please report any issue with other version -//! You should only use this crate for test purpose, it's not production ready +//! This crate was only test with PHP 8.2 please report any issue with other +//! version You should only use this crate for test purpose, it's not production +//! ready mod ffi; @@ -43,13 +44,14 @@ static RUN_FN_LOCK: RwLock<()> = const_rwlock(()); impl Embed { /// Run a php script from a file /// - /// This function will only work correctly when used inside the `Embed::run` function - /// otherwise behavior is unexpected + /// This function will only work correctly when used inside the `Embed::run` + /// function otherwise behavior is unexpected /// /// # Returns /// /// * `Ok(())` - The script was executed successfully - /// * `Err(EmbedError)` - An error occured during the execution of the script + /// * `Err(EmbedError)` - An error occured during the execution of the + /// script /// /// # Example /// @@ -97,10 +99,10 @@ impl Embed { /// Start and run embed sapi engine /// - /// This function will allow to run php code from rust, the same PHP context is keep between calls - /// inside the function passed to this method. - /// Which means subsequent calls to `Embed::eval` or `Embed::run_script` will be able to access - /// variables defined in previous calls + /// This function will allow to run php code from rust, the same PHP context + /// is keep between calls inside the function passed to this method. + /// Which means subsequent calls to `Embed::eval` or `Embed::run_script` + /// will be able to access variables defined in previous calls /// /// # Returns /// @@ -127,7 +129,8 @@ impl Embed { // @TODO handle php thread safe // // This is to prevent multiple threads from running php at the same time - // At some point we should detect if php is compiled with thread safety and avoid doing that in this case + // At some point we should detect if php is compiled with thread safety and + // avoid doing that in this case let _guard = RUN_FN_LOCK.write(); let panic = unsafe { @@ -155,7 +158,8 @@ impl Embed { /// Evaluate a php code /// - /// This function will only work correctly when used inside the `Embed::run` function + /// This function will only work correctly when used inside the `Embed::run` + /// function /// /// # Returns /// diff --git a/src/types/class_object.rs b/src/types/class_object.rs index 753933d393..9b63b500ca 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -5,7 +5,8 @@ use std::{ fmt::Debug, mem, ops::{Deref, DerefMut}, - ptr::{self, NonNull}, os::raw::c_char, + os::raw::c_char, + ptr::{self, NonNull}, }; use crate::{ diff --git a/src/zend/ini_entry_def.rs b/src/zend/ini_entry_def.rs index 8817300316..d9c8f64d8c 100644 --- a/src/zend/ini_entry_def.rs +++ b/src/zend/ini_entry_def.rs @@ -7,8 +7,9 @@ use crate::{ffi::zend_ini_entry_def, ffi::zend_register_ini_entries, flags::IniE /// A Zend ini entry definition. /// -/// To register ini definitions for extensions, the IniEntryDef builder should be used. Ini -/// entries should be registered in your module's startup_function via `IniEntryDef::register(Vec)`. +/// To register ini definitions for extensions, the IniEntryDef builder should +/// be used. Ini entries should be registered in your module's startup_function +/// via `IniEntryDef::register(Vec)`. pub type IniEntryDef = zend_ini_entry_def; impl IniEntryDef { diff --git a/src/zend/try_catch.rs b/src/zend/try_catch.rs index f74b427a59..37cd89655c 100644 --- a/src/zend/try_catch.rs +++ b/src/zend/try_catch.rs @@ -18,7 +18,8 @@ pub(crate) unsafe extern "C" fn panic_wrapper R + RefUnwindSafe /// PHP propose a try catch mechanism in C using setjmp and longjmp (bailout) /// It store the arg of setjmp into the bailout field of the global executor -/// If a bailout is triggered, the executor will jump to the setjmp and restore the previous setjmp +/// If a bailout is triggered, the executor will jump to the setjmp and restore +/// the previous setjmp /// /// try_catch allow to use this mechanism /// @@ -60,10 +61,11 @@ pub fn try_catch R + RefUnwindSafe>(func: F) -> Result ! { ext_php_rs_zend_bailout(); } From d8a5a09b1ad8ebd3ea3e9b0430e4d52bb7594ef8 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 17 Nov 2023 18:59:13 +0100 Subject: [PATCH 067/100] Cleanup docs --- guide/src/SUMMARY.md | 3 +- guide/src/macros/async_impl.md | 130 +++++++++++++++++++++++++++++++ guide/src/macros/impl.md | 135 +-------------------------------- 3 files changed, 134 insertions(+), 134 deletions(-) create mode 100644 guide/src/macros/async_impl.md diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 4108d1bda7..db614111d3 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -24,13 +24,14 @@ - [Class Object](./types/class_object.md) - [Closure](./types/closure.md) - [Functions & methods](./types/functions.md) - - [Async futures](./macros/impl.md#async) + - [Async futures](./macros/async_impl.md) - [Macros](./macros/index.md) - [Module](./macros/module.md) - [Module Startup Function](./macros/module_startup.md) - [Function](./macros/function.md) - [Classes](./macros/classes.md) - [`impl`s](./macros/impl.md) + - [async `impl`s](./macros/async_impl.md) - [Constants](./macros/constant.md) - [`ZvalConvert`](./macros/zval_convert.md) - [Exceptions](./exceptions.md) diff --git a/guide/src/macros/async_impl.md b/guide/src/macros/async_impl.md new file mode 100644 index 0000000000..0912001013 --- /dev/null +++ b/guide/src/macros/async_impl.md @@ -0,0 +1,130 @@ +# `#[php_async_impl]` + +Using `#[php_async_impl]` instead of `#[php_impl]` allows us to expose any async Rust library to PHP, using [PHP fibers](https://www.php.net/manual/en/language.fibers.php), [php-tokio](https://github.com/danog/php-tokio) and the [PHP Revolt event loop](https://revolt.run) under the hood to handle async interoperability. + +This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). + +Traits annotated with `#[php_impl]` can freely expose any async function, using `await` and any async Rust library. + +Make sure to also expose the `php_tokio::EventLoop::init` and `php_tokio::EventLoop::wakeup` functions to PHP in order to initialize the event loop, as specified in the full example [here »](#async-example). + +Also, make sure to invoke `EventLoop::shutdown` in the request shutdown handler to clean up the tokio event loop before finishing the request. + +## Async example + +In this example, we're exposing an async Rust HTTP client library called [reqwest](https://docs.rs/reqwest/latest/reqwest/) to PHP, using [PHP fibers](https://www.php.net/manual/en/language.fibers.php), [php-tokio](https://github.com/danog/php-tokio) and the [PHP Revolt event loop](https://revolt.run) under the hood to handle async interoperability. + +This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). + +Make sure to require [php-tokio](https://github.com/danog/php-tokio) as a dependency before proceeding. + +```rust,ignore +use ext_php_rs::prelude::*; +use php_tokio::{php_async_impl, EventLoop}; + +#[php_class] +struct Client {} + +#[php_async_impl] +impl Client { + pub fn init() -> PhpResult { + EventLoop::init() + } + pub fn wakeup() -> PhpResult<()> { + EventLoop::wakeup() + } + pub async fn get(url: &str) -> anyhow::Result { + Ok(reqwest::get(url).await?.text().await?) + } +} + +pub extern "C" fn request_shutdown(_type: i32, _module_number: i32) -> i32 { + EventLoop::shutdown(); + 0 +} + +#[php_module] +pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { + module.request_shutdown_function(request_shutdown) +} +``` + +Here's the async PHP code we use to interact with the Rust class we just exposed. + +The `Client::init` method needs to be called only once in order to initialize the Revolt event loop and link it to the Tokio event loop, as shown by the following code. + +See [here »](https://amphp.org) for more info on async PHP using [amphp](https://amphp.org) + [revolt](https://revolt.run). + +```php + \Client::wakeup()); + } + + public static function reference(): void + { + EventLoop::reference(self::$id); + } + public static function unreference(): void + { + EventLoop::unreference(self::$id); + } + + public static function __callStatic(string $name, array $args): mixed + { + return \Client::$name(...$args); + } +} + + +Client::init(); + +function test(int $delay): void +{ + $url = "https://httpbin.org/delay/$delay"; + $t = time(); + echo "Making async reqwest to $url that will return after $delay seconds...".PHP_EOL; + Client::get($url); + $t = time() - $t; + echo "Got response from $url after ~".$t." seconds!".PHP_EOL; +}; + +$futures = []; +$futures []= async(test(...), 5); +$futures []= async(test(...), 5); +$futures []= async(test(...), 5); + +await($futures); +``` + +Result: + +``` +Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... +Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... +Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... +Got response from https://httpbin.org/delay/5 after ~5 seconds! +Got response from https://httpbin.org/delay/5 after ~5 seconds! +Got response from https://httpbin.org/delay/5 after ~5 seconds! +``` + +[`php_function`]: ./function.md diff --git a/guide/src/macros/impl.md b/guide/src/macros/impl.md index 1b872f1944..db12ecaf3d 100644 --- a/guide/src/macros/impl.md +++ b/guide/src/macros/impl.md @@ -8,7 +8,7 @@ implementations cannot be exported to PHP. If you do not want a function exported to PHP, you should place it in a separate `impl` block. -If you want to use async Rust, use `#[php_async_impl]`, instead: see [here »](#async) for more info. +If you want to use async Rust, use `#[php_async_impl]`, instead: see [here »](./async_impl.md) for more info. ## Methods @@ -65,20 +65,6 @@ the attribute, the function is not exported to PHP like a regular method. Constructors cannot use the visibility or rename attributes listed above. -### Async - -Using `#[php_async_impl]` instead of `#[php_impl]` allows us to expose any async Rust library to PHP, using [PHP fibers](https://www.php.net/manual/en/language.fibers.php), [php-tokio](https://github.com/danog/php-tokio) and the [PHP Revolt event loop](https://revolt.run) under the hood to handle async interoperability. - -This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). - -Traits annotated with `#[php_impl]` can freely expose any async function, using `await` and any async Rust library. - -Make sure to also expose the `php_tokio::EventLoop::init` and `php_tokio::EventLoop::wakeup` functions to PHP in order to initialize the event loop, as specified in the full example [here »](#async-example). - -Also, make sure to invoke `EventLoop::shutdown` in the request shutdown handler to clean up the tokio event loop before finishing the request. - -See [here »](#async-example) for the full example. - ## Constants Constants are defined as regular Rust `impl` constants. Any type that implements @@ -178,121 +164,4 @@ var_dump(Human::get_max_age()); // int(100) var_dump(Human::MAX_AGE); // int(100) ``` -### Async example - -In this example, we're exposing an async Rust HTTP client library called [reqwest](https://docs.rs/reqwest/latest/reqwest/) to PHP, using [PHP fibers](https://www.php.net/manual/en/language.fibers.php), [php-tokio](https://github.com/danog/php-tokio) and the [PHP Revolt event loop](https://revolt.run) under the hood to handle async interoperability. - -This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). - -Make sure to require [php-tokio](https://github.com/danog/php-tokio) as a dependency before proceeding. - -```rust,ignore -use ext_php_rs::prelude::*; -use php_tokio::{php_async_impl, EventLoop}; - -#[php_class] -struct Client {} - -#[php_async_impl] -impl Client { - pub fn init() -> PhpResult { - EventLoop::init() - } - pub fn wakeup() -> PhpResult<()> { - EventLoop::wakeup() - } - pub async fn get(url: &str) -> anyhow::Result { - Ok(reqwest::get(url).await?.text().await?) - } -} - -pub extern "C" fn request_shutdown(_type: i32, _module_number: i32) -> i32 { - EventLoop::shutdown(); - 0 -} - -#[php_module] -pub fn get_module(module: ModuleBuilder) -> ModuleBuilder { - module.request_shutdown_function(request_shutdown) -} -``` - -Here's the async PHP code we use to interact with the Rust class we just exposed. - -The `Client::init` method needs to be called only once in order to initialize the Revolt event loop and link it to the Tokio event loop, as shown by the following code. - -See [here »](https://amphp.org) for more info on async PHP using [amphp](https://amphp.org) + [revolt](https://revolt.run). - -```php - \Client::wakeup()); - } - - public static function reference(): void - { - EventLoop::reference(self::$id); - } - public static function unreference(): void - { - EventLoop::unreference(self::$id); - } - - public static function __callStatic(string $name, array $args): mixed - { - return \Client::$name(...$args); - } -} - - -Client::init(); - -function test(int $delay): void -{ - $url = "https://httpbin.org/delay/$delay"; - $t = time(); - echo "Making async reqwest to $url that will return after $delay seconds...".PHP_EOL; - Client::get($url); - $t = time() - $t; - echo "Got response from $url after ~".$t." seconds!".PHP_EOL; -}; - -$futures = []; -$futures []= async(test(...), 5); -$futures []= async(test(...), 5); -$futures []= async(test(...), 5); - -await($futures); -``` - -Result: - -``` -Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... -Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... -Making async reqwest to https://httpbin.org/delay/5 that will return after 5 seconds... -Got response from https://httpbin.org/delay/5 after ~5 seconds! -Got response from https://httpbin.org/delay/5 after ~5 seconds! -Got response from https://httpbin.org/delay/5 after ~5 seconds! -``` - -[`php_function`]: ./function.md +[`php_async_impl`]: ./async_impl.md From b81128acafb118036e77afd9ad2a0266061abc5e Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 17 Nov 2023 19:04:02 +0100 Subject: [PATCH 068/100] Fix typo --- guide/src/macros/async_impl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/macros/async_impl.md b/guide/src/macros/async_impl.md index 0912001013..979c58bdee 100644 --- a/guide/src/macros/async_impl.md +++ b/guide/src/macros/async_impl.md @@ -4,7 +4,7 @@ Using `#[php_async_impl]` instead of `#[php_impl]` allows us to expose any async This allows full compatibility with [amphp](https://amphp.org), [PSL](https://github.com/azjezz/psl), [reactphp](https://reactphp.org) and any other async PHP library based on [Revolt](https://revolt.run). -Traits annotated with `#[php_impl]` can freely expose any async function, using `await` and any async Rust library. +Traits annotated with `#[php_async_impl]` can freely expose any async function, using `await` and any async Rust library. Make sure to also expose the `php_tokio::EventLoop::init` and `php_tokio::EventLoop::wakeup` functions to PHP in order to initialize the event loop, as specified in the full example [here »](#async-example). From f621e60be66425d3bf3ac1a5ebccf675cdda263c Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Tue, 21 Nov 2023 20:13:57 +0100 Subject: [PATCH 069/100] Add PHP 8.3, SAPI globals support --- .github/workflows/build.yml | 2 +- allowed_bindings.rs | 4 + build.rs | 2 +- docsrs_bindings.rs | 850 ++++++++++++++++++++++++++++++++---- src/ffi.rs | 1 + src/wrapper.c | 12 + src/wrapper.h | 4 +- src/zend/globals.rs | 45 +- src/zend/mod.rs | 1 + 9 files changed, 821 insertions(+), 100 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe68b07ed7..557d80cd54 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - php: ["8.0", "8.1", "8.2"] + php: ["8.0", "8.1", "8.2", "8.3"] rust: [stable, nightly] clang: ["14"] phpts: [ts, nts] diff --git a/allowed_bindings.rs b/allowed_bindings.rs index ca2195bde0..53e5a5e2e9 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -26,6 +26,7 @@ bind! { _efree, _emalloc, _zend_executor_globals, + _sapi_globals_struct, _zend_expected_type, _zend_expected_type_Z_EXPECTED_ARRAY, _zend_expected_type_Z_EXPECTED_BOOL, @@ -241,6 +242,7 @@ bind! { zend_class_serialize_deny, zend_class_unserialize_deny, zend_executor_globals, + sapi_globals_struct, zend_objects_store_del, zend_hash_move_forward_ex, zend_hash_get_current_key_type_ex, @@ -251,10 +253,12 @@ bind! { gc_possible_root, ZEND_ACC_NOT_SERIALIZABLE, executor_globals, + sapi_globals, php_printf, __zend_malloc, tsrm_get_ls_cache, executor_globals_offset, + sapi_globals_offset, zend_atomic_bool_store, zend_interrupt_function, zend_eval_string, diff --git a/build.rs b/build.rs index 78c9cf3226..601a0c359a 100644 --- a/build.rs +++ b/build.rs @@ -16,7 +16,7 @@ use bindgen::RustTarget; use impl_::Provider; const MIN_PHP_API_VER: u32 = 20200930; -const MAX_PHP_API_VER: u32 = 20220829; +const MAX_PHP_API_VER: u32 = 20230831; pub trait PHPProvider<'a>: Sized { /// Create a new PHP provider. diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index f7e54f1e25..dd3978a472 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -1,5 +1,85 @@ /* automatically generated by rust-bindgen 0.68.1 */ +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct __BindgenBitfieldUnit { + storage: Storage, +} +impl __BindgenBitfieldUnit { + #[inline] + pub const fn new(storage: Storage) -> Self { + Self { storage } + } +} +impl __BindgenBitfieldUnit +where + Storage: AsRef<[u8]> + AsMut<[u8]>, +{ + #[inline] + pub fn get_bit(&self, index: usize) -> bool { + debug_assert!(index / 8 < self.storage.as_ref().len()); + let byte_index = index / 8; + let byte = self.storage.as_ref()[byte_index]; + let bit_index = if cfg!(target_endian = "big") { + 7 - (index % 8) + } else { + index % 8 + }; + let mask = 1 << bit_index; + byte & mask == mask + } + #[inline] + pub fn set_bit(&mut self, index: usize, val: bool) { + debug_assert!(index / 8 < self.storage.as_ref().len()); + let byte_index = index / 8; + let byte = &mut self.storage.as_mut()[byte_index]; + let bit_index = if cfg!(target_endian = "big") { + 7 - (index % 8) + } else { + index % 8 + }; + let mask = 1 << bit_index; + if val { + *byte |= mask; + } else { + *byte &= !mask; + } + } + #[inline] + pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + let mut val = 0; + for i in 0..(bit_width as usize) { + if self.get_bit(i + bit_offset) { + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + val |= 1 << index; + } + } + val + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + for i in 0..(bit_width as usize) { + let mask = 1 << i; + let val_bit_is_set = val & mask == mask; + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + self.set_bit(index + bit_offset, val_bit_is_set); + } + } +} pub const ZEND_DEBUG: u32 = 1; pub const _ZEND_TYPE_NAME_BIT: u32 = 16777216; pub const _ZEND_TYPE_NULLABLE_BIT: u32 = 2; @@ -97,7 +177,7 @@ pub const ZEND_EVAL_CODE: u32 = 4; pub const ZEND_ISEMPTY: u32 = 1; pub const _ZEND_SEND_MODE_SHIFT: u32 = 25; pub const _ZEND_IS_VARIADIC_BIT: u32 = 134217728; -pub const ZEND_MODULE_API_NO: u32 = 20220829; +pub const ZEND_MODULE_API_NO: u32 = 20230831; pub const USING_ZTS: u32 = 0; pub const MAY_BE_BOOL: u32 = 12; pub const MAY_BE_ANY: u32 = 1022; @@ -109,68 +189,82 @@ 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; -pub type __int64_t = ::std::os::raw::c_longlong; -pub type __darwin_off_t = __int64_t; -pub type fpos_t = __darwin_off_t; +pub type __dev_t = ::std::os::raw::c_ulong; +pub type __uid_t = ::std::os::raw::c_uint; +pub type __gid_t = ::std::os::raw::c_uint; +pub type __ino_t = ::std::os::raw::c_ulong; +pub type __mode_t = ::std::os::raw::c_uint; +pub type __nlink_t = ::std::os::raw::c_ulong; +pub type __off_t = ::std::os::raw::c_long; +pub type __off64_t = ::std::os::raw::c_long; +pub type __time_t = ::std::os::raw::c_long; +pub type __blksize_t = ::std::os::raw::c_long; +pub type __blkcnt_t = ::std::os::raw::c_long; +pub type __syscall_slong_t = ::std::os::raw::c_long; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __sigset_t { + pub __val: [::std::os::raw::c_ulong; 16usize], +} #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct __sbuf { - pub _base: *mut ::std::os::raw::c_uchar, - pub _size: ::std::os::raw::c_int, +pub struct timespec { + pub tv_sec: __time_t, + pub tv_nsec: __syscall_slong_t, } +pub type FILE = _IO_FILE; #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct __sFILEX { +pub struct _IO_marker { _unused: [u8; 0], } #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct __sFILE { - pub _p: *mut ::std::os::raw::c_uchar, - pub _r: ::std::os::raw::c_int, - pub _w: ::std::os::raw::c_int, - pub _flags: ::std::os::raw::c_short, - pub _file: ::std::os::raw::c_short, - pub _bf: __sbuf, - pub _lbfsize: ::std::os::raw::c_int, - pub _cookie: *mut ::std::os::raw::c_void, - pub _close: ::std::option::Option< - unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int, - >, - pub _read: ::std::option::Option< - unsafe extern "C" fn( - arg1: *mut ::std::os::raw::c_void, - arg2: *mut ::std::os::raw::c_char, - arg3: ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int, - >, - pub _seek: ::std::option::Option< - unsafe extern "C" fn( - arg1: *mut ::std::os::raw::c_void, - arg2: fpos_t, - arg3: ::std::os::raw::c_int, - ) -> fpos_t, - >, - pub _write: ::std::option::Option< - unsafe extern "C" fn( - arg1: *mut ::std::os::raw::c_void, - arg2: *const ::std::os::raw::c_char, - arg3: ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int, - >, - pub _ub: __sbuf, - pub _extra: *mut __sFILEX, - pub _ur: ::std::os::raw::c_int, - pub _ubuf: [::std::os::raw::c_uchar; 3usize], - pub _nbuf: [::std::os::raw::c_uchar; 1usize], - pub _lb: __sbuf, - pub _blksize: ::std::os::raw::c_int, - pub _offset: fpos_t, -} -pub type FILE = __sFILE; +pub struct _IO_codecvt { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _IO_wide_data { + _unused: [u8; 0], +} +pub type _IO_lock_t = ::std::os::raw::c_void; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _IO_FILE { + pub _flags: ::std::os::raw::c_int, + pub _IO_read_ptr: *mut ::std::os::raw::c_char, + pub _IO_read_end: *mut ::std::os::raw::c_char, + pub _IO_read_base: *mut ::std::os::raw::c_char, + pub _IO_write_base: *mut ::std::os::raw::c_char, + pub _IO_write_ptr: *mut ::std::os::raw::c_char, + pub _IO_write_end: *mut ::std::os::raw::c_char, + pub _IO_buf_base: *mut ::std::os::raw::c_char, + pub _IO_buf_end: *mut ::std::os::raw::c_char, + pub _IO_save_base: *mut ::std::os::raw::c_char, + pub _IO_backup_base: *mut ::std::os::raw::c_char, + pub _IO_save_end: *mut ::std::os::raw::c_char, + pub _markers: *mut _IO_marker, + pub _chain: *mut _IO_FILE, + pub _fileno: ::std::os::raw::c_int, + pub _flags2: ::std::os::raw::c_int, + pub _old_offset: __off_t, + pub _cur_column: ::std::os::raw::c_ushort, + pub _vtable_offset: ::std::os::raw::c_schar, + pub _shortbuf: [::std::os::raw::c_char; 1usize], + pub _lock: *mut _IO_lock_t, + pub _offset: __off64_t, + pub _codecvt: *mut _IO_codecvt, + pub _wide_data: *mut _IO_wide_data, + pub _freeres_list: *mut _IO_FILE, + pub _freeres_buf: *mut ::std::os::raw::c_void, + pub __pad5: usize, + pub _mode: ::std::os::raw::c_int, + pub _unused2: [::std::os::raw::c_char; 20usize], +} pub type zend_long = i64; pub type zend_ulong = u64; -pub type zend_uchar = ::std::os::raw::c_uchar; +pub type zend_off_t = i64; pub const ZEND_RESULT_CODE_SUCCESS: ZEND_RESULT_CODE = 0; pub const ZEND_RESULT_CODE_FAILURE: ZEND_RESULT_CODE = -1; pub type ZEND_RESULT_CODE = ::std::os::raw::c_int; @@ -234,8 +328,8 @@ pub union _zval_struct__bindgen_ty_1 { #[repr(C)] #[derive(Copy, Clone)] pub struct _zval_struct__bindgen_ty_1__bindgen_ty_1 { - pub type_: zend_uchar, - pub type_flags: zend_uchar, + pub type_: u8, + pub type_flags: u8, pub u: _zval_struct__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1, } #[repr(C)] @@ -253,7 +347,7 @@ pub union _zval_struct__bindgen_ty_2 { pub num_args: u32, pub fe_pos: u32, pub fe_iter_idx: u32, - pub property_guard: u32, + pub guard: u32, pub constant_flags: u32, pub extra: u32, } @@ -311,10 +405,10 @@ pub union _zend_array__bindgen_ty_1 { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _zend_array__bindgen_ty_1__bindgen_ty_1 { - pub flags: zend_uchar, - pub _unused: zend_uchar, - pub nIteratorsCount: zend_uchar, - pub _unused2: zend_uchar, + pub flags: u8, + pub _unused: u8, + pub nIteratorsCount: u8, + pub _unused2: u8, } #[repr(C)] #[derive(Copy, Clone)] @@ -329,6 +423,7 @@ pub type HashPosition = u32; pub struct _HashTableIterator { pub ht: *mut HashTable, pub pos: HashPosition, + pub next_copy: u32, } pub type HashTableIterator = _HashTableIterator; #[repr(C)] @@ -386,6 +481,28 @@ extern "C" { extern "C" { pub fn __zend_malloc(len: usize) -> *mut ::std::os::raw::c_void; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_llist_element { + pub next: *mut _zend_llist_element, + pub prev: *mut _zend_llist_element, + pub data: [::std::os::raw::c_char; 1usize], +} +pub type zend_llist_element = _zend_llist_element; +pub type llist_dtor_func_t = + ::std::option::Option; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_llist { + pub head: *mut zend_llist_element, + pub tail: *mut zend_llist_element, + pub count: usize, + pub size: usize, + pub dtor: llist_dtor_func_t, + pub persistent: ::std::os::raw::c_uchar, + pub traverse_ptr: *mut zend_llist_element, +} +pub type zend_llist = _zend_llist; pub type zend_string_init_interned_func_t = ::std::option::Option< unsafe extern "C" fn( str_: *const ::std::os::raw::c_char, @@ -541,6 +658,25 @@ pub struct _zend_class_arrayaccess_funcs { pub zf_offsetunset: *mut zend_function, } pub type zend_class_arrayaccess_funcs = _zend_class_arrayaccess_funcs; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct stat { + pub st_dev: __dev_t, + pub st_ino: __ino_t, + pub st_nlink: __nlink_t, + pub st_mode: __mode_t, + pub st_uid: __uid_t, + pub st_gid: __gid_t, + pub __pad0: ::std::os::raw::c_int, + pub st_rdev: __dev_t, + pub st_size: __off_t, + pub st_blksize: __blksize_t, + pub st_blocks: __blkcnt_t, + pub st_atim: timespec, + pub st_mtim: timespec, + pub st_ctim: timespec, + pub __glibc_reserved: [__syscall_slong_t; 3usize], +} pub type zend_stream_fsizer_t = ::std::option::Option usize>; pub type zend_stream_reader_t = ::std::option::Option< @@ -568,7 +704,7 @@ pub struct _zend_file_handle { pub handle: _zend_file_handle__bindgen_ty_1, pub filename: *mut zend_string, pub opened_path: *mut zend_string, - pub type_: zend_uchar, + pub type_: u8, pub primary_script: bool, pub in_list: bool, pub buf: *mut ::std::os::raw::c_char, @@ -587,6 +723,7 @@ extern "C" { filename: *const ::std::os::raw::c_char, ); } +pub type zend_stat_t = stat; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _zend_serialize_data { @@ -700,6 +837,7 @@ pub struct _zend_class_entry { pub __debugInfo: *mut zend_function, pub __serialize: *mut zend_function, pub __unserialize: *mut zend_function, + pub default_object_handlers: *const zend_object_handlers, pub iterator_funcs_ptr: *mut zend_class_iterator_funcs, pub arrayaccess_funcs_ptr: *mut zend_class_arrayaccess_funcs, pub __bindgen_anon_2: _zend_class_entry__bindgen_ty_2, @@ -929,7 +1067,7 @@ pub type zend_object_get_gc_t = ::std::option::Option< >; pub type zend_object_do_operation_t = ::std::option::Option< unsafe extern "C" fn( - opcode: zend_uchar, + opcode: u8, result: *mut zval, op1: *mut zval, op2: *mut zval, @@ -996,10 +1134,10 @@ extern "C" { ) -> ::std::os::raw::c_int; } extern "C" { - pub fn zend_is_identical(op1: *mut zval, op2: *mut zval) -> bool; + pub fn zend_is_identical(op1: *const zval, op2: *const zval) -> bool; } extern "C" { - pub fn zend_is_true(op: *mut zval) -> ::std::os::raw::c_int; + pub fn zend_is_true(op: *const zval) -> ::std::os::raw::c_int; } pub type zend_op_array = _zend_op_array; pub type zend_op = _zend_op; @@ -1022,10 +1160,10 @@ pub struct _zend_op { pub result: znode_op, pub extended_value: u32, pub lineno: u32, - pub opcode: zend_uchar, - pub op1_type: zend_uchar, - pub op2_type: zend_uchar, - pub result_type: zend_uchar, + pub opcode: u8, + pub op1_type: u8, + pub op2_type: u8, + pub result_type: u8, } #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -1074,8 +1212,8 @@ pub type zend_arg_info = _zend_arg_info; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _zend_op_array { - pub type_: zend_uchar, - pub arg_flags: [zend_uchar; 3usize], + pub type_: u8, + pub arg_flags: [u8; 3usize], pub fn_flags: u32, pub function_name: *mut zend_string, pub scope: *mut zend_class_entry, @@ -1084,8 +1222,8 @@ pub struct _zend_op_array { pub required_num_args: u32, pub arg_info: *mut zend_arg_info, pub attributes: *mut HashTable, - pub T: u32, pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, + pub T: u32, pub cache_size: ::std::os::raw::c_int, pub last_var: ::std::os::raw::c_int, pub last: u32, @@ -1114,8 +1252,8 @@ pub type zif_handler = ::std::option::Option< #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _zend_internal_function { - pub type_: zend_uchar, - pub arg_flags: [zend_uchar; 3usize], + pub type_: u8, + pub arg_flags: [u8; 3usize], pub fn_flags: u32, pub function_name: *mut zend_string, pub scope: *mut zend_class_entry, @@ -1124,8 +1262,8 @@ pub struct _zend_internal_function { pub required_num_args: u32, pub arg_info: *mut zend_internal_arg_info, pub attributes: *mut HashTable, - pub T: u32, pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, + pub T: u32, pub handler: zif_handler, pub module: *mut _zend_module_entry, pub reserved: [*mut ::std::os::raw::c_void; 6usize], @@ -1134,7 +1272,7 @@ pub type zend_internal_function = _zend_internal_function; #[repr(C)] #[derive(Copy, Clone)] pub union _zend_function { - pub type_: zend_uchar, + pub type_: u8, pub quick_arg_flags: u32, pub common: _zend_function__bindgen_ty_1, pub op_array: zend_op_array, @@ -1143,8 +1281,8 @@ pub union _zend_function { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _zend_function__bindgen_ty_1 { - pub type_: zend_uchar, - pub arg_flags: [zend_uchar; 3usize], + pub type_: u8, + pub arg_flags: [u8; 3usize], pub fn_flags: u32, pub function_name: *mut zend_string, pub scope: *mut zend_class_entry, @@ -1153,8 +1291,8 @@ pub struct _zend_function__bindgen_ty_1 { pub required_num_args: u32, pub arg_info: *mut zend_arg_info, pub attributes: *mut HashTable, - pub T: u32, pub run_time_cache__ptr: *mut *mut ::std::os::raw::c_void, + pub T: u32, } #[repr(C)] pub struct _zend_execute_data { @@ -1168,7 +1306,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; @@ -1215,6 +1361,13 @@ pub type zend_objects_store = _zend_objects_store; extern "C" { pub fn zend_objects_store_del(object: *mut zend_object); } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_call_stack { + pub base: *mut ::std::os::raw::c_void, + pub max_size: usize, +} +pub type zend_call_stack = _zend_call_stack; pub type zend_vm_stack = *mut _zend_vm_stack; pub type zend_ini_entry = _zend_ini_entry; #[repr(C)] @@ -1238,7 +1391,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, @@ -1251,29 +1404,32 @@ pub struct _zend_executor_globals { pub current_execute_data: *mut _zend_execute_data, pub fake_scope: *mut zend_class_entry, pub jit_trace_num: u32, - pub precision: zend_long, pub ticks_count: ::std::os::raw::c_int, + pub precision: zend_long, pub persistent_constants_count: u32, pub persistent_functions_count: u32, pub persistent_classes_count: u32, - pub in_autoload: *mut HashTable, - pub full_tables_cleanup: bool, pub no_extensions: bool, + pub full_tables_cleanup: bool, pub vm_interrupt: zend_atomic_bool, pub timed_out: zend_atomic_bool, + pub in_autoload: *mut HashTable, pub hard_timeout: zend_long, + pub stack_base: *mut ::std::os::raw::c_void, + pub stack_limit: *mut ::std::os::raw::c_void, pub regular_list: HashTable, pub persistent_list: HashTable, pub user_error_handler_error_reporting: ::std::os::raw::c_int, + pub exception_ignore_args: bool, pub user_error_handler: zval, pub user_exception_handler: zval, pub user_error_handlers_error_reporting: zend_stack, pub user_error_handlers: zend_stack, pub user_exception_handlers: zend_stack, - pub error_handling: zend_error_handling_t, pub exception_class: *mut zend_class_entry, - pub timeout_seconds: zend_long, + pub error_handling: zend_error_handling_t, pub capture_warnings_during_sccp: ::std::os::raw::c_int, + pub timeout_seconds: zend_long, pub ini_directives: *mut HashTable, pub modified_ini_directives: *mut HashTable, pub error_reporting_ini_entry: *mut zend_ini_entry, @@ -1284,7 +1440,7 @@ pub struct _zend_executor_globals { pub exception_op: [zend_op; 3usize], pub current_module: *mut _zend_module_entry, pub active: bool, - pub flags: zend_uchar, + pub flags: u8, pub assertions: zend_long, pub ht_iterators_count: u32, pub ht_iterators_used: u32, @@ -1294,18 +1450,20 @@ pub struct _zend_executor_globals { pub trampoline: zend_function, pub call_trampoline_op: zend_op, pub weakrefs: HashTable, - pub exception_ignore_args: bool, pub exception_string_param_max_len: zend_long, pub get_gc_buffer: zend_get_gc_buffer, pub main_fiber_context: *mut zend_fiber_context, pub current_fiber_context: *mut zend_fiber_context, pub active_fiber: *mut zend_fiber, - pub fiber_stack_size: zend_long, + pub fiber_stack_size: usize, pub record_errors: bool, pub num_errors: u32, pub errors: *mut *mut zend_error_info, pub filename_override: *mut zend_string, pub lineno_override: zend_long, + pub call_stack: zend_call_stack, + pub max_allowed_stack_size: zend_long, + pub reserved_stack_size: zend_ulong, pub reserved: [*mut ::std::os::raw::c_void; 6usize], } pub type zend_module_entry = _zend_module_entry; @@ -1404,6 +1562,19 @@ pub struct _zend_function_entry { pub flags: u32, } pub type zend_function_entry = _zend_function_entry; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _zend_fcall_info_cache { + pub function_handler: *mut zend_function, + pub calling_scope: *mut zend_class_entry, + pub called_scope: *mut zend_class_entry, + pub object: *mut zend_object, + pub closure: *mut zend_object, +} +pub type zend_fcall_info_cache = _zend_fcall_info_cache; +extern "C" { + pub fn zend_register_module_ex(module: *mut zend_module_entry) -> *mut zend_module_entry; +} extern "C" { pub fn zend_register_internal_class_ex( class_entry: *mut zend_class_entry, @@ -1482,15 +1653,17 @@ pub const _zend_expected_type_Z_EXPECTED_DOUBLE: _zend_expected_type = 20; pub const _zend_expected_type_Z_EXPECTED_DOUBLE_OR_NULL: _zend_expected_type = 21; pub const _zend_expected_type_Z_EXPECTED_NUMBER: _zend_expected_type = 22; pub const _zend_expected_type_Z_EXPECTED_NUMBER_OR_NULL: _zend_expected_type = 23; -pub const _zend_expected_type_Z_EXPECTED_ARRAY_OR_STRING: _zend_expected_type = 24; -pub const _zend_expected_type_Z_EXPECTED_ARRAY_OR_STRING_OR_NULL: _zend_expected_type = 25; -pub const _zend_expected_type_Z_EXPECTED_STRING_OR_LONG: _zend_expected_type = 26; -pub const _zend_expected_type_Z_EXPECTED_STRING_OR_LONG_OR_NULL: _zend_expected_type = 27; -pub const _zend_expected_type_Z_EXPECTED_OBJECT_OR_CLASS_NAME: _zend_expected_type = 28; -pub const _zend_expected_type_Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL: _zend_expected_type = 29; -pub const _zend_expected_type_Z_EXPECTED_OBJECT_OR_STRING: _zend_expected_type = 30; -pub const _zend_expected_type_Z_EXPECTED_OBJECT_OR_STRING_OR_NULL: _zend_expected_type = 31; -pub const _zend_expected_type_Z_EXPECTED_LAST: _zend_expected_type = 32; +pub const _zend_expected_type_Z_EXPECTED_NUMBER_OR_STRING: _zend_expected_type = 24; +pub const _zend_expected_type_Z_EXPECTED_NUMBER_OR_STRING_OR_NULL: _zend_expected_type = 25; +pub const _zend_expected_type_Z_EXPECTED_ARRAY_OR_STRING: _zend_expected_type = 26; +pub const _zend_expected_type_Z_EXPECTED_ARRAY_OR_STRING_OR_NULL: _zend_expected_type = 27; +pub const _zend_expected_type_Z_EXPECTED_STRING_OR_LONG: _zend_expected_type = 28; +pub const _zend_expected_type_Z_EXPECTED_STRING_OR_LONG_OR_NULL: _zend_expected_type = 29; +pub const _zend_expected_type_Z_EXPECTED_OBJECT_OR_CLASS_NAME: _zend_expected_type = 30; +pub const _zend_expected_type_Z_EXPECTED_OBJECT_OR_CLASS_NAME_OR_NULL: _zend_expected_type = 31; +pub const _zend_expected_type_Z_EXPECTED_OBJECT_OR_STRING: _zend_expected_type = 32; +pub const _zend_expected_type_Z_EXPECTED_OBJECT_OR_STRING_OR_NULL: _zend_expected_type = 33; +pub const _zend_expected_type_Z_EXPECTED_LAST: _zend_expected_type = 34; pub type _zend_expected_type = ::std::os::raw::c_uint; extern "C" { pub fn zend_wrong_parameters_count_error(min_num_args: u32, max_num_args: u32); @@ -1506,6 +1679,418 @@ extern "C" { ... ); } +pub type php_stream = _php_stream; +pub type php_stream_wrapper = _php_stream_wrapper; +pub type php_stream_context = _php_stream_context; +pub type php_stream_filter = _php_stream_filter; +pub type php_stream_notification_func = ::std::option::Option< + unsafe extern "C" fn( + context: *mut php_stream_context, + notifycode: ::std::os::raw::c_int, + severity: ::std::os::raw::c_int, + xmsg: *mut ::std::os::raw::c_char, + xcode: ::std::os::raw::c_int, + bytes_sofar: usize, + bytes_max: usize, + ptr: *mut ::std::os::raw::c_void, + ), +>; +pub type php_stream_notifier = _php_stream_notifier; +#[repr(C)] +pub struct _php_stream_notifier { + pub func: php_stream_notification_func, + pub dtor: ::std::option::Option, + pub ptr: zval, + pub mask: ::std::os::raw::c_int, + pub progress: usize, + pub progress_max: usize, +} +#[repr(C)] +pub struct _php_stream_context { + pub notifier: *mut php_stream_notifier, + pub options: zval, + pub res: *mut zend_resource, +} +pub type php_stream_bucket = _php_stream_bucket; +pub type php_stream_bucket_brigade = _php_stream_bucket_brigade; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_bucket { + pub next: *mut php_stream_bucket, + pub prev: *mut php_stream_bucket, + pub brigade: *mut php_stream_bucket_brigade, + pub buf: *mut ::std::os::raw::c_char, + pub buflen: usize, + pub own_buf: u8, + pub is_persistent: u8, + pub refcount: ::std::os::raw::c_int, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_bucket_brigade { + pub head: *mut php_stream_bucket, + pub tail: *mut php_stream_bucket, +} +pub const php_stream_filter_status_t_PSFS_ERR_FATAL: php_stream_filter_status_t = 0; +pub const php_stream_filter_status_t_PSFS_FEED_ME: php_stream_filter_status_t = 1; +pub const php_stream_filter_status_t_PSFS_PASS_ON: php_stream_filter_status_t = 2; +pub type php_stream_filter_status_t = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_filter_ops { + pub filter: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + thisfilter: *mut php_stream_filter, + buckets_in: *mut php_stream_bucket_brigade, + buckets_out: *mut php_stream_bucket_brigade, + bytes_consumed: *mut usize, + flags: ::std::os::raw::c_int, + ) -> php_stream_filter_status_t, + >, + pub dtor: ::std::option::Option, + pub label: *const ::std::os::raw::c_char, +} +pub type php_stream_filter_ops = _php_stream_filter_ops; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_filter_chain { + pub head: *mut php_stream_filter, + pub tail: *mut php_stream_filter, + pub stream: *mut php_stream, +} +pub type php_stream_filter_chain = _php_stream_filter_chain; +#[repr(C)] +pub struct _php_stream_filter { + pub fops: *const php_stream_filter_ops, + pub abstract_: zval, + pub next: *mut php_stream_filter, + pub prev: *mut php_stream_filter, + pub is_persistent: ::std::os::raw::c_int, + pub chain: *mut php_stream_filter_chain, + pub buffer: php_stream_bucket_brigade, + pub res: *mut zend_resource, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_statbuf { + pub sb: zend_stat_t, +} +pub type php_stream_statbuf = _php_stream_statbuf; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_ops { + pub write: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + buf: *const ::std::os::raw::c_char, + count: usize, + ) -> isize, + >, + pub read: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + buf: *mut ::std::os::raw::c_char, + count: usize, + ) -> isize, + >, + pub close: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + close_handle: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >, + pub flush: ::std::option::Option< + unsafe extern "C" fn(stream: *mut php_stream) -> ::std::os::raw::c_int, + >, + pub label: *const ::std::os::raw::c_char, + pub seek: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + offset: zend_off_t, + whence: ::std::os::raw::c_int, + newoffset: *mut zend_off_t, + ) -> ::std::os::raw::c_int, + >, + pub cast: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + castas: ::std::os::raw::c_int, + ret: *mut *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, + pub stat: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + ssb: *mut php_stream_statbuf, + ) -> ::std::os::raw::c_int, + >, + pub set_option: ::std::option::Option< + unsafe extern "C" fn( + stream: *mut php_stream, + option: ::std::os::raw::c_int, + value: ::std::os::raw::c_int, + ptrparam: *mut ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int, + >, +} +pub type php_stream_ops = _php_stream_ops; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_wrapper_ops { + pub stream_opener: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + filename: *const ::std::os::raw::c_char, + mode: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + opened_path: *mut *mut zend_string, + context: *mut php_stream_context, + __php_stream_call_depth: ::std::os::raw::c_int, + __zend_filename: *const ::std::os::raw::c_char, + __zend_lineno: u32, + __zend_orig_filename: *const ::std::os::raw::c_char, + __zend_orig_lineno: u32, + ) -> *mut php_stream, + >, + pub stream_closer: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + stream: *mut php_stream, + ) -> ::std::os::raw::c_int, + >, + pub stream_stat: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + stream: *mut php_stream, + ssb: *mut php_stream_statbuf, + ) -> ::std::os::raw::c_int, + >, + pub url_stat: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + flags: ::std::os::raw::c_int, + ssb: *mut php_stream_statbuf, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub dir_opener: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + filename: *const ::std::os::raw::c_char, + mode: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + opened_path: *mut *mut zend_string, + context: *mut php_stream_context, + __php_stream_call_depth: ::std::os::raw::c_int, + __zend_filename: *const ::std::os::raw::c_char, + __zend_lineno: u32, + __zend_orig_filename: *const ::std::os::raw::c_char, + __zend_orig_lineno: u32, + ) -> *mut php_stream, + >, + pub label: *const ::std::os::raw::c_char, + pub unlink: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub rename: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url_from: *const ::std::os::raw::c_char, + url_to: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub stream_mkdir: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + mode: ::std::os::raw::c_int, + options: ::std::os::raw::c_int, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub stream_rmdir: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, + pub stream_metadata: ::std::option::Option< + unsafe extern "C" fn( + wrapper: *mut php_stream_wrapper, + url: *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + value: *mut ::std::os::raw::c_void, + context: *mut php_stream_context, + ) -> ::std::os::raw::c_int, + >, +} +pub type php_stream_wrapper_ops = _php_stream_wrapper_ops; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _php_stream_wrapper { + pub wops: *const php_stream_wrapper_ops, + pub abstract_: *mut ::std::os::raw::c_void, + pub is_url: ::std::os::raw::c_int, +} +#[repr(C)] +pub struct _php_stream { + pub ops: *const php_stream_ops, + pub abstract_: *mut ::std::os::raw::c_void, + pub readfilters: php_stream_filter_chain, + pub writefilters: php_stream_filter_chain, + pub wrapper: *mut php_stream_wrapper, + pub wrapperthis: *mut ::std::os::raw::c_void, + pub wrapperdata: zval, + pub _bitfield_align_1: [u8; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 2usize]>, + pub mode: [::std::os::raw::c_char; 16usize], + pub flags: u32, + pub res: *mut zend_resource, + pub stdiocast: *mut FILE, + pub orig_path: *mut ::std::os::raw::c_char, + pub ctx: *mut zend_resource, + pub position: zend_off_t, + pub readbuf: *mut ::std::os::raw::c_uchar, + pub readbuflen: usize, + pub readpos: zend_off_t, + pub writepos: zend_off_t, + pub chunk_size: usize, + pub open_filename: *const ::std::os::raw::c_char, + pub open_lineno: u32, + pub enclosing_stream: *mut _php_stream, +} +impl _php_stream { + #[inline] + pub fn is_persistent(&self) -> u16 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u16) } + } + #[inline] + pub fn set_is_persistent(&mut self, val: u16) { + unsafe { + let val: u16 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn in_free(&self) -> u16 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 2u8) as u16) } + } + #[inline] + pub fn set_in_free(&mut self, val: u16) { + unsafe { + let val: u16 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 2u8, val as u64) + } + } + #[inline] + pub fn eof(&self) -> u16 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u16) } + } + #[inline] + pub fn set_eof(&mut self, val: u16) { + unsafe { + let val: u16 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn __exposed(&self) -> u16 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u16) } + } + #[inline] + pub fn set___exposed(&mut self, val: u16) { + unsafe { + let val: u16 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn fclose_stdiocast(&self) -> u16 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 2u8) as u16) } + } + #[inline] + pub fn set_fclose_stdiocast(&mut self, val: u16) { + unsafe { + let val: u16 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 2u8, val as u64) + } + } + #[inline] + pub fn has_buffered_data(&self) -> u16 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u16) } + } + #[inline] + pub fn set_has_buffered_data(&mut self, val: u16) { + unsafe { + let val: u16 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn fclose_stdiocast_flush_in_progress(&self) -> u16 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u16) } + } + #[inline] + pub fn set_fclose_stdiocast_flush_in_progress(&mut self, val: u16) { + unsafe { + let val: u16 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + is_persistent: u16, + in_free: u16, + eof: u16, + __exposed: u16, + fclose_stdiocast: u16, + has_buffered_data: u16, + fclose_stdiocast_flush_in_progress: u16, + ) -> __BindgenBitfieldUnit<[u8; 2usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 2usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let is_persistent: u16 = unsafe { ::std::mem::transmute(is_persistent) }; + is_persistent as u64 + }); + __bindgen_bitfield_unit.set(1usize, 2u8, { + let in_free: u16 = unsafe { ::std::mem::transmute(in_free) }; + in_free as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let eof: u16 = unsafe { ::std::mem::transmute(eof) }; + eof as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let __exposed: u16 = unsafe { ::std::mem::transmute(__exposed) }; + __exposed as u64 + }); + __bindgen_bitfield_unit.set(5usize, 2u8, { + let fclose_stdiocast: u16 = unsafe { ::std::mem::transmute(fclose_stdiocast) }; + fclose_stdiocast as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let has_buffered_data: u16 = unsafe { ::std::mem::transmute(has_buffered_data) }; + has_buffered_data as u64 + }); + __bindgen_bitfield_unit.set(8usize, 1u8, { + let fclose_stdiocast_flush_in_progress: u16 = + unsafe { ::std::mem::transmute(fclose_stdiocast_flush_in_progress) }; + fclose_stdiocast_flush_in_progress as u64 + }); + __bindgen_bitfield_unit + } +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _zend_ini_entry_def { @@ -1678,3 +2263,76 @@ extern "C" { extern "C" { pub static mut zend_ce_stringable: *mut zend_class_entry; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sapi_headers_struct { + pub headers: zend_llist, + pub http_response_code: ::std::os::raw::c_int, + pub send_default_content_type: ::std::os::raw::c_uchar, + pub mimetype: *mut ::std::os::raw::c_char, + pub http_status_line: *mut ::std::os::raw::c_char, +} +pub type sapi_post_entry = _sapi_post_entry; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sapi_request_info { + pub request_method: *const ::std::os::raw::c_char, + pub query_string: *mut ::std::os::raw::c_char, + pub cookie_data: *mut ::std::os::raw::c_char, + pub content_length: zend_long, + pub path_translated: *mut ::std::os::raw::c_char, + pub request_uri: *mut ::std::os::raw::c_char, + pub request_body: *mut _php_stream, + pub content_type: *const ::std::os::raw::c_char, + pub headers_only: bool, + pub no_headers: bool, + pub headers_read: bool, + pub post_entry: *mut sapi_post_entry, + pub content_type_dup: *mut ::std::os::raw::c_char, + pub auth_user: *mut ::std::os::raw::c_char, + pub auth_password: *mut ::std::os::raw::c_char, + pub auth_digest: *mut ::std::os::raw::c_char, + pub argv0: *mut ::std::os::raw::c_char, + pub current_user: *mut ::std::os::raw::c_char, + pub current_user_length: ::std::os::raw::c_int, + pub argc: ::std::os::raw::c_int, + pub argv: *mut *mut ::std::os::raw::c_char, + pub proto_num: ::std::os::raw::c_int, +} +#[repr(C)] +pub struct _sapi_globals_struct { + pub server_context: *mut ::std::os::raw::c_void, + pub request_info: sapi_request_info, + pub sapi_headers: sapi_headers_struct, + pub read_post_bytes: i64, + pub post_read: ::std::os::raw::c_uchar, + pub headers_sent: ::std::os::raw::c_uchar, + pub global_stat: zend_stat_t, + pub default_mimetype: *mut ::std::os::raw::c_char, + pub default_charset: *mut ::std::os::raw::c_char, + pub rfc1867_uploaded_files: *mut HashTable, + pub post_max_size: zend_long, + pub options: ::std::os::raw::c_int, + pub sapi_started: bool, + pub global_request_time: f64, + pub known_post_content_types: HashTable, + pub callback_func: zval, + pub fci_cache: zend_fcall_info_cache, +} +pub type sapi_globals_struct = _sapi_globals_struct; +extern "C" { + pub static mut sapi_globals: sapi_globals_struct; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _sapi_post_entry { + pub content_type: *mut ::std::os::raw::c_char, + pub content_type_len: u32, + pub post_reader: ::std::option::Option, + pub post_handler: ::std::option::Option< + unsafe extern "C" fn( + content_type_dup: *mut ::std::os::raw::c_char, + arg: *mut ::std::os::raw::c_void, + ), + >, +} diff --git a/src/ffi.rs b/src/ffi.rs index a6c3d94865..c658239af6 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -26,6 +26,7 @@ extern "C" { pub fn ext_php_rs_zend_object_alloc(obj_size: usize, ce: *mut zend_class_entry) -> *mut c_void; pub fn ext_php_rs_zend_object_release(obj: *mut zend_object); pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals; + pub fn ext_php_rs_sapi_globals() -> *mut sapi_globals_struct; pub fn ext_php_rs_zend_try_catch( func: unsafe extern "C" fn(*const c_void) -> *const c_void, ctx: *const c_void, diff --git a/src/wrapper.c b/src/wrapper.c index a1fd900ff5..a708e57cd8 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -40,6 +40,18 @@ zend_executor_globals *ext_php_rs_executor_globals() { #endif } +sapi_globals_struct *ext_php_rs_sapi_globals() { +#ifdef ZTS +#ifdef ZEND_ENABLE_STATIC_TSRMLS_CACHE + return TSRMG_FAST_BULK_STATIC(sapi_globals_offset, sapi_globals_struct); +#else + return TSRMG_FAST_BULK(sapi_globals_offset, sapi_globals_struct *); +#endif +#else + return &sapi_globals; +#endif +} + bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result) { zend_try { *result = callback(ctx); diff --git a/src/wrapper.h b/src/wrapper.h index e4e55517ca..cb5c6678fb 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -21,6 +21,7 @@ #include "zend_inheritance.h" #include "zend_interfaces.h" #include "zend_ini.h" +#include "main/SAPI.h" zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent); void ext_php_rs_zend_string_release(zend_string *zs); @@ -30,6 +31,7 @@ void ext_php_rs_set_known_valid_utf8(zend_string *zs); const char *ext_php_rs_php_build_id(); void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce); void ext_php_rs_zend_object_release(zend_object *obj); -zend_executor_globals *ext_php_rs_executor_globals(); +zend_executor_globals *ext_php_rs_executor_globals();; +sapi_globals_struct *ext_php_rs_sapi_globals(); bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result); void ext_php_rs_zend_bailout(); diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 9761ab91d5..cf8ded802c 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -8,12 +8,15 @@ use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; #[cfg(php82)] use crate::ffi::zend_atomic_bool_store; -use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals, zend_ini_entry}; +use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals, _sapi_globals_struct, ext_php_rs_sapi_globals, zend_ini_entry}; use crate::types::{ZendHashTable, ZendObject}; /// Stores global variables used in the PHP executor. pub type ExecutorGlobals = _zend_executor_globals; +/// Stores global SAPI variables used in the PHP executor. +pub type SapiGlobals = _sapi_globals_struct; + impl ExecutorGlobals { /// Returns a reference to the PHP executor globals. /// @@ -127,12 +130,52 @@ impl ExecutorGlobals { } } +impl SapiGlobals { + /// Returns a reference to the PHP SAPI globals. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get() -> GlobalReadGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { ext_php_rs_sapi_globals().as_ref() } + .expect("Static executor globals were invalid"); + let guard = SAPI_LOCK.read(); + GlobalReadGuard { globals, guard } + } + + /// Returns a mutable reference to the PHP executor globals. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get_mut() -> GlobalWriteGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { ext_php_rs_sapi_globals().as_mut() } + .expect("Static executor globals were invalid"); + let guard = SAPI_LOCK.write(); + GlobalWriteGuard { globals, guard } + } +} + /// Executor globals rwlock. /// /// PHP provides no indication if the executor globals are being accessed so /// this is only effective on the Rust side. static GLOBALS_LOCK: RwLock<()> = const_rwlock(()); +/// SAPI globals rwlock. +/// +/// PHP provides no indication if the executor globals are being accessed so +/// this is only effective on the Rust side. +static SAPI_LOCK: RwLock<()> = const_rwlock(()); + /// Wrapper guard that contains a reference to a given type `T`. Dropping a /// guard releases the lock on the relevant rwlock. pub struct GlobalReadGuard { diff --git a/src/zend/mod.rs b/src/zend/mod.rs index af8a5c2d8e..67840f908d 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -20,6 +20,7 @@ pub use ex::ExecuteData; pub use function::Function; pub use function::FunctionEntry; pub use globals::ExecutorGlobals; +pub use globals::SapiGlobals; pub use handlers::ZendObjectHandlers; pub use ini_entry_def::IniEntryDef; pub use module::ModuleEntry; From 53d72499638ca61ed0fef31d20451c4ea2b2a34d Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Tue, 21 Nov 2023 20:18:47 +0100 Subject: [PATCH 070/100] Fixup --- src/zend/globals.rs | 5 ++++- src/zend/handlers.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/zend/globals.rs b/src/zend/globals.rs index cf8ded802c..cbea80a2b4 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -8,7 +8,10 @@ use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::boxed::ZBox; #[cfg(php82)] use crate::ffi::zend_atomic_bool_store; -use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals, _sapi_globals_struct, ext_php_rs_sapi_globals, zend_ini_entry}; +use crate::ffi::{ + _sapi_globals_struct, _zend_executor_globals, ext_php_rs_executor_globals, + ext_php_rs_sapi_globals, zend_ini_entry, +}; use crate::types::{ZendHashTable, ZendObject}; /// Stores global variables used in the PHP executor. diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index 11d220ba08..f8882a1d7e 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -238,7 +238,7 @@ impl ZendObjectHandlers { let mut zv = Zval::new(); val.get(self_, &mut zv)?; - if zend_is_true(&mut zv) == 1 { + if zend_is_true(&zv) == 1 { return Ok(1); } } From fc6a8aee5e1f11c8613b693790d00c6238f5d46d Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Tue, 21 Nov 2023 20:23:18 +0100 Subject: [PATCH 071/100] Fixup --- src/zend/handlers.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index f8882a1d7e..7e88a7e053 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -238,7 +238,8 @@ impl ZendObjectHandlers { let mut zv = Zval::new(); val.get(self_, &mut zv)?; - if zend_is_true(&zv) == 1 { + #[allow(clippy::unnecessary_mut_passed)] + if zend_is_true(&mut zv) == 1 { return Ok(1); } } From 116c6987b9f61bbda0ced0b6fc5211442eb810c4 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Tue, 21 Nov 2023 20:26:49 +0100 Subject: [PATCH 072/100] Add version --- build.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.rs b/build.rs index 601a0c359a..5afef52e2d 100644 --- a/build.rs +++ b/build.rs @@ -228,6 +228,8 @@ fn check_php_version(info: &PHPInfo) -> Result<()> { const PHP_82_API_VER: u32 = 20220829; + const PHP_83_API_VER: u32 = 20230831; + println!("cargo:rustc-cfg=php80"); if (PHP_81_API_VER..PHP_82_API_VER).contains(&version) { @@ -238,6 +240,10 @@ fn check_php_version(info: &PHPInfo) -> Result<()> { println!("cargo:rustc-cfg=php82"); } + if version >= PHP_83_API_VER { + println!("cargo:rustc-cfg=php83"); + } + Ok(()) } From 10eac2497add0974c805fef06b42d2401860118f Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 11:53:25 +0100 Subject: [PATCH 073/100] Expose SAPI module --- allowed_bindings.rs | 3 +++ src/ffi.rs | 1 + src/wrapper.c | 4 ++++ src/wrapper.h | 1 + src/zend/globals.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 53 insertions(+) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 53e5a5e2e9..2046ab5275 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -27,6 +27,7 @@ bind! { _emalloc, _zend_executor_globals, _sapi_globals_struct, + _sapi_module_struct, _zend_expected_type, _zend_expected_type_Z_EXPECTED_ARRAY, _zend_expected_type_Z_EXPECTED_BOOL, @@ -243,6 +244,7 @@ bind! { zend_class_unserialize_deny, zend_executor_globals, sapi_globals_struct, + sapi_module_struct, zend_objects_store_del, zend_hash_move_forward_ex, zend_hash_get_current_key_type_ex, @@ -254,6 +256,7 @@ bind! { ZEND_ACC_NOT_SERIALIZABLE, executor_globals, sapi_globals, + sapi_module, php_printf, __zend_malloc, tsrm_get_ls_cache, diff --git a/src/ffi.rs b/src/ffi.rs index c658239af6..343b1fada2 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -27,6 +27,7 @@ extern "C" { pub fn ext_php_rs_zend_object_release(obj: *mut zend_object); pub fn ext_php_rs_executor_globals() -> *mut zend_executor_globals; pub fn ext_php_rs_sapi_globals() -> *mut sapi_globals_struct; + pub fn ext_php_rs_sapi_module() -> *mut sapi_module_struct; pub fn ext_php_rs_zend_try_catch( func: unsafe extern "C" fn(*const c_void) -> *const c_void, ctx: *const c_void, diff --git a/src/wrapper.c b/src/wrapper.c index a708e57cd8..109b5aee9b 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -52,6 +52,10 @@ sapi_globals_struct *ext_php_rs_sapi_globals() { #endif } +sapi_module_struct *ext_php_rs_sapi_module() { + return &sapi_module; +} + bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result) { zend_try { *result = callback(ctx); diff --git a/src/wrapper.h b/src/wrapper.h index cb5c6678fb..883f1b812d 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -33,5 +33,6 @@ void *ext_php_rs_zend_object_alloc(size_t obj_size, zend_class_entry *ce); void ext_php_rs_zend_object_release(zend_object *obj); zend_executor_globals *ext_php_rs_executor_globals();; sapi_globals_struct *ext_php_rs_sapi_globals(); +sapi_module_struct *ext_php_rs_sapi_module(); bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result); void ext_php_rs_zend_bailout(); diff --git a/src/zend/globals.rs b/src/zend/globals.rs index cbea80a2b4..fc4864f6d9 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -10,6 +10,7 @@ use crate::boxed::ZBox; use crate::ffi::zend_atomic_bool_store; use crate::ffi::{ _sapi_globals_struct, _zend_executor_globals, ext_php_rs_executor_globals, + _sapi_module_struct, ext_php_rs_sapi_module, ext_php_rs_sapi_globals, zend_ini_entry, }; use crate::types::{ZendHashTable, ZendObject}; @@ -20,6 +21,9 @@ pub type ExecutorGlobals = _zend_executor_globals; /// Stores global SAPI variables used in the PHP executor. pub type SapiGlobals = _sapi_globals_struct; +/// Stores the SAPI module used in the PHP executor. +pub type SapiModule = _sapi_module_struct; + impl ExecutorGlobals { /// Returns a reference to the PHP executor globals. /// @@ -167,6 +171,40 @@ impl SapiGlobals { } } +impl SapiModule { + /// Returns a reference to the PHP SAPI module. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get() -> GlobalReadGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { ext_php_rs_sapi_module().as_ref() } + .expect("Static executor globals were invalid"); + let guard = SAPI_MODULE_LOCK.read(); + GlobalReadGuard { globals, guard } + } + + /// Returns a mutable reference to the PHP executor globals. + /// + /// The executor globals are guarded by a RwLock. There can be multiple + /// immutable references at one time but only ever one mutable reference. + /// Attempting to retrieve the globals while already holding the global + /// guard will lead to a deadlock. Dropping the globals guard will release + /// the lock. + pub fn get_mut() -> GlobalWriteGuard { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + let globals = unsafe { ext_php_rs_sapi_module().as_mut() } + .expect("Static executor globals were invalid"); + let guard = SAPI_MODULE_LOCK.write(); + GlobalWriteGuard { globals, guard } + } +} + /// Executor globals rwlock. /// /// PHP provides no indication if the executor globals are being accessed so @@ -179,6 +217,12 @@ static GLOBALS_LOCK: RwLock<()> = const_rwlock(()); /// this is only effective on the Rust side. static SAPI_LOCK: RwLock<()> = const_rwlock(()); +/// SAPI globals rwlock. +/// +/// PHP provides no indication if the executor globals are being accessed so +/// this is only effective on the Rust side. +static SAPI_MODULE_LOCK: RwLock<()> = const_rwlock(()); + /// Wrapper guard that contains a reference to a given type `T`. Dropping a /// guard releases the lock on the relevant rwlock. pub struct GlobalReadGuard { From 0fbf54a0969357e746e161c0c8d54d332692d89a Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 11:55:33 +0100 Subject: [PATCH 074/100] Expose SAPI module --- src/zend/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zend/mod.rs b/src/zend/mod.rs index 67840f908d..5ef716115b 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -21,6 +21,7 @@ pub use function::Function; pub use function::FunctionEntry; pub use globals::ExecutorGlobals; pub use globals::SapiGlobals; +pub use globals::SapiModule; pub use handlers::ZendObjectHandlers; pub use ini_entry_def::IniEntryDef; pub use module::ModuleEntry; From 917d7e2f1830386a0e6ee441c6739569d7d81329 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 11:58:36 +0100 Subject: [PATCH 075/100] Fmt --- src/zend/globals.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/zend/globals.rs b/src/zend/globals.rs index fc4864f6d9..6a6bf70424 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -9,9 +9,8 @@ use crate::boxed::ZBox; #[cfg(php82)] use crate::ffi::zend_atomic_bool_store; use crate::ffi::{ - _sapi_globals_struct, _zend_executor_globals, ext_php_rs_executor_globals, - _sapi_module_struct, ext_php_rs_sapi_module, - ext_php_rs_sapi_globals, zend_ini_entry, + _sapi_globals_struct, _sapi_module_struct, _zend_executor_globals, ext_php_rs_executor_globals, + ext_php_rs_sapi_globals, ext_php_rs_sapi_module, zend_ini_entry, }; use crate::types::{ZendHashTable, ZendObject}; From 3081630a3325e13da6cae20bb874a276e7bece81 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 12:10:40 +0100 Subject: [PATCH 076/100] Update docsrs_bindings.rs --- docsrs_bindings.rs | 117 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index dd3978a472..2617c4e6c6 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -201,6 +201,8 @@ pub type __time_t = ::std::os::raw::c_long; pub type __blksize_t = ::std::os::raw::c_long; pub type __blkcnt_t = ::std::os::raw::c_long; pub type __syscall_slong_t = ::std::os::raw::c_long; +pub type gid_t = __gid_t; +pub type uid_t = __uid_t; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __sigset_t { @@ -2265,6 +2267,12 @@ extern "C" { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct sapi_header_struct { + pub header: *mut ::std::os::raw::c_char, + pub header_len: usize, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct sapi_headers_struct { pub headers: zend_llist, pub http_response_code: ::std::os::raw::c_int, @@ -2273,6 +2281,10 @@ pub struct sapi_headers_struct { pub http_status_line: *mut ::std::os::raw::c_char, } pub type sapi_post_entry = _sapi_post_entry; +pub type sapi_module_struct = _sapi_module_struct; +extern "C" { + pub static mut sapi_module: sapi_module_struct; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct sapi_request_info { @@ -2323,6 +2335,111 @@ pub type sapi_globals_struct = _sapi_globals_struct; extern "C" { pub static mut sapi_globals: sapi_globals_struct; } +pub const sapi_header_op_enum_SAPI_HEADER_REPLACE: sapi_header_op_enum = 0; +pub const sapi_header_op_enum_SAPI_HEADER_ADD: sapi_header_op_enum = 1; +pub const sapi_header_op_enum_SAPI_HEADER_DELETE: sapi_header_op_enum = 2; +pub const sapi_header_op_enum_SAPI_HEADER_DELETE_ALL: sapi_header_op_enum = 3; +pub const sapi_header_op_enum_SAPI_HEADER_SET_STATUS: sapi_header_op_enum = 4; +pub type sapi_header_op_enum = ::std::os::raw::c_uint; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _sapi_module_struct { + pub name: *mut ::std::os::raw::c_char, + pub pretty_name: *mut ::std::os::raw::c_char, + pub startup: ::std::option::Option< + unsafe extern "C" fn(sapi_module: *mut _sapi_module_struct) -> ::std::os::raw::c_int, + >, + pub shutdown: ::std::option::Option< + unsafe extern "C" fn(sapi_module: *mut _sapi_module_struct) -> ::std::os::raw::c_int, + >, + pub activate: ::std::option::Option ::std::os::raw::c_int>, + pub deactivate: ::std::option::Option ::std::os::raw::c_int>, + pub ub_write: ::std::option::Option< + unsafe extern "C" fn(str_: *const ::std::os::raw::c_char, str_length: usize) -> usize, + >, + pub flush: + ::std::option::Option, + pub get_stat: ::std::option::Option *mut zend_stat_t>, + pub getenv: ::std::option::Option< + unsafe extern "C" fn( + name: *const ::std::os::raw::c_char, + name_len: usize, + ) -> *mut ::std::os::raw::c_char, + >, + pub sapi_error: ::std::option::Option< + unsafe extern "C" fn( + type_: ::std::os::raw::c_int, + error_msg: *const ::std::os::raw::c_char, + ... + ), + >, + pub header_handler: ::std::option::Option< + unsafe extern "C" fn( + sapi_header: *mut sapi_header_struct, + op: sapi_header_op_enum, + sapi_headers: *mut sapi_headers_struct, + ) -> ::std::os::raw::c_int, + >, + pub send_headers: ::std::option::Option< + unsafe extern "C" fn(sapi_headers: *mut sapi_headers_struct) -> ::std::os::raw::c_int, + >, + pub send_header: ::std::option::Option< + unsafe extern "C" fn( + sapi_header: *mut sapi_header_struct, + server_context: *mut ::std::os::raw::c_void, + ), + >, + pub read_post: ::std::option::Option< + unsafe extern "C" fn(buffer: *mut ::std::os::raw::c_char, count_bytes: usize) -> usize, + >, + pub read_cookies: ::std::option::Option *mut ::std::os::raw::c_char>, + pub register_server_variables: + ::std::option::Option, + pub log_message: ::std::option::Option< + unsafe extern "C" fn( + message: *const ::std::os::raw::c_char, + syslog_type_int: ::std::os::raw::c_int, + ), + >, + pub get_request_time: + ::std::option::Option zend_result>, + pub terminate_process: ::std::option::Option, + pub php_ini_path_override: *mut ::std::os::raw::c_char, + pub default_post_reader: ::std::option::Option, + pub treat_data: ::std::option::Option< + unsafe extern "C" fn( + arg: ::std::os::raw::c_int, + str_: *mut ::std::os::raw::c_char, + destArray: *mut zval, + ), + >, + pub executable_location: *mut ::std::os::raw::c_char, + pub php_ini_ignore: ::std::os::raw::c_int, + pub php_ini_ignore_cwd: ::std::os::raw::c_int, + pub get_fd: ::std::option::Option< + unsafe extern "C" fn(fd: *mut ::std::os::raw::c_int) -> ::std::os::raw::c_int, + >, + pub force_http_10: ::std::option::Option ::std::os::raw::c_int>, + pub get_target_uid: + ::std::option::Option ::std::os::raw::c_int>, + pub get_target_gid: + ::std::option::Option ::std::os::raw::c_int>, + pub input_filter: ::std::option::Option< + unsafe extern "C" fn( + arg: ::std::os::raw::c_int, + var: *const ::std::os::raw::c_char, + val: *mut *mut ::std::os::raw::c_char, + val_len: usize, + new_val_len: *mut usize, + ) -> ::std::os::raw::c_uint, + >, + pub ini_defaults: + ::std::option::Option, + pub phpinfo_as_text: ::std::os::raw::c_int, + pub ini_entries: *const ::std::os::raw::c_char, + pub additional_functions: *const zend_function_entry, + pub input_filter_init: ::std::option::Option ::std::os::raw::c_uint>, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _sapi_post_entry { From b59dcd91c73785a1bd9c87d24fa572edc5c99575 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 12:33:56 +0100 Subject: [PATCH 077/100] Bump --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a484175510..0deea94671 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/davidcole1340/ext-php-rs" homepage = "https://github.com/davidcole1340/ext-php-rs" license = "MIT OR Apache-2.0" keywords = ["php", "ffi", "zend"] -version = "0.10.4" +version = "0.10.5" authors = ["David Cole "] edition = "2018" categories = ["api-bindings"] From 6c0719c96b744edd1fcbf9a1259e7263d01a1227 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 12:52:33 +0100 Subject: [PATCH 078/100] Run tests using clang 16 --- .github/workflows/build.yml | 2 +- .github/workflows/docs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 557d80cd54..a21061e121 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] php: ["8.0", "8.1", "8.2", "8.3"] rust: [stable, nightly] - clang: ["14"] + clang: ["16"] phpts: [ts, nts] exclude: # ext-php-rs requires nightly Rust when on Windows. diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5f55df1c91..522682ba03 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,7 @@ jobs: matrix: os: ["ubuntu-latest"] php: ["8.2"] - clang: ["14"] + clang: ["17"] mdbook: ["latest"] steps: - name: Checkout code From 8761816e23fa8b9cbb3ab77f2e44cc955e5a32ac Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 13:09:30 +0100 Subject: [PATCH 079/100] Test more combos --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a21061e121..409b016648 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] php: ["8.0", "8.1", "8.2", "8.3"] rust: [stable, nightly] - clang: ["16"] + clang: ["15", "17"] phpts: [ts, nts] exclude: # ext-php-rs requires nightly Rust when on Windows. @@ -26,6 +26,8 @@ jobs: # setup-php doesn't support thread safe PHP on Linux and macOS. - os: macos-latest phpts: ts + - os: macos-latest + clang: "17" - os: ubuntu-latest phpts: ts env: From 5bb9ab1e452dae7c44434fbfc4cf5abbb9ea3a27 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 13:09:55 +0100 Subject: [PATCH 080/100] Test more combos --- .github/workflows/build.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 409b016648..e324863496 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,10 +26,14 @@ jobs: # setup-php doesn't support thread safe PHP on Linux and macOS. - os: macos-latest phpts: ts + - os: ubuntu-latest + phpts: ts - os: macos-latest clang: "17" - os: ubuntu-latest - phpts: ts + clang: "15" + - os: windows-latest + clang: "15" env: CARGO_TERM_COLOR: always steps: From 3cd7a8a1f75c92fc64910c0e4456c8cbd99dc00b Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Sun, 5 Feb 2023 12:08:28 +0200 Subject: [PATCH 081/100] Add support for Zval IS_INDIRECT As mentioned in #219, I believe we're making incorrect use of IS_CALLABLE on Zval types right now. Zval type bits are never actually stored as IS_CALLABLE, which overlaps with the _actual_ type value of IS_INDIRECT. IS_INDIRECT is almost the same as IS_REFERENCE, but is a direct reference to another Zval rather than reference object. As `Zval::is_callable()` and `Zval::callable()` don't actually make use of `IS_CALLABLE` then I think it's safe to switch this out. --- allowed_bindings.rs | 1 + docsrs_bindings.rs | 1 + src/flags.rs | 28 ++++++++++++++++------------ src/types/zval.rs | 24 ++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 2046ab5275..40556e9e3f 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -143,6 +143,7 @@ bind! { IS_CONSTANT_AST_EX, IS_DOUBLE, IS_FALSE, + IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_MIXED, diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 2617c4e6c6..34da760b5d 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -99,6 +99,7 @@ pub const IS_CONSTANT_AST: u32 = 11; pub const IS_CALLABLE: u32 = 12; pub const IS_VOID: u32 = 14; pub const IS_MIXED: u32 = 16; +pub const IS_INDIRECT: u32 = 12; pub const IS_PTR: u32 = 13; pub const _IS_BOOL: u32 = 18; pub const Z_TYPE_FLAGS_SHIFT: u32 = 8; diff --git a/src/flags.rs b/src/flags.rs index 60897a0654..9e814d2a26 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -8,14 +8,14 @@ use crate::ffi::{ CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, E_COMPILE_ERROR, E_COMPILE_WARNING, E_CORE_ERROR, E_CORE_WARNING, E_DEPRECATED, E_ERROR, E_NOTICE, E_PARSE, E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING, - E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_MIXED, - IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, - IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, PHP_INI_PERDIR, PHP_INI_SYSTEM, - PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, - ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, - ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, - ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE, - ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE, + E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_INDIRECT, IS_LONG, + IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, + IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, PHP_INI_PERDIR, + PHP_INI_SYSTEM, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, + ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, + ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, + ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, + ZEND_ACC_HAS_RETURN_TYPE, ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE, ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, ZEND_ACC_NEARLY_LINKED, ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE, ZEND_ACC_PROMOTED, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES, @@ -246,6 +246,7 @@ pub enum DataType { Mixed, Bool, Ptr, + Indirect, } impl Default for DataType { @@ -269,6 +270,7 @@ impl DataType { DataType::Object(_) => IS_OBJECT, DataType::Resource => IS_RESOURCE, DataType::Reference => IS_RESOURCE, + DataType::Indirect => IS_INDIRECT, DataType::Callable => IS_CALLABLE, DataType::ConstantExpression => IS_CONSTANT_AST, DataType::Void => IS_VOID, @@ -337,6 +339,7 @@ impl From for DataType { contains!(IS_VOID, Void); contains!(IS_PTR, Ptr); + contains!(IS_INDIRECT, Indirect); contains!(IS_CALLABLE, Callable); contains!(IS_CONSTANT_AST, ConstantExpression); contains!(IS_REFERENCE, Reference); @@ -379,6 +382,7 @@ impl Display for DataType { DataType::Bool => write!(f, "Bool"), DataType::Mixed => write!(f, "Mixed"), DataType::Ptr => write!(f, "Pointer"), + DataType::Indirect => write!(f, "Indirect"), } } } @@ -388,9 +392,9 @@ mod tests { use super::DataType; use crate::ffi::{ IS_ARRAY, IS_ARRAY_EX, IS_CALLABLE, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, - IS_FALSE, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_PTR, - IS_REFERENCE, IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, IS_STRING_EX, - IS_TRUE, IS_UNDEF, IS_VOID, + IS_FALSE, IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, + IS_PTR, IS_REFERENCE, IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, + IS_STRING_EX, IS_TRUE, IS_UNDEF, IS_VOID, }; use std::convert::TryFrom; @@ -414,7 +418,7 @@ mod tests { test!(IS_RESOURCE, Resource); test!(IS_REFERENCE, Reference); test!(IS_CONSTANT_AST, ConstantExpression); - test!(IS_CALLABLE, Callable); + test!(IS_INDIRECT, Indirect); test!(IS_VOID, Void); test!(IS_PTR, Ptr); diff --git a/src/types/zval.rs b/src/types/zval.rs index 94b62aa985..97def9a09d 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -213,6 +213,24 @@ impl Zval { .try_call_method(name, params) } + /// Returns the value of the zval if it is an internal indirect reference. + pub fn indirect(&self) -> Option<&Zval> { + if self.is_indirect() { + Some(unsafe { &*(self.value.zv as *mut Zval) }) + } else { + None + } + } + + /// Returns a mutable reference to the zval if it is an internal indirect reference. + pub fn indirect_mut(&self) -> Option<&mut Zval> { + if self.is_indirect() { + Some(unsafe { &mut *(self.value.zv as *mut Zval) }) + } else { + None + } + } + /// Returns the value of the zval if it is a reference. pub fn reference(&self) -> Option<&Zval> { if self.is_reference() { @@ -329,6 +347,11 @@ impl Zval { self.get_type() == DataType::Reference } + /// Returns true if the zval is a reference, false otherwise. + pub fn is_indirect(&self) -> bool { + self.get_type() == DataType::Indirect + } + /// Returns true if the zval is callable, false otherwise. pub fn is_callable(&self) -> bool { let ptr: *const Self = self; @@ -601,6 +624,7 @@ impl Debug for Zval { DataType::ConstantExpression => field!(Option::<()>::None), DataType::Void => field!(Option::<()>::None), DataType::Bool => field!(self.bool()), + DataType::Indirect => field!(self.indirect()), // SAFETY: We are not accessing the pointer. DataType::Ptr => field!(unsafe { self.ptr::() }), }; From 05eb9fc3eafba1325f44965822d9aed91b7e8789 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 13:32:15 +0100 Subject: [PATCH 082/100] Use better name --- src/zend/function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zend/function.rs b/src/zend/function.rs index 234fb625cc..d4aa20b500 100644 --- a/src/zend/function.rs +++ b/src/zend/function.rs @@ -50,7 +50,7 @@ impl FunctionEntry { pub type Function = zend_function; impl Function { - pub fn type_(&self) -> FunctionType { + pub fn function_type(&self) -> FunctionType { FunctionType::from(unsafe { self.type_ }) } From 50dad0cec44dc0be7dda14d54bdab7299a22c8ca Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 24 Nov 2023 13:38:46 +0100 Subject: [PATCH 083/100] Manually drop zval --- src/exception.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/exception.rs b/src/exception.rs index e1f4c637f2..aed768aa40 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -206,8 +206,9 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> /// throw_object( error.into_zval(true).unwrap() ); /// ``` pub fn throw_object(zval: Zval) -> Result<()> { + let mut zv = core::mem::ManuallyDrop::new(zval); unsafe { - zend_throw_exception_object((&zval as *const _) as *mut _); + zend_throw_exception_object(core::ptr::addr_of_mut!(zv).cast()) }; Ok(()) } From 10d5f3a41a9d02b93bd0afe7df4493e83cc1ce4b Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 13:38:56 +0100 Subject: [PATCH 084/100] Fmt --- src/zend/function.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zend/function.rs b/src/zend/function.rs index d4aa20b500..6e6dd1a39e 100644 --- a/src/zend/function.rs +++ b/src/zend/function.rs @@ -9,7 +9,8 @@ use crate::{ zend_call_known_function, zend_fetch_function_str, zend_function, zend_function_entry, zend_hash_str_find_ptr_lc, }, - types::Zval, flags::FunctionType, + flags::FunctionType, + types::Zval, }; use super::ClassEntry; From 6fdd6a35bd3527c964553c58254d2aab1852ec25 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 24 Nov 2023 13:47:25 +0100 Subject: [PATCH 085/100] fmt --- src/exception.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/exception.rs b/src/exception.rs index aed768aa40..de7febab54 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -207,8 +207,6 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> /// ``` pub fn throw_object(zval: Zval) -> Result<()> { let mut zv = core::mem::ManuallyDrop::new(zval); - unsafe { - zend_throw_exception_object(core::ptr::addr_of_mut!(zv).cast()) - }; + unsafe { zend_throw_exception_object(core::ptr::addr_of_mut!(zv).cast()) }; Ok(()) } From 025f603e6e49207f9d453c0ce23266efb26df433 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 14:10:24 +0100 Subject: [PATCH 086/100] Fixup --- src/exception.rs | 4 ++-- src/types/zval.rs | 3 ++- src/zend/globals.rs | 54 +++++---------------------------------------- src/zend/mod.rs | 6 ++--- 4 files changed, 11 insertions(+), 56 deletions(-) diff --git a/src/exception.rs b/src/exception.rs index bf06487b75..aee0d9099f 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -69,8 +69,8 @@ impl PhpException { /// Set the Zval object for the exception. /// - /// Exceptions can be based of instantiated Zval objects when you are throwing a custom exception with - /// stateful properties. + /// Exceptions can be based of instantiated Zval objects when you are + /// throwing a custom exception with stateful properties. /// /// # Parameters /// diff --git a/src/types/zval.rs b/src/types/zval.rs index 97def9a09d..b0ceec77c4 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -222,7 +222,8 @@ impl Zval { } } - /// Returns a mutable reference to the zval if it is an internal indirect reference. + /// Returns a mutable reference to the zval if it is an internal indirect + /// reference. pub fn indirect_mut(&self) -> Option<&mut Zval> { if self.is_indirect() { Some(unsafe { &mut *(self.value.zv as *mut Zval) }) diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 8a5c71d5d1..046195fc5e 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -1,6 +1,7 @@ //! Types related to the PHP executor, sapi and process globals. use std::collections::HashMap; +use std::ffi::CStr; use std::ops::{Deref, DerefMut}; use std::slice; use std::str; @@ -11,26 +12,21 @@ use crate::boxed::ZBox; #[cfg(php82)] use crate::ffi::zend_atomic_bool_store; use crate::ffi::{ - _zend_executor_globals, ext_php_rs_executor_globals, ext_php_rs_file_globals, - ext_php_rs_process_globals, ext_php_rs_sapi_globals, php_core_globals, php_file_globals, - sapi_globals_struct, sapi_header_struct, sapi_headers_struct, sapi_request_info, + _sapi_module_struct, _zend_executor_globals, ext_php_rs_executor_globals, + ext_php_rs_file_globals, ext_php_rs_process_globals, ext_php_rs_sapi_globals, + ext_php_rs_sapi_module, php_core_globals, php_file_globals, sapi_globals_struct, + sapi_header_struct, sapi_headers_struct, sapi_request_info, zend_ini_entry, zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, TRACK_VARS_REQUEST, TRACK_VARS_SERVER, - _sapi_globals_struct, _sapi_module_struct, _zend_executor_globals, ext_php_rs_executor_globals, - ext_php_rs_sapi_globals, ext_php_rs_sapi_module, zend_ini_entry, }; use crate::types::{ZendHashTable, ZendObject, ZendStr}; use super::linked_list::ZendLinkedListIterator; -use crate::types::{ZendHashTable, ZendObject}; /// Stores global variables used in the PHP executor. pub type ExecutorGlobals = _zend_executor_globals; -/// Stores global SAPI variables used in the PHP executor. -pub type SapiGlobals = _sapi_globals_struct; - /// Stores the SAPI module used in the PHP executor. pub type SapiModule = _sapi_module_struct; @@ -157,40 +153,6 @@ impl ExecutorGlobals { } } -impl SapiGlobals { - /// Returns a reference to the PHP SAPI globals. - /// - /// The executor globals are guarded by a RwLock. There can be multiple - /// immutable references at one time but only ever one mutable reference. - /// Attempting to retrieve the globals while already holding the global - /// guard will lead to a deadlock. Dropping the globals guard will release - /// the lock. - pub fn get() -> GlobalReadGuard { - // SAFETY: PHP executor globals are statically declared therefore should never - // return an invalid pointer. - let globals = unsafe { ext_php_rs_sapi_globals().as_ref() } - .expect("Static executor globals were invalid"); - let guard = SAPI_LOCK.read(); - GlobalReadGuard { globals, guard } - } - - /// Returns a mutable reference to the PHP executor globals. - /// - /// The executor globals are guarded by a RwLock. There can be multiple - /// immutable references at one time but only ever one mutable reference. - /// Attempting to retrieve the globals while already holding the global - /// guard will lead to a deadlock. Dropping the globals guard will release - /// the lock. - pub fn get_mut() -> GlobalWriteGuard { - // SAFETY: PHP executor globals are statically declared therefore should never - // return an invalid pointer. - let globals = unsafe { ext_php_rs_sapi_globals().as_mut() } - .expect("Static executor globals were invalid"); - let guard = SAPI_LOCK.write(); - GlobalWriteGuard { globals, guard } - } -} - impl SapiModule { /// Returns a reference to the PHP SAPI module. /// @@ -556,12 +518,6 @@ static PROCESS_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); static SAPI_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); static FILE_GLOBALS_LOCK: RwLock<()> = const_rwlock(()); -/// SAPI globals rwlock. -/// -/// PHP provides no indication if the executor globals are being accessed so -/// this is only effective on the Rust side. -static SAPI_LOCK: RwLock<()> = const_rwlock(()); - /// SAPI globals rwlock. /// /// PHP provides no indication if the executor globals are being accessed so diff --git a/src/zend/mod.rs b/src/zend/mod.rs index faf4187944..099374ab82 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -7,8 +7,8 @@ mod ex; mod function; mod globals; mod handlers; -mod linked_list; mod ini_entry_def; +mod linked_list; mod module; mod try_catch; @@ -27,12 +27,10 @@ pub use globals::ExecutorGlobals; pub use globals::FileGlobals; pub use globals::ProcessGlobals; pub use globals::SapiGlobals; -pub use handlers::ZendObjectHandlers; -pub use linked_list::ZendLinkedList; -pub use globals::SapiGlobals; pub use globals::SapiModule; pub use handlers::ZendObjectHandlers; pub use ini_entry_def::IniEntryDef; +pub use linked_list::ZendLinkedList; pub use module::ModuleEntry; #[cfg(feature = "embed")] pub(crate) use try_catch::panic_wrapper; From 582a38738ea32580ea2be00d47ac0d6455ec239f Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 14:12:23 +0100 Subject: [PATCH 087/100] Cleanup --- docsrs_bindings.rs | 175 +++++++++++++++++++++++++++++++++++++++++++++ src/zend/ex.rs | 2 +- 2 files changed, 176 insertions(+), 1 deletion(-) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index b25762d9d9..d6678ea069 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -182,6 +182,13 @@ pub const ZEND_MODULE_API_NO: u32 = 20230831; pub const USING_ZTS: u32 = 0; pub const MAY_BE_BOOL: u32 = 12; pub const MAY_BE_ANY: u32 = 1022; +pub const TRACK_VARS_POST: u32 = 0; +pub const TRACK_VARS_GET: u32 = 1; +pub const TRACK_VARS_COOKIE: u32 = 2; +pub const TRACK_VARS_SERVER: u32 = 3; +pub const TRACK_VARS_ENV: u32 = 4; +pub const TRACK_VARS_FILES: u32 = 5; +pub const TRACK_VARS_REQUEST: u32 = 6; pub const PHP_INI_USER: u32 = 1; pub const PHP_INI_PERDIR: u32 = 2; pub const PHP_INI_SYSTEM: u32 = 4; @@ -506,6 +513,19 @@ pub struct _zend_llist { pub traverse_ptr: *mut zend_llist_element, } pub type zend_llist = _zend_llist; +pub type zend_llist_position = *mut zend_llist_element; +extern "C" { + pub fn zend_llist_get_next_ex( + l: *mut zend_llist, + pos: *mut zend_llist_position, + ) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn zend_llist_get_prev_ex( + l: *mut zend_llist, + pos: *mut zend_llist_position, + ) -> *mut ::std::os::raw::c_void; +} pub type zend_string_init_interned_func_t = ::std::option::Option< unsafe extern "C" fn( str_: *const ::std::os::raw::c_char, @@ -1469,6 +1489,9 @@ pub struct _zend_executor_globals { pub reserved_stack_size: zend_ulong, pub reserved: [*mut ::std::os::raw::c_void; 6usize], } +extern "C" { + pub fn zend_is_auto_global(name: *mut zend_string) -> bool; +} pub type zend_module_entry = _zend_module_entry; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -2094,6 +2117,127 @@ impl _php_stream { __bindgen_bitfield_unit } } +extern "C" { + pub static mut php_stream_stdio_ops: php_stream_ops; +} +extern "C" { + pub fn php_register_url_stream_wrapper( + protocol: *const ::std::os::raw::c_char, + wrapper: *const php_stream_wrapper, + ) -> zend_result; +} +extern "C" { + pub fn php_unregister_url_stream_wrapper( + protocol: *const ::std::os::raw::c_char, + ) -> zend_result; +} +extern "C" { + pub fn php_register_url_stream_wrapper_volatile( + protocol: *mut zend_string, + wrapper: *mut php_stream_wrapper, + ) -> zend_result; +} +extern "C" { + pub fn php_unregister_url_stream_wrapper_volatile(protocol: *mut zend_string) -> zend_result; +} +extern "C" { + pub fn php_stream_locate_url_wrapper( + path: *const ::std::os::raw::c_char, + path_for_open: *mut *const ::std::os::raw::c_char, + options: ::std::os::raw::c_int, + ) -> *mut php_stream_wrapper; +} +pub type php_core_globals = _php_core_globals; +#[repr(C)] +pub struct _php_core_globals { + pub output_buffering: zend_long, + pub implicit_flush: bool, + pub enable_dl: bool, + pub display_errors: u8, + pub display_startup_errors: bool, + pub log_errors: bool, + pub ignore_repeated_errors: bool, + pub ignore_repeated_source: bool, + pub report_memleaks: bool, + pub output_handler: *mut ::std::os::raw::c_char, + pub unserialize_callback_func: *mut ::std::os::raw::c_char, + pub serialize_precision: zend_long, + pub memory_limit: zend_long, + pub max_input_time: zend_long, + pub error_log: *mut ::std::os::raw::c_char, + pub doc_root: *mut ::std::os::raw::c_char, + pub user_dir: *mut ::std::os::raw::c_char, + pub include_path: *mut ::std::os::raw::c_char, + pub open_basedir: *mut ::std::os::raw::c_char, + pub open_basedir_modified: bool, + pub extension_dir: *mut ::std::os::raw::c_char, + pub php_binary: *mut ::std::os::raw::c_char, + pub sys_temp_dir: *mut ::std::os::raw::c_char, + pub upload_tmp_dir: *mut ::std::os::raw::c_char, + pub upload_max_filesize: zend_long, + pub error_append_string: *mut ::std::os::raw::c_char, + pub error_prepend_string: *mut ::std::os::raw::c_char, + pub auto_prepend_file: *mut ::std::os::raw::c_char, + pub auto_append_file: *mut ::std::os::raw::c_char, + pub input_encoding: *mut ::std::os::raw::c_char, + pub internal_encoding: *mut ::std::os::raw::c_char, + pub output_encoding: *mut ::std::os::raw::c_char, + pub arg_separator: arg_separators, + pub variables_order: *mut ::std::os::raw::c_char, + pub rfc1867_protected_variables: HashTable, + pub connection_status: ::std::os::raw::c_short, + pub ignore_user_abort: bool, + pub header_is_being_sent: ::std::os::raw::c_uchar, + pub tick_functions: zend_llist, + pub http_globals: [zval; 6usize], + pub expose_php: bool, + pub register_argc_argv: bool, + pub auto_globals_jit: bool, + pub html_errors: bool, + pub xmlrpc_errors: bool, + pub docref_root: *mut ::std::os::raw::c_char, + pub docref_ext: *mut ::std::os::raw::c_char, + pub xmlrpc_error_number: zend_long, + pub activated_auto_globals: [bool; 8usize], + pub modules_activated: bool, + pub file_uploads: bool, + pub during_request_startup: bool, + pub allow_url_fopen: bool, + pub enable_post_data_reading: bool, + pub report_zend_debug: bool, + pub last_error_type: ::std::os::raw::c_int, + pub last_error_lineno: ::std::os::raw::c_int, + pub last_error_message: *mut zend_string, + pub last_error_file: *mut zend_string, + pub php_sys_temp_dir: *mut ::std::os::raw::c_char, + pub disable_classes: *mut ::std::os::raw::c_char, + pub max_input_nesting_level: zend_long, + pub max_input_vars: zend_long, + pub user_ini_filename: *mut ::std::os::raw::c_char, + pub user_ini_cache_ttl: zend_long, + pub request_order: *mut ::std::os::raw::c_char, + pub mail_log: *mut ::std::os::raw::c_char, + pub mail_x_header: bool, + pub mail_mixed_lf_and_crlf: bool, + pub in_error_log: bool, + pub allow_url_include: bool, + pub in_user_include: bool, + pub have_called_openlog: bool, + pub syslog_facility: zend_long, + pub syslog_ident: *mut ::std::os::raw::c_char, + pub syslog_filter: zend_long, + pub error_log_mode: zend_long, +} +extern "C" { + pub static mut core_globals: _php_core_globals; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _arg_separators { + pub output: *mut ::std::os::raw::c_char, + pub input: *mut ::std::os::raw::c_char, +} +pub type arg_separators = _arg_separators; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct _zend_ini_entry_def { @@ -2201,6 +2345,37 @@ extern "C" { extern "C" { pub fn php_info_print_table_end(); } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct hostent { + pub h_name: *mut ::std::os::raw::c_char, + pub h_aliases: *mut *mut ::std::os::raw::c_char, + pub h_addrtype: ::std::os::raw::c_int, + pub h_length: ::std::os::raw::c_int, + pub h_addr_list: *mut *mut ::std::os::raw::c_char, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct php_file_globals { + pub pclose_ret: ::std::os::raw::c_int, + pub def_chunk_size: usize, + pub auto_detect_line_endings: bool, + pub default_socket_timeout: zend_long, + pub user_agent: *mut ::std::os::raw::c_char, + pub from_address: *mut ::std::os::raw::c_char, + pub user_stream_current_filename: *const ::std::os::raw::c_char, + pub default_context: *mut php_stream_context, + pub stream_wrappers: *mut HashTable, + pub stream_filters: *mut HashTable, + pub wrapper_errors: *mut HashTable, + pub pclose_wait: ::std::os::raw::c_int, + pub tmp_host_info: hostent, + pub tmp_host_buf: *mut ::std::os::raw::c_char, + pub tmp_host_buf_len: usize, +} +extern "C" { + pub static mut file_globals: php_file_globals; +} extern "C" { pub static mut zend_ce_throwable: *mut zend_class_entry; } diff --git a/src/zend/ex.rs b/src/zend/ex.rs index 1658e7febd..cb389cb2e7 100644 --- a/src/zend/ex.rs +++ b/src/zend/ex.rs @@ -1,4 +1,4 @@ -use crate::ffi::{_zend_function, zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}; +use crate::ffi::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}; use crate::{ args::ArgParser, From 28b190afa6c64ef0ca77365e8adf5fc2907e3faa Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 14:16:40 +0100 Subject: [PATCH 088/100] Cleanup --- allowed_bindings.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 62e7ca95ce..b6f2065398 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -245,7 +245,6 @@ bind! { zend_class_serialize_deny, zend_class_unserialize_deny, zend_executor_globals, - sapi_globals_struct, sapi_module_struct, zend_objects_store_del, zend_hash_move_forward_ex, @@ -261,7 +260,6 @@ bind! { core_globals, sapi_globals_struct, sapi_globals, - sapi_globals, sapi_module, php_printf, __zend_malloc, From 75eb6d751c4a9fa610deeea80876903cbb358a3c Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 24 Nov 2023 14:23:49 +0100 Subject: [PATCH 089/100] fmt --- src/zend/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zend/mod.rs b/src/zend/mod.rs index a2308a4931..357f2934aa 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -10,8 +10,8 @@ mod handlers; mod ini_entry_def; mod linked_list; mod module; -mod try_catch; mod streams; +mod try_catch; use crate::{ error::Result, @@ -33,10 +33,10 @@ pub use handlers::ZendObjectHandlers; pub use ini_entry_def::IniEntryDef; pub use linked_list::ZendLinkedList; pub use module::ModuleEntry; +pub use streams::*; #[cfg(feature = "embed")] pub(crate) use try_catch::panic_wrapper; pub use try_catch::{bailout, try_catch}; -pub use streams::*; // Used as the format string for `php_printf`. const FORMAT_STR: &[u8] = b"%s\0"; From 2c5bd2a5f827188fa7982f2036d5fcc46382db92 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 14:26:46 +0100 Subject: [PATCH 090/100] Cleanup --- src/error.rs | 10 ++++++++++ src/zend/streams.rs | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/error.rs b/src/error.rs index 5c4c9ceeda..52e2222e13 100644 --- a/src/error.rs +++ b/src/error.rs @@ -65,6 +65,10 @@ pub enum Error { IntegerOverflow, /// An exception was thrown in a function. Exception(ZBox), + /// A failure occurred while registering the stream wrapper + StreamWrapperRegistrationFailure, + /// A failure occurred while unregistering the stream wrapper + StreamWrapperUnregistrationFailure, } impl Display for Error { @@ -99,6 +103,12 @@ impl Display for Error { write!(f, "Converting integer arguments resulted in an overflow.") } Error::Exception(e) => write!(f, "Exception was thrown: {e:?}"), + Error::StreamWrapperRegistrationFailure => { + write!(f, "A failure occurred while registering the stream wrapper") + }, + Error::StreamWrapperUnregistrationFailure => { + write!(f, "A failure occurred while unregistering the stream wrapper") + } } } } diff --git a/src/zend/streams.rs b/src/zend/streams.rs index 1045a0c03c..4e3e3a0623 100644 --- a/src/zend/streams.rs +++ b/src/zend/streams.rs @@ -7,7 +7,7 @@ use crate::{ php_stream_wrapper_ops, php_unregister_url_stream_wrapper, php_unregister_url_stream_wrapper_volatile, zend_string, }, - types::ZendStr, + types::ZendStr, error::Error, }; pub type StreamWrapper = php_stream_wrapper; @@ -41,20 +41,20 @@ impl StreamWrapper { } } - pub fn register(self, name: &str) -> Result { + pub fn register(self, name: &str) -> Result { // We have to convert it to a static so owned streamwrapper doesn't get dropped. let copy = Box::new(self); let copy = Box::leak(copy); - let name = std::ffi::CString::new(name).unwrap(); + let name = std::ffi::CString::new(name).expect("Could not create C string for name!"); let result = unsafe { php_register_url_stream_wrapper(name.as_ptr(), copy) }; if result == 0 { Ok(*copy) } else { - Err(()) + Err(Error::StreamWrapperRegistrationFailure) } } - pub fn register_volatile(self, name: &str) -> Result { + pub fn register_volatile(self, name: &str) -> Result { // We have to convert it to a static so owned streamwrapper doesn't get dropped. let copy = Box::new(self); let copy = Box::leak(copy); @@ -64,23 +64,23 @@ impl StreamWrapper { if result == 0 { Ok(*copy) } else { - Err(()) + Err(Error::StreamWrapperRegistrationFailure) } } - pub fn unregister(name: &str) -> Result<(), ()> { - let name = std::ffi::CString::new(name).unwrap(); + pub fn unregister(name: &str) -> Result<(), Error> { + let name = std::ffi::CString::new(name).expect("Could not create C string for name!"); match unsafe { php_unregister_url_stream_wrapper(name.as_ptr()) } { 0 => Ok(()), - _ => Err(()), + _ => Err(Error::StreamWrapperUnregistrationFailure), } } - pub fn unregister_volatile(name: &str) -> Result<(), ()> { + pub fn unregister_volatile(name: &str) -> Result<(), Error> { let name = ZendStr::new(name, false); match unsafe { php_unregister_url_stream_wrapper_volatile((*name).as_ptr() as _) } { 0 => Ok(()), - _ => Err(()), + _ => Err(Error::StreamWrapperUnregistrationFailure), } } From 961b948538b8646d8031f30af912539af7bc202c Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 14:30:34 +0100 Subject: [PATCH 091/100] Fmt --- src/error.rs | 7 +++++-- src/zend/streams.rs | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index 52e2222e13..071e997584 100644 --- a/src/error.rs +++ b/src/error.rs @@ -105,9 +105,12 @@ impl Display for Error { Error::Exception(e) => write!(f, "Exception was thrown: {e:?}"), Error::StreamWrapperRegistrationFailure => { write!(f, "A failure occurred while registering the stream wrapper") - }, + } Error::StreamWrapperUnregistrationFailure => { - write!(f, "A failure occurred while unregistering the stream wrapper") + write!( + f, + "A failure occurred while unregistering the stream wrapper" + ) } } } diff --git a/src/zend/streams.rs b/src/zend/streams.rs index 4e3e3a0623..8ea5da2287 100644 --- a/src/zend/streams.rs +++ b/src/zend/streams.rs @@ -1,13 +1,14 @@ use std::ptr::{self, NonNull}; use crate::{ + error::Error, ffi::{ php_register_url_stream_wrapper, php_register_url_stream_wrapper_volatile, php_stream, php_stream_context, php_stream_locate_url_wrapper, php_stream_wrapper, php_stream_wrapper_ops, php_unregister_url_stream_wrapper, php_unregister_url_stream_wrapper_volatile, zend_string, }, - types::ZendStr, error::Error, + types::ZendStr, }; pub type StreamWrapper = php_stream_wrapper; From 678073a533bb19f0e87c5fc1bc84cb31c692256f Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 14:43:05 +0100 Subject: [PATCH 092/100] Cleanup --- guide/src/types/iterator.md | 2 ++ src/flags.rs | 9 ++++----- src/types/iterable.rs | 4 ++-- src/types/iterator.rs | 31 ++++++++++++++++--------------- src/types/zval.rs | 6 ++++-- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/guide/src/types/iterator.md b/guide/src/types/iterator.md index af7d09f194..b113e41478 100644 --- a/guide/src/types/iterator.md +++ b/guide/src/types/iterator.md @@ -11,6 +11,8 @@ the variable. This means that any value, at the exception of an `array`, that ca a `foreach` loop can be converted into a `ZendIterator`. As an example, a `Generator` can be used but also a the result of a `query` call with `PDO`. +If you want a more universal `iterable` type that also supports arrays, see [Iterable](./iterable.md). + ## Rust example ```rust,no_run diff --git a/src/flags.rs b/src/flags.rs index 6857a76192..c91fc1965a 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -8,11 +8,10 @@ use crate::ffi::{ CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, E_COMPILE_ERROR, E_COMPILE_WARNING, E_CORE_ERROR, E_CORE_WARNING, E_DEPRECATED, E_ERROR, E_NOTICE, E_PARSE, E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING, - E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_INDIRECT, IS_LONG, - IS_ITERABLE, - IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, - IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, PHP_INI_PERDIR, - PHP_INI_SYSTEM, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, + E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_INDIRECT, + IS_ITERABLE, IS_LONG, IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, + IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, + PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, diff --git a/src/types/iterable.rs b/src/types/iterable.rs index dd5c13efe7..fb266c155d 100644 --- a/src/types/iterable.rs +++ b/src/types/iterable.rs @@ -5,8 +5,8 @@ use crate::flags::DataType; use crate::types::iterator::IterKey; use crate::types::{ZendHashTable, ZendIterator, Zval}; -/// This type represents a PHP iterable, which can be either an array or an object implementing -/// the Traversable interface. +/// This type represents a PHP iterable, which can be either an array or an +/// object implementing the Traversable interface. #[derive(Debug)] pub enum Iterable<'a> { Array(&'a ZendHashTable), diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 1dbfa0b054..7d42625b24 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -7,8 +7,8 @@ use std::fmt::{Debug, Display, Formatter}; /// A PHP Iterator. /// -/// In PHP, iterators are represented as zend_object_iterator. This allow user to iterate -/// over object implementing Traversable interface using foreach. +/// In PHP, iterators are represented as zend_object_iterator. This allow user +/// to iterate over object implementing Traversable interface using foreach. pub type ZendIterator = zend_object_iterator; impl ZendIterator { @@ -29,8 +29,8 @@ impl ZendIterator { /// Check if the current position of the iterator is valid. /// - /// As an example this will call the user defined valid method of the ['\Iterator'] interface. - /// see + /// As an example this will call the user defined valid method of the + /// ['\Iterator'] interface. see pub fn valid(&mut self) -> bool { if let Some(valid) = unsafe { (*self.funcs).valid } { let valid = unsafe { valid(&mut *self) == ZEND_RESULT_CODE_SUCCESS }; @@ -47,13 +47,13 @@ impl ZendIterator { /// Rewind the iterator to the first element. /// - /// As an example this will call the user defined rewind method of the ['\Iterator'] interface. - /// see + /// As an example this will call the user defined rewind method of the + /// ['\Iterator'] interface. see /// /// # Returns /// - /// Returns true if the iterator was successfully rewind, false otherwise. (when there is - /// an exception during rewind) + /// Returns true if the iterator was successfully rewind, false otherwise. + /// (when there is an exception during rewind) pub fn rewind(&mut self) -> bool { if let Some(rewind) = unsafe { (*self.funcs).rewind } { unsafe { @@ -66,13 +66,13 @@ impl ZendIterator { /// Move the iterator forward to the next element. /// - /// As an example this will call the user defined next method of the ['\Iterator'] interface. - /// see + /// As an example this will call the user defined next method of the + /// ['\Iterator'] interface. see /// /// # Returns /// - /// Returns true if the iterator was successfully move, false otherwise. (when there is - /// an exception during next) + /// Returns true if the iterator was successfully move, false otherwise. + /// (when there is an exception during next) pub fn move_forward(&mut self) -> bool { if let Some(move_forward) = unsafe { (*self.funcs).move_forward } { unsafe { @@ -104,8 +104,8 @@ impl ZendIterator { /// /// # Returns /// - /// Returns a new ['Zval'] containing the current key of the iterator if available - /// , ['None'] otherwise. + /// Returns a new ['Zval'] containing the current key of the iterator if + /// available , ['None'] otherwise. pub fn get_current_key(&mut self) -> Option { let get_current_key = unsafe { (*self.funcs).get_current_key? }; let mut key = Zval::new(); @@ -178,7 +178,8 @@ impl<'a> Iterator for Iter<'a> { type Item = (IterKey, &'a Zval); fn next(&mut self) -> Option { - // Call next when index > 0, so next is really called at the start of each iteration, which allow to work better with generator iterator + // Call next when index > 0, so next is really called at the start of each + // iteration, which allow to work better with generator iterator if self.zi.index > 0 && !self.zi.move_forward() { return None; } diff --git a/src/types/zval.rs b/src/types/zval.rs index f9a3e7b924..f031214f32 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -267,7 +267,8 @@ impl Zval { } } - /// Returns an iterable over the zval if it is an array or traversable. (is iterable) + /// Returns an iterable over the zval if it is an array or traversable. (is + /// iterable) pub fn iterable(&self) -> Option { if self.is_iterable() { Iterable::from_zval(self) @@ -399,7 +400,8 @@ impl Zval { } } - /// Returns true if the zval is iterable (array or traversable), false otherwise. + /// Returns true if the zval is iterable (array or traversable), false + /// otherwise. pub fn is_iterable(&self) -> bool { let ptr: *const Self = self; unsafe { zend_is_iterable(ptr as *mut Self) } From 7614f3a4c2a8489435e82d151e626846b7b14a7a Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 14:45:20 +0100 Subject: [PATCH 093/100] Rename --- src/types/array.rs | 2 +- src/types/iterator.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/array.rs b/src/types/array.rs index 29e5c6efd8..958bc23160 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -464,7 +464,7 @@ impl ZendHashTable { /// assert!(!ht.has_numerical_keys()); /// ``` pub fn has_numerical_keys(&self) -> bool { - !self.iter().any(|(k, _)| !k.is_numerical()) + !self.iter().any(|(k, _)| !k.is_long()) } /// Checks if the hashtable has numerical, sequential keys. diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 7d42625b24..c58b849df3 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -136,12 +136,12 @@ pub enum IterKey { /// Represent the key of a PHP iterator, which can be either a long or a string. impl IterKey { - /// Check if the key is numerical. + /// Check if the key is an integer. /// /// # Returns /// - /// Returns true if the key is numerical, false otherwise. - pub fn is_numerical(&self) -> bool { + /// Returns true if the key is an integer, false otherwise. + pub fn is_long(&self) -> bool { match self { IterKey::Long(_) => true, IterKey::String(_) => false, @@ -289,14 +289,14 @@ mod tests { let (key, value) = iter.next().unwrap(); - assert!(!key.is_numerical()); + assert!(!key.is_long()); assert_eq!(key, IterKey::String("key".to_string())); assert!(value.is_string()); assert_eq!(value.string().unwrap(), "foo"); let (key, value) = iter.next().unwrap(); - assert!(key.is_numerical()); + assert!(key.is_long()); assert_eq!(key, IterKey::Long(10)); assert!(value.is_string()); assert_eq!(value.string().unwrap(), "bar"); From 3418b045beeb0b06c0a9909055aa72c13679d4ef Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 15:38:01 +0100 Subject: [PATCH 094/100] Refactoring --- docsrs_bindings.rs | 2 +- src/types/array.rs | 134 ++++++++++++++++++++++++++++++++---------- src/types/iterable.rs | 21 +++++-- src/types/iterator.rs | 61 +++++-------------- 4 files changed, 135 insertions(+), 83 deletions(-) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index ede47f1b6e..c5cdd8061e 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -1657,7 +1657,7 @@ extern "C" { ); } extern "C" { - pub fn zend_is_iterable(iterable: *mut zval) -> bool; + pub fn zend_is_iterable(iterable: *const zval) -> bool; } pub const _zend_expected_type_Z_EXPECTED_LONG: _zend_expected_type = 0; pub const _zend_expected_type_Z_EXPECTED_LONG_OR_NULL: _zend_expected_type = 1; diff --git a/src/types/array.rs b/src/types/array.rs index 958bc23160..b8f14a9a20 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -5,12 +5,11 @@ use std::{ collections::HashMap, convert::{TryFrom, TryInto}, ffi::CString, - fmt::Debug, + fmt::{Debug, Display}, iter::FromIterator, u64, }; -use crate::types::iterator::IterKey; use crate::{ boxed::{ZBox, ZBoxable}, convert::{FromZval, IntoZval}, @@ -464,7 +463,7 @@ impl ZendHashTable { /// assert!(!ht.has_numerical_keys()); /// ``` pub fn has_numerical_keys(&self) -> bool { - !self.iter().any(|(k, _)| !k.is_long()) + !self.into_iter().any(|(k, _)| !k.is_long()) } /// Checks if the hashtable has numerical, sequential keys. @@ -491,13 +490,13 @@ impl ZendHashTable { /// ``` pub fn has_sequential_keys(&self) -> bool { !self - .iter() + .into_iter() .enumerate() - .any(|(i, (k, _))| IterKey::Long(i as u64) != k) + .any(|(i, (k, _))| ArrayKey::Long(i as i64) != k) } - /// Returns an iterator over the key(s) and value contained inside the - /// hashtable. + /// Returns an iterator over the values contained inside the hashtable, as + /// if it was a set or list. /// /// # Example /// @@ -506,20 +505,16 @@ impl ZendHashTable { /// /// let mut ht = ZendHashTable::new(); /// - /// for (key, val) in ht.iter() { - /// // ^ Index if inserted at an index. - /// // ^ Optional string key, if inserted like a hashtable. - /// // ^ Inserted value. - /// - /// dbg!(key, val); + /// for val in ht.values() { + /// dbg!(val); /// } #[inline] - pub fn iter(&self) -> Iter { - Iter::new(self) + pub fn values(&self) -> Values { + Values::new(self) } - /// Returns an iterator over the values contained inside the hashtable, as - /// if it was a set or list. + /// Returns an iterator over the key(s) and value contained inside the + /// hashtable. /// /// # Example /// @@ -528,12 +523,16 @@ impl ZendHashTable { /// /// let mut ht = ZendHashTable::new(); /// - /// for val in ht.values() { - /// dbg!(val); + /// for (key, val) in ht { + /// // ^ Index if inserted at an index. + /// // ^ Optional string key, if inserted like a hashtable. + /// // ^ Inserted value. + /// + /// dbg!(key, val); /// } #[inline] - pub fn values(&self) -> Values { - Values::new(self) + pub fn iter(&self) -> Iter { + self.into_iter() } } @@ -547,7 +546,7 @@ unsafe impl ZBoxable for ZendHashTable { impl Debug for ZendHashTable { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_map() - .entries(self.iter().map(|(k, v)| (k.to_string(), v))) + .entries(self.into_iter().map(|(k, v)| (k.to_string(), v))) .finish() } } @@ -572,10 +571,54 @@ impl ToOwned for ZendHashTable { /// Immutable iterator upon a reference to a hashtable. pub struct Iter<'a> { ht: &'a ZendHashTable, - current_num: u64, + current_num: i64, pos: HashPosition, } +#[derive(Debug, PartialEq)] +pub enum ArrayKey<'a> { + Long(i64), + String(&'a str), +} + +/// Represent the key of a PHP array, which can be either a long or a string. +impl<'a> ArrayKey<'a> { + /// Check if the key is an integer. + /// + /// # Returns + /// + /// Returns true if the key is an integer, false otherwise. + pub fn is_long(&self) -> bool { + match self { + ArrayKey::Long(_) => true, + ArrayKey::String(_) => false, + } + } +} + +impl<'a> Display for ArrayKey<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ArrayKey::Long(key) => write!(f, "{}", key), + ArrayKey::String(key) => write!(f, "{}", key), + } + } +} + +impl<'a> FromZval<'_> for ArrayKey<'a> { + const TYPE: DataType = DataType::String; + + fn from_zval(zval: &Zval) -> Option { + if let Some(key) = zval.long() { + return Some(ArrayKey::Long(key)); + } + if let Some(key) = zval.str() { + return Some(ArrayKey::String(key)); + } + return None; + } +} + impl<'a> Iter<'a> { /// Creates a new iterator over a hashtable. /// @@ -591,8 +634,35 @@ impl<'a> Iter<'a> { } } +impl<'a> IntoIterator for &'a ZendHashTable { + type Item = (ArrayKey<'a>, &'a Zval); + type IntoIter = Iter<'a>; + + /// Returns an iterator over the key(s) and value contained inside the + /// hashtable. + /// + /// # Example + /// + /// ```no_run + /// use ext_php_rs::types::ZendHashTable; + /// + /// let mut ht = ZendHashTable::new(); + /// + /// for (key, val) in ht { + /// // ^ Index if inserted at an index. + /// // ^ Optional string key, if inserted like a hashtable. + /// // ^ Inserted value. + /// + /// dbg!(key, val); + /// } + #[inline] + fn into_iter(self) -> Self::IntoIter { + Iter::new(self) + } +} + impl<'a> Iterator for Iter<'a> { - type Item = (IterKey, &'a Zval); + type Item = (ArrayKey<'a>, &'a Zval); fn next(&mut self) -> Option { let key_type = unsafe { @@ -621,9 +691,9 @@ impl<'a> Iterator for Iter<'a> { ) }; - let r = match IterKey::from_zval(&key) { - Some(key) => (key, value), - None => (IterKey::Long(self.current_num), value), + let key = match ArrayKey::from_zval(&key) { + Some(key) => key, + None => ArrayKey::Long(self.current_num), }; unsafe { @@ -634,7 +704,7 @@ impl<'a> Iterator for Iter<'a> { }; self.current_num += 1; - Some(r) + Some((key, value)) } fn count(self) -> usize @@ -679,9 +749,9 @@ impl<'a> DoubleEndedIterator for Iter<'a> { ) }; - let r = match IterKey::from_zval(&key) { + let r = match ArrayKey::from_zval(&key) { Some(key) => (key, value), - None => (IterKey::Long(self.current_num), value), + None => (ArrayKey::Long(self.current_num), value), }; unsafe { @@ -780,7 +850,7 @@ where fn try_from(value: &'a ZendHashTable) -> Result { let mut hm = HashMap::with_capacity(value.len()); - for (key, val) in value.iter() { + for (key, val) in value { hm.insert( key.to_string(), V::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?, @@ -849,7 +919,7 @@ where fn try_from(value: &'a ZendHashTable) -> Result { let mut vec = Vec::with_capacity(value.len()); - for (_, val) in value.iter() { + for (_, val) in value { vec.push(T::from_zval(val).ok_or_else(|| Error::ZvalConversion(val.get_type()))?); } diff --git a/src/types/iterable.rs b/src/types/iterable.rs index fb266c155d..32eca2b6a4 100644 --- a/src/types/iterable.rs +++ b/src/types/iterable.rs @@ -1,8 +1,7 @@ -use super::array::Iter as ZendHashTableIter; +use super::array::{ArrayKey, Iter as ZendHashTableIter}; use super::iterator::Iter as ZendIteratorIter; use crate::convert::FromZval; use crate::flags::DataType; -use crate::types::iterator::IterKey; use crate::types::{ZendHashTable, ZendIterator, Zval}; /// This type represents a PHP iterable, which can be either an array or an @@ -15,6 +14,7 @@ pub enum Iterable<'a> { impl<'a> Iterable<'a> { /// Creates a new rust iterator from a PHP iterable. + /// May return None if a Traversable cannot be rewound. pub fn iter(&mut self) -> Option { match self { Iterable::Array(array) => Some(Iter::Array(array.iter())), @@ -23,6 +23,15 @@ impl<'a> Iterable<'a> { } } +impl<'a> IntoIterator for &'a mut Iterable<'a> { + type Item = (&'a Zval, &'a Zval); + type IntoIter = Iter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter().expect("Could not rewind iterator!") + } +} + impl<'a> FromZval<'a> for Iterable<'a> { const TYPE: DataType = DataType::Iterable; @@ -46,11 +55,15 @@ pub enum Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = (IterKey, &'a Zval); + type Item = (&'a Zval, &'a Zval); fn next(&mut self) -> Option { match self { - Iter::Array(array) => array.next(), + Iter::Array(array) => match array.next() { + Some((ArrayKey::Long(k), v)) => Zval::new().set_long(v), + Some((ArrayKey::String(k), v)) => Zval::new().set_string(v), + None => None, + }, Iter::Traversable(traversable) => traversable.next(), } } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index c58b849df3..83e39b9d6f 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -7,16 +7,17 @@ use std::fmt::{Debug, Display, Formatter}; /// A PHP Iterator. /// -/// In PHP, iterators are represented as zend_object_iterator. This allow user -/// to iterate over object implementing Traversable interface using foreach. +/// In PHP, iterators are represented as zend_object_iterator. This allows user +/// to iterate over objects implementing Traversable interface using foreach. +/// +/// Use ZendIterable to iterate over both iterators and arrays. pub type ZendIterator = zend_object_iterator; impl ZendIterator { /// Creates a new rust iterator from a zend_object_iterator. /// - /// # Returns - /// - /// Returns a iterator over the zend_object_iterator. + /// Returns a iterator over the zend_object_iterator, or None if the + /// iterator cannot be rewound. pub fn iter(&mut self) -> Option { self.index = 0; @@ -122,50 +123,18 @@ impl ZendIterator { } } -impl Debug for ZendIterator { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ZendIterator").finish() - } -} - -#[derive(Debug, PartialEq)] -pub enum IterKey { - Long(u64), - String(String), -} +impl<'a> IntoIterator for &'a mut ZendIterator { + type Item = (&'a Zval, &'a Zval); + type IntoIter = Iter<'a>; -/// Represent the key of a PHP iterator, which can be either a long or a string. -impl IterKey { - /// Check if the key is an integer. - /// - /// # Returns - /// - /// Returns true if the key is an integer, false otherwise. - pub fn is_long(&self) -> bool { - match self { - IterKey::Long(_) => true, - IterKey::String(_) => false, - } - } -} - -impl Display for IterKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - IterKey::Long(key) => write!(f, "{}", key), - IterKey::String(key) => write!(f, "{}", key), - } + fn into_iter(self) -> Self::IntoIter { + self.iter().expect("Could not rewind iterator!") } } -impl FromZval<'_> for IterKey { - const TYPE: DataType = DataType::String; - - fn from_zval(zval: &Zval) -> Option { - match zval.long() { - Some(key) => Some(IterKey::Long(key as u64)), - None => zval.string().map(IterKey::String), - } +impl Debug for ZendIterator { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ZendIterator").finish() } } @@ -175,7 +144,7 @@ pub struct Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = (IterKey, &'a Zval); + type Item = (&'a Zval, &'a Zval); fn next(&mut self) -> Option { // Call next when index > 0, so next is really called at the start of each From 91dcc38e50891725d5be6ad7c650a3e21a2f1389 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 16:50:58 +0100 Subject: [PATCH 095/100] Refactor --- src/types/array.rs | 77 +++++++++++++++++++++++-------------------- src/types/iterable.rs | 12 +++---- src/types/iterator.rs | 19 ++++++----- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/src/types/array.rs b/src/types/array.rs index b8f14a9a20..4c5bc72f4a 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -576,13 +576,13 @@ pub struct Iter<'a> { } #[derive(Debug, PartialEq)] -pub enum ArrayKey<'a> { +pub enum ArrayKey { Long(i64), - String(&'a str), + String(String), } /// Represent the key of a PHP array, which can be either a long or a string. -impl<'a> ArrayKey<'a> { +impl ArrayKey { /// Check if the key is an integer. /// /// # Returns @@ -596,7 +596,7 @@ impl<'a> ArrayKey<'a> { } } -impl<'a> Display for ArrayKey<'a> { +impl Display for ArrayKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ArrayKey::Long(key) => write!(f, "{}", key), @@ -605,17 +605,17 @@ impl<'a> Display for ArrayKey<'a> { } } -impl<'a> FromZval<'_> for ArrayKey<'a> { +impl<'a> FromZval<'a> for ArrayKey { const TYPE: DataType = DataType::String; - fn from_zval(zval: &Zval) -> Option { + fn from_zval(zval: &'a Zval) -> Option { if let Some(key) = zval.long() { return Some(ArrayKey::Long(key)); } - if let Some(key) = zval.str() { + if let Some(key) = zval.string() { return Some(ArrayKey::String(key)); } - return None; + None } } @@ -635,7 +635,7 @@ impl<'a> Iter<'a> { } impl<'a> IntoIterator for &'a ZendHashTable { - type Item = (ArrayKey<'a>, &'a Zval); + type Item = (ArrayKey, &'a Zval); type IntoIter = Iter<'a>; /// Returns an iterator over the key(s) and value contained inside the @@ -662,9 +662,28 @@ impl<'a> IntoIterator for &'a ZendHashTable { } impl<'a> Iterator for Iter<'a> { - type Item = (ArrayKey<'a>, &'a Zval); + type Item = (ArrayKey, &'a Zval); fn next(&mut self) -> Option { + self.next_zval().map(|(k, v)| (ArrayKey::from_zval(&k).expect("Invalid array key!"), v)) + } + + fn count(self) -> usize + where + Self: Sized, + { + self.ht.len() + } +} + +impl<'a> ExactSizeIterator for Iter<'a> { + fn len(&self) -> usize { + self.ht.len() + } +} + +impl<'a> DoubleEndedIterator for Iter<'a> { + fn next_back(&mut self) -> Option { let key_type = unsafe { zend_hash_get_current_key_type_ex( self.ht as *const ZendHashTable as *mut ZendHashTable, @@ -677,6 +696,7 @@ impl<'a> Iterator for Iter<'a> { } let key = Zval::new(); + unsafe { zend_hash_get_current_key_zval_ex( self.ht as *const ZendHashTable as *mut ZendHashTable, @@ -697,32 +717,19 @@ impl<'a> Iterator for Iter<'a> { }; unsafe { - zend_hash_move_forward_ex( + zend_hash_move_backwards_ex( self.ht as *const ZendHashTable as *mut ZendHashTable, &mut self.pos as *mut HashPosition, ) }; - self.current_num += 1; + self.current_num -= 1; Some((key, value)) } - - fn count(self) -> usize - where - Self: Sized, - { - self.ht.len() - } -} - -impl<'a> ExactSizeIterator for Iter<'a> { - fn len(&self) -> usize { - self.ht.len() - } } -impl<'a> DoubleEndedIterator for Iter<'a> { - fn next_back(&mut self) -> Option { +impl<'a, 'b> Iter<'a> { + pub fn next_zval(&'b mut self) -> Option<(Zval, &'a Zval)> { let key_type = unsafe { zend_hash_get_current_key_type_ex( self.ht as *const ZendHashTable as *mut ZendHashTable, @@ -734,7 +741,8 @@ impl<'a> DoubleEndedIterator for Iter<'a> { return None; } - let key = Zval::new(); + let mut key = Zval::new(); + unsafe { zend_hash_get_current_key_zval_ex( self.ht as *const ZendHashTable as *mut ZendHashTable, @@ -749,20 +757,19 @@ impl<'a> DoubleEndedIterator for Iter<'a> { ) }; - let r = match ArrayKey::from_zval(&key) { - Some(key) => (key, value), - None => (ArrayKey::Long(self.current_num), value), - }; + if !key.is_long() && !key.is_string() { + key.set_long(self.current_num) + } unsafe { - zend_hash_move_backwards_ex( + zend_hash_move_forward_ex( self.ht as *const ZendHashTable as *mut ZendHashTable, &mut self.pos as *mut HashPosition, ) }; - self.current_num -= 1; + self.current_num += 1; - Some(r) + Some((key, value)) } } diff --git a/src/types/iterable.rs b/src/types/iterable.rs index 32eca2b6a4..1440a679d9 100644 --- a/src/types/iterable.rs +++ b/src/types/iterable.rs @@ -1,4 +1,4 @@ -use super::array::{ArrayKey, Iter as ZendHashTableIter}; +use super::array::Iter as ZendHashTableIter; use super::iterator::Iter as ZendIteratorIter; use crate::convert::FromZval; use crate::flags::DataType; @@ -24,7 +24,7 @@ impl<'a> Iterable<'a> { } impl<'a> IntoIterator for &'a mut Iterable<'a> { - type Item = (&'a Zval, &'a Zval); + type Item = (Zval, &'a Zval); type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { @@ -55,15 +55,11 @@ pub enum Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = (&'a Zval, &'a Zval); + type Item = (Zval, &'a Zval); fn next(&mut self) -> Option { match self { - Iter::Array(array) => match array.next() { - Some((ArrayKey::Long(k), v)) => Zval::new().set_long(v), - Some((ArrayKey::String(k), v)) => Zval::new().set_string(v), - None => None, - }, + Iter::Array(array) => array.next_zval(), Iter::Traversable(traversable) => traversable.next(), } } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 83e39b9d6f..406194cfdd 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -1,9 +1,9 @@ -use crate::convert::{FromZval, FromZvalMut}; +use crate::convert::FromZvalMut; use crate::ffi::{zend_object_iterator, ZEND_RESULT_CODE_SUCCESS}; use crate::flags::DataType; use crate::types::Zval; use crate::zend::ExecutorGlobals; -use std::fmt::{Debug, Display, Formatter}; +use std::fmt::{Debug, Formatter}; /// A PHP Iterator. /// @@ -124,7 +124,7 @@ impl ZendIterator { } impl<'a> IntoIterator for &'a mut ZendIterator { - type Item = (&'a Zval, &'a Zval); + type Item = (Zval, &'a Zval); type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { @@ -144,7 +144,7 @@ pub struct Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = (&'a Zval, &'a Zval); + type Item = (Zval, &'a Zval); fn next(&mut self) -> Option { // Call next when index > 0, so next is really called at the start of each @@ -162,11 +162,12 @@ impl<'a> Iterator for Iter<'a> { let real_index = self.zi.index - 1; let key = match self.zi.get_current_key() { - None => IterKey::Long(real_index), - Some(key) => match IterKey::from_zval(&key) { - Some(key) => key, - None => IterKey::Long(real_index), - }, + None => { + let mut z = Zval::new(); + z.set_long(real_index as i64); + z + } + Some(key) => key, }; self.zi.get_current_data().map(|value| (key, value)) From e5d7a32a471073146ec6863956feebe585a2fa47 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 17:12:00 +0100 Subject: [PATCH 096/100] Fixup --- guide/src/types/iterable.md | 5 +++-- guide/src/types/iterator.md | 5 +++-- src/flags.rs | 2 +- src/types/array.rs | 4 ++-- src/types/iterator.test.php | 3 +++ 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/guide/src/types/iterable.md b/guide/src/types/iterable.md index 0e689c1d91..cda816d8bd 100644 --- a/guide/src/types/iterable.md +++ b/guide/src/types/iterable.md @@ -19,8 +19,8 @@ that implements the `Traversable` interface. This means that any value that can # use ext_php_rs::types::Iterable; #[php_function] pub fn test_iterable(mut iterable: Iterable) { - for (k, v) in iterable.iter().expect("cannot get iterable") { - println!("k: {} v: {}", k, v.string().unwrap()); + for (k, v) in iterable.iter().expect("cannot rewind iterator") { + println!("k: {:?} v: {}", k, v.string().unwrap()); } } # fn main() {} @@ -35,6 +35,7 @@ $generator = function() { yield 'hello' => 'world'; yield 'rust' => 'php'; yield 'okk'; + yield new class {} => new class {}; }; $array = [ diff --git a/guide/src/types/iterator.md b/guide/src/types/iterator.md index b113e41478..ce0e9cfba3 100644 --- a/guide/src/types/iterator.md +++ b/guide/src/types/iterator.md @@ -22,8 +22,8 @@ If you want a more universal `iterable` type that also supports arrays, see [Ite # use ext_php_rs::types::ZendIterator; #[php_function] pub fn test_iterator(iterator: &mut ZendIterator) { - for (k, v) in iterator.iter().expect("cannot get iterator") { - println!("k: {} v: {}", k, v.string().unwrap()); + for (k, v) in iterator.iter().expect("cannot rewind iterator") { + println!("k: {:?} v: {}", k, v.string().unwrap()); } } # fn main() {} @@ -38,6 +38,7 @@ $generator = function() { yield 'hello' => 'world'; yield 'rust' => 'php'; yield 'okk'; + yield new class {} => new class {}; }; test_iterator($generator()); diff --git a/src/flags.rs b/src/flags.rs index c91fc1965a..c78a26a2bd 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -395,7 +395,7 @@ impl Display for DataType { mod tests { use super::DataType; use crate::ffi::{ - IS_ARRAY, IS_ARRAY_EX, IS_CALLABLE, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, + IS_ARRAY, IS_ARRAY_EX, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, IS_FALSE, IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_PTR, IS_REFERENCE, IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, IS_STRING_EX, IS_TRUE, IS_UNDEF, IS_VOID, diff --git a/src/types/array.rs b/src/types/array.rs index 4c5bc72f4a..aa32c42157 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -523,7 +523,7 @@ impl ZendHashTable { /// /// let mut ht = ZendHashTable::new(); /// - /// for (key, val) in ht { + /// for (key, val) in ht.iter() { /// // ^ Index if inserted at an index. /// // ^ Optional string key, if inserted like a hashtable. /// // ^ Inserted value. @@ -648,7 +648,7 @@ impl<'a> IntoIterator for &'a ZendHashTable { /// /// let mut ht = ZendHashTable::new(); /// - /// for (key, val) in ht { + /// for (key, val) in ht.iter() { /// // ^ Index if inserted at an index. /// // ^ Optional string key, if inserted like a hashtable. /// // ^ Inserted value. diff --git a/src/types/iterator.test.php b/src/types/iterator.test.php index 216d663c70..e5463ecee5 100644 --- a/src/types/iterator.test.php +++ b/src/types/iterator.test.php @@ -4,6 +4,7 @@ function create_generator() { yield 1; yield 2; yield 3; + yield new class {}; } class TestIterator implements \Iterator { @@ -15,6 +16,7 @@ public function current() 0 => 'foo', 1 => 'bar', 2 => 'baz', + 3 => new class {}, default => null, }; } @@ -30,6 +32,7 @@ public function key() 0 => 'key', 1 => 10, 2 => 2, + 3 => new class {}, default => null, }; } From bc4405de8ba1dab8a5e676e75f275c46dd1ea607 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 18:30:39 +0100 Subject: [PATCH 097/100] Fixup bailout test --- src/embed/mod.rs | 2 +- src/flags.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/embed/mod.rs b/src/embed/mod.rs index d8f914ff78..9e9f2a8587 100644 --- a/src/embed/mod.rs +++ b/src/embed/mod.rs @@ -268,7 +268,7 @@ mod tests { #[test] fn test_eval_bailout() { Embed::run(|| { - let result = Embed::eval("str_repeat('a', 100_000_000_000_000);"); + let result = Embed::eval("trigger_error(\"Fatal error\", E_USER_ERROR);"); assert!(result.is_err()); assert!(result.unwrap_err().is_bailout()); diff --git a/src/flags.rs b/src/flags.rs index 9e814d2a26..5036c7e05d 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -391,10 +391,10 @@ impl Display for DataType { mod tests { use super::DataType; use crate::ffi::{ - IS_ARRAY, IS_ARRAY_EX, IS_CALLABLE, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, - IS_FALSE, IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, - IS_PTR, IS_REFERENCE, IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, - IS_STRING_EX, IS_TRUE, IS_UNDEF, IS_VOID, + IS_ARRAY, IS_ARRAY_EX, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, IS_FALSE, + IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_PTR, + IS_REFERENCE, IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, IS_STRING_EX, + IS_TRUE, IS_UNDEF, IS_VOID, }; use std::convert::TryFrom; From 050c017e261054d454acc2801b524f9a617c568c Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 18:39:59 +0100 Subject: [PATCH 098/100] Cleanup --- src/flags.rs | 8 ++++---- src/types/array.rs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/flags.rs b/src/flags.rs index c78a26a2bd..da3a6844c8 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -395,10 +395,10 @@ impl Display for DataType { mod tests { use super::DataType; use crate::ffi::{ - IS_ARRAY, IS_ARRAY_EX, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, - IS_FALSE, IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, - IS_PTR, IS_REFERENCE, IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, - IS_STRING_EX, IS_TRUE, IS_UNDEF, IS_VOID, + IS_ARRAY, IS_ARRAY_EX, IS_CONSTANT_AST, IS_CONSTANT_AST_EX, IS_DOUBLE, IS_FALSE, + IS_INDIRECT, IS_INTERNED_STRING_EX, IS_LONG, IS_NULL, IS_OBJECT, IS_OBJECT_EX, IS_PTR, + IS_REFERENCE, IS_REFERENCE_EX, IS_RESOURCE, IS_RESOURCE_EX, IS_STRING, IS_STRING_EX, + IS_TRUE, IS_UNDEF, IS_VOID, }; use std::convert::TryFrom; diff --git a/src/types/array.rs b/src/types/array.rs index aa32c42157..19768f3165 100644 --- a/src/types/array.rs +++ b/src/types/array.rs @@ -665,7 +665,8 @@ impl<'a> Iterator for Iter<'a> { type Item = (ArrayKey, &'a Zval); fn next(&mut self) -> Option { - self.next_zval().map(|(k, v)| (ArrayKey::from_zval(&k).expect("Invalid array key!"), v)) + self.next_zval() + .map(|(k, v)| (ArrayKey::from_zval(&k).expect("Invalid array key!"), v)) } fn count(self) -> usize From 0f401865f74e11652fb317cd3080a5ee12486eab Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 18:46:19 +0100 Subject: [PATCH 099/100] Cleanup --- src/types/iterator.rs | 36 +++++++++++++++++++++++++----------- src/types/iterator.test.php | 14 +++++++------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 406194cfdd..b6591ab348 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -186,7 +186,6 @@ impl<'a> FromZvalMut<'a> for &'a mut ZendIterator { #[cfg(feature = "embed")] mod tests { use crate::embed::Embed; - use crate::types::iterator::IterKey; #[test] fn test_generator() { @@ -212,22 +211,27 @@ mod tests { let (key, value) = iter.next().unwrap(); - assert_eq!(key, IterKey::Long(0)); + assert_eq!(key.long(), Some(0)); assert!(value.is_long()); assert_eq!(value.long().unwrap(), 1); let (key, value) = iter.next().unwrap(); - assert_eq!(key, IterKey::Long(1)); + assert_eq!(key.long(), Some(1)); assert!(value.is_long()); assert_eq!(value.long().unwrap(), 2); let (key, value) = iter.next().unwrap(); - assert_eq!(key, IterKey::Long(2)); + assert_eq!(key.long(), Some(2)); assert!(value.is_long()); assert_eq!(value.long().unwrap(), 3); + let (key, value) = iter.next().unwrap(); + + assert!(key.is_object()); + assert!(value.is_object()); + let next = iter.next(); assert!(next.is_none()); @@ -260,23 +264,28 @@ mod tests { let (key, value) = iter.next().unwrap(); assert!(!key.is_long()); - assert_eq!(key, IterKey::String("key".to_string())); + assert_eq!(key.str(), Some("key")); assert!(value.is_string()); - assert_eq!(value.string().unwrap(), "foo"); + assert_eq!(value.str(), Some("foo")); let (key, value) = iter.next().unwrap(); assert!(key.is_long()); - assert_eq!(key, IterKey::Long(10)); + assert_eq!(key.long(), Some(10)); assert!(value.is_string()); assert_eq!(value.string().unwrap(), "bar"); let (key, value) = iter.next().unwrap(); - assert_eq!(key, IterKey::Long(2)); + assert_eq!(key.long(), Some(2)); assert!(value.is_string()); assert_eq!(value.string().unwrap(), "baz"); + let (key, value) = iter.next().unwrap(); + + assert!(key.is_object()); + assert!(value.is_object()); + let next = iter.next(); assert!(next.is_none()); @@ -288,22 +297,27 @@ mod tests { let (key, value) = iter.next().unwrap(); - assert_eq!(key, IterKey::String("key".to_string())); + assert_eq!(key.str(), Some("key")); assert!(value.is_string()); assert_eq!(value.string().unwrap(), "foo"); let (key, value) = iter.next().unwrap(); - assert_eq!(key, IterKey::Long(10)); + assert_eq!(key.long(), Some(10)); assert!(value.is_string()); assert_eq!(value.string().unwrap(), "bar"); let (key, value) = iter.next().unwrap(); - assert_eq!(key, IterKey::Long(2)); + assert_eq!(key.long(), Some(2)); assert!(value.is_string()); assert_eq!(value.string().unwrap(), "baz"); + let (key, value) = iter.next().unwrap(); + + assert!(key.is_object()); + assert!(value.is_object()); + let next = iter.next(); assert!(next.is_none()); diff --git a/src/types/iterator.test.php b/src/types/iterator.test.php index e5463ecee5..891f0a9156 100644 --- a/src/types/iterator.test.php +++ b/src/types/iterator.test.php @@ -4,13 +4,13 @@ function create_generator() { yield 1; yield 2; yield 3; - yield new class {}; + yield new class {} => new class {}; } class TestIterator implements \Iterator { private $count = 0; - public function current() + public function current(): mixed { return match ($this->count) { 0 => 'foo', @@ -21,12 +21,12 @@ public function current() }; } - public function next() + public function next(): void { $this->count++; } - public function key() + public function key(): mixed { return match ($this->count) { 0 => 'key', @@ -37,12 +37,12 @@ public function key() }; } - public function valid() + public function valid(): bool { - return $this->count < 3; + return $this->count < 4; } - public function rewind() + public function rewind(): void { $this->count = 0; } From f449f4e4e119cdf4306cbaad495ecba50b6f32ce Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 18:48:13 +0100 Subject: [PATCH 100/100] Cleanup --- guide/src/types/iterable.md | 7 +------ guide/src/types/iterator.md | 7 +++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/guide/src/types/iterable.md b/guide/src/types/iterable.md index cda816d8bd..fec8072e3f 100644 --- a/guide/src/types/iterable.md +++ b/guide/src/types/iterable.md @@ -20,7 +20,7 @@ that implements the `Traversable` interface. This means that any value that can #[php_function] pub fn test_iterable(mut iterable: Iterable) { for (k, v) in iterable.iter().expect("cannot rewind iterator") { - println!("k: {:?} v: {}", k, v.string().unwrap()); + println!("k: {} v: {}", k.string().unwrap(), v.string().unwrap()); } } # fn main() {} @@ -34,14 +34,11 @@ pub fn test_iterable(mut iterable: Iterable) { $generator = function() { yield 'hello' => 'world'; yield 'rust' => 'php'; - yield 'okk'; - yield new class {} => new class {}; }; $array = [ 'hello' => 'world', 'rust' => 'php', - 'okk', ]; test_iterable($generator()); @@ -53,8 +50,6 @@ Output: ```text k: hello v: world k: rust v: php -k: 0 v: okk k: hello v: world k: rust v: php -k: 0 v: okk ``` diff --git a/guide/src/types/iterator.md b/guide/src/types/iterator.md index ce0e9cfba3..4c53e6727d 100644 --- a/guide/src/types/iterator.md +++ b/guide/src/types/iterator.md @@ -23,7 +23,9 @@ If you want a more universal `iterable` type that also supports arrays, see [Ite #[php_function] pub fn test_iterator(iterator: &mut ZendIterator) { for (k, v) in iterator.iter().expect("cannot rewind iterator") { - println!("k: {:?} v: {}", k, v.string().unwrap()); + // Note that the key can be anything, even an object + // when iterating over Traversables! + println!("k: {} v: {}", k.string().unwrap(), v.string().unwrap()); } } # fn main() {} @@ -37,8 +39,6 @@ pub fn test_iterator(iterator: &mut ZendIterator) { $generator = function() { yield 'hello' => 'world'; yield 'rust' => 'php'; - yield 'okk'; - yield new class {} => new class {}; }; test_iterator($generator()); @@ -49,5 +49,4 @@ Output: ```text k: hello v: world k: rust v: php -k: 0 v: okk ```