From a65282dd04f78e12bd7dd44fc24c3fd1214d8cb6 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 30 Oct 2023 16:21:40 +0100 Subject: [PATCH 1/8] feat(sapi): add initial work on having it's own sapi --- allowed_bindings.rs | 10 ++++- src/builders/mod.rs | 4 ++ src/builders/sapi.rs | 88 ++++++++++++++++++++++++++++++++++++++++ src/embed/embed.c | 15 +++++++ src/embed/embed.h | 2 + src/embed/ffi.rs | 2 + src/embed/mod.rs | 23 +++++++---- src/embed/sapi.rs | 15 +++++++ src/ffi.rs | 8 ++++ src/wrapper.c | 10 +++++ src/wrapper.h | 1 + src/zend/try_catch.rs | 94 ++++++++++++++++++++++++++++--------------- tests/sapi.rs | 81 +++++++++++++++++++++++++++++++++++++ 13 files changed, 311 insertions(+), 42 deletions(-) create mode 100644 src/builders/sapi.rs create mode 100644 src/embed/sapi.rs create mode 100644 tests/sapi.rs diff --git a/allowed_bindings.rs b/allowed_bindings.rs index ca2195bde0..dbc58c6ac8 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -260,7 +260,15 @@ bind! { zend_eval_string, zend_file_handle, zend_stream_init_filename, + zend_destroy_file_handle, php_execute_script, zend_register_module_ex, - _zend_bailout + _zend_bailout, + sapi_module_struct, + sapi_startup, + sapi_shutdown, + php_module_startup, + php_module_shutdown, + php_request_startup, + php_request_shutdown } diff --git a/src/builders/mod.rs b/src/builders/mod.rs index 66ad38942b..1a72a43a0b 100644 --- a/src/builders/mod.rs +++ b/src/builders/mod.rs @@ -4,7 +4,11 @@ mod class; mod function; mod module; +#[cfg(feature = "embed")] +mod sapi; pub use class::ClassBuilder; pub use function::FunctionBuilder; pub use module::ModuleBuilder; +#[cfg(feature = "embed")] +pub use sapi::SapiBuilder; diff --git a/src/builders/sapi.rs b/src/builders/sapi.rs new file mode 100644 index 0000000000..80ddff9976 --- /dev/null +++ b/src/builders/sapi.rs @@ -0,0 +1,88 @@ +use crate::ffi::sapi_header_struct; +use crate::{embed::SapiModule, error::Result}; + +use std::ffi::c_void; +use std::{ffi::CString, ptr}; + +pub struct SapiBuilder { + name: String, + pretty_name: String, + module: SapiModule, +} + +impl SapiBuilder { + pub fn new, U: Into>(name: T, pretty_name: U) -> Self { + Self { + name: name.into(), + pretty_name: pretty_name.into(), + module: SapiModule { + name: ptr::null_mut(), + pretty_name: ptr::null_mut(), + startup: None, + shutdown: None, + activate: None, + deactivate: None, + ub_write: None, + flush: None, + get_stat: None, + getenv: None, + sapi_error: None, + header_handler: None, + send_headers: None, + send_header: None, + read_post: None, + read_cookies: None, + register_server_variables: None, + log_message: None, + get_request_time: None, + terminate_process: None, + php_ini_path_override: ptr::null_mut(), + default_post_reader: None, + treat_data: None, + executable_location: ptr::null_mut(), + php_ini_ignore: 0, + php_ini_ignore_cwd: 0, + get_fd: None, + force_http_10: None, + get_target_uid: None, + get_target_gid: None, + input_filter: None, + ini_defaults: None, + phpinfo_as_text: 0, + ini_entries: ptr::null_mut(), + additional_functions: ptr::null(), + input_filter_init: None, + }, + } + } + + /// Sets the send header function for this SAPI + /// + /// # Arguments + /// + /// * `func` - The function to be called on shutdown. + pub fn send_header_function(mut self, func: SapiSendHeaderFunc) -> Self { + self.module.send_header = Some(func); + self + } + + /// Builds the extension and returns a `SapiModule`. + /// + /// Returns a result containing the sapi module if successful. + pub fn build(mut self) -> Result { + self.module.name = CString::new(self.name)?.into_raw(); + self.module.pretty_name = CString::new(self.pretty_name)?.into_raw(); + + if self.module.send_header.is_none() { + self.module.send_header = Some(dummy_send_header); + } + + Ok(self.module) + } +} + +/// A function to be called when the extension is starting up or shutting down. +pub type SapiSendHeaderFunc = + extern "C" fn(header: *mut sapi_header_struct, server_context: *mut c_void); + +extern "C" fn dummy_send_header(_header: *mut sapi_header_struct, _server_context: *mut c_void) {} diff --git a/src/embed/embed.c b/src/embed/embed.c index ae7d8bc625..3dc866b06c 100644 --- a/src/embed/embed.c +++ b/src/embed/embed.c @@ -13,3 +13,18 @@ void* ext_php_rs_embed_callback(int argc, char** argv, void* (*callback)(void *) return result; } + +void ext_php_rs_sapi_startup() { + #if defined(SIGPIPE) && defined(SIG_IGN) + signal(SIGPIPE, SIG_IGN); + #endif + + #ifdef ZTS + php_tsrm_startup(); + #ifdef PHP_WIN32 + ZEND_TSRMLS_CACHE_UPDATE(); + #endif + #endif + + zend_signal_startup(); +} diff --git a/src/embed/embed.h b/src/embed/embed.h index beabffbb3a..bf6810b367 100644 --- a/src/embed/embed.h +++ b/src/embed/embed.h @@ -2,3 +2,5 @@ #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_sapi_startup(); diff --git a/src/embed/ffi.rs b/src/embed/ffi.rs index b52ce6a92a..13706307d3 100644 --- a/src/embed/ffi.rs +++ b/src/embed/ffi.rs @@ -13,4 +13,6 @@ extern "C" { func: unsafe extern "C" fn(*const c_void) -> *const c_void, ctx: *const c_void, ) -> *mut c_void; + + pub fn ext_php_rs_sapi_startup(); } diff --git a/src/embed/mod.rs b/src/embed/mod.rs index 0ad64a6159..96737611e2 100644 --- a/src/embed/mod.rs +++ b/src/embed/mod.rs @@ -5,6 +5,7 @@ //! You should only use this crate for test purpose, it's not production ready mod ffi; +mod sapi; use crate::boxed::ZBox; use crate::embed::ffi::ext_php_rs_embed_callback; @@ -20,6 +21,9 @@ use std::panic::{resume_unwind, RefUnwindSafe}; use std::path::Path; use std::ptr::null_mut; +pub use ffi::ext_php_rs_sapi_startup; +pub use sapi::SapiModule; + pub struct Embed; #[derive(Debug)] @@ -86,7 +90,7 @@ impl Embed { zend_stream_init_filename(&mut file_handle, path.as_ptr()); } - let exec_result = try_catch(|| unsafe { php_execute_script(&mut file_handle) }); + let exec_result = try_catch(|| unsafe { php_execute_script(&mut file_handle) }, false); match exec_result { Err(_) => Err(EmbedError::CatchError), @@ -180,13 +184,16 @@ impl Embed { let mut result = Zval::new(); - 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 exec_result = try_catch( + || unsafe { + zend_eval_string( + cstr.as_ptr() as *const c_char, + &mut result, + b"run\0".as_ptr() as *const _, + ) + }, + false, + ); match exec_result { Err(_) => Err(EmbedError::CatchError), diff --git a/src/embed/sapi.rs b/src/embed/sapi.rs new file mode 100644 index 0000000000..ef6c359358 --- /dev/null +++ b/src/embed/sapi.rs @@ -0,0 +1,15 @@ +//! Builder and objects for creating modules in PHP. A module is the base of a +//! PHP extension. + +use crate::ffi::sapi_module_struct; + +/// A Zend module entry, also known as an extension. +pub type SapiModule = sapi_module_struct; + +impl SapiModule { + /// Allocates the module entry on the heap, returning a pointer to the + /// memory location. The caller is responsible for the memory pointed to. + pub fn into_raw(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } +} diff --git a/src/ffi.rs b/src/ffi.rs index a6c3d94865..e7b5c141db 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -26,11 +26,19 @@ 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_first_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() -> !; } diff --git a/src/wrapper.c b/src/wrapper.c index a1fd900ff5..6c0696f279 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -50,6 +50,16 @@ bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **resu return false; } +bool ext_php_rs_zend_first_try_catch(void* (*callback)(void *), void *ctx, void **result) { + zend_first_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 e4e55517ca..ab22165382 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -32,4 +32,5 @@ 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(); bool ext_php_rs_zend_try_catch(void* (*callback)(void *), void *ctx, void **result); +bool ext_php_rs_zend_first_try_catch(void* (*callback)(void *), void *ctx, void **result); void ext_php_rs_zend_bailout(); diff --git a/src/zend/try_catch.rs b/src/zend/try_catch.rs index f74b427a59..b0822e417d 100644 --- a/src/zend/try_catch.rs +++ b/src/zend/try_catch.rs @@ -1,4 +1,6 @@ -use crate::ffi::{ext_php_rs_zend_bailout, ext_php_rs_zend_try_catch}; +use crate::ffi::{ + ext_php_rs_zend_bailout, ext_php_rs_zend_first_try_catch, ext_php_rs_zend_try_catch, +}; use std::ffi::c_void; use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe}; use std::ptr::null_mut; @@ -26,14 +28,25 @@ pub(crate) unsafe extern "C" fn panic_wrapper R + RefUnwindSafe /// /// * `Ok(R)` - The result of the function /// * `Err(CatchError)` - A bailout occurred during the execution -pub fn try_catch R + RefUnwindSafe>(func: F) -> Result { +pub fn try_catch R + RefUnwindSafe>( + func: F, + first: bool, +) -> 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, - ) + if first { + ext_php_rs_zend_first_try_catch( + panic_wrapper::, + &func as *const F as *const c_void, + (&mut panic_ptr) as *mut *mut c_void, + ) + } else { + 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; @@ -78,16 +91,19 @@ mod tests { #[test] fn test_catch() { Embed::run(|| { - let catch = try_catch(|| { - unsafe { - bailout(); - } - - #[allow(unreachable_code)] - { - assert!(false); - } - }); + let catch = try_catch( + || { + unsafe { + bailout(); + } + + #[allow(unreachable_code)] + { + assert!(false); + } + }, + false, + ); assert!(catch.is_err()); }); @@ -96,9 +112,12 @@ mod tests { #[test] fn test_no_catch() { Embed::run(|| { - let catch = try_catch(|| { - assert!(true); - }); + let catch = try_catch( + || { + assert!(true); + }, + false, + ); assert!(catch.is_ok()); }); @@ -122,18 +141,24 @@ mod tests { #[should_panic] fn test_panic() { Embed::run(|| { - let _ = try_catch(|| { - panic!("should panic"); - }); + let _ = try_catch( + || { + panic!("should panic"); + }, + false, + ); }); } #[test] fn test_return() { let foo = Embed::run(|| { - let result = try_catch(|| { - return "foo"; - }); + let result = try_catch( + || { + return "foo"; + }, + false, + ); assert!(result.is_ok()); @@ -147,14 +172,17 @@ mod tests { fn test_memory_leak() { let mut ptr = null_mut(); - let _ = try_catch(|| { - let mut result = "foo".to_string(); - ptr = &mut result; + let _ = try_catch( + || { + let mut result = "foo".to_string(); + ptr = &mut result; - unsafe { - bailout(); - } - }); + unsafe { + bailout(); + } + }, + false, + ); // Check that the string is never released let result = unsafe { &*ptr as &str }; diff --git a/tests/sapi.rs b/tests/sapi.rs new file mode 100644 index 0000000000..e11c4d88a7 --- /dev/null +++ b/tests/sapi.rs @@ -0,0 +1,81 @@ +#![cfg_attr(windows, feature(abi_vectorcall))] +extern crate ext_php_rs; + +#[cfg(feature = "embed")] +use ext_php_rs::builders::SapiBuilder; +#[cfg(feature = "embed")] +use ext_php_rs::embed::{ext_php_rs_sapi_startup, Embed}; +use ext_php_rs::ffi::{ + php_module_shutdown, php_module_startup, php_request_shutdown, php_request_startup, + sapi_shutdown, sapi_startup, ZEND_RESULT_CODE_SUCCESS, +}; +use ext_php_rs::prelude::*; +use ext_php_rs::zend::try_catch; + +#[test] +#[cfg(feature = "embed")] +fn test_sapi() { + let builder = SapiBuilder::new("test", "Test"); + let sapi = builder.build().unwrap().into_raw(); + let module = get_module(); + + unsafe { + ext_php_rs_sapi_startup(); + } + + unsafe { + sapi_startup(sapi); + } + + unsafe { + php_module_startup(sapi, module); + } + + let result = unsafe { php_request_startup() }; + + assert_eq!(result, ZEND_RESULT_CODE_SUCCESS); + + let _ = try_catch( + || { + 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!"); + }, + true, + ); + + unsafe { + php_request_shutdown(std::ptr::null_mut()); + } + + unsafe { + php_module_shutdown(); + } + + unsafe { + sapi_shutdown(); + } +} + +/// 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 51042e531044a451659146cd33278646c422047a Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 30 Oct 2023 16:33:47 +0100 Subject: [PATCH 2/8] fix test without embed --- tests/sapi.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/sapi.rs b/tests/sapi.rs index e11c4d88a7..f6bdf38b18 100644 --- a/tests/sapi.rs +++ b/tests/sapi.rs @@ -5,11 +5,13 @@ extern crate ext_php_rs; use ext_php_rs::builders::SapiBuilder; #[cfg(feature = "embed")] use ext_php_rs::embed::{ext_php_rs_sapi_startup, Embed}; +#[cfg(feature = "embed")] use ext_php_rs::ffi::{ php_module_shutdown, php_module_startup, php_request_shutdown, php_request_startup, sapi_shutdown, sapi_startup, ZEND_RESULT_CODE_SUCCESS, }; use ext_php_rs::prelude::*; +#[cfg(feature = "embed")] use ext_php_rs::zend::try_catch; #[test] From 4f74bfcd8a7a936248ad7ce416d52de73f7c1afd Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Mon, 30 Oct 2023 16:58:00 +0100 Subject: [PATCH 3/8] add output --- src/builders/sapi.rs | 12 ++++++++++-- tests/sapi.rs | 31 ++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/builders/sapi.rs b/src/builders/sapi.rs index 80ddff9976..4916d3d0db 100644 --- a/src/builders/sapi.rs +++ b/src/builders/sapi.rs @@ -1,7 +1,7 @@ use crate::ffi::sapi_header_struct; use crate::{embed::SapiModule, error::Result}; -use std::ffi::c_void; +use std::ffi::{c_char, c_void}; use std::{ffi::CString, ptr}; pub struct SapiBuilder { @@ -56,6 +56,11 @@ impl SapiBuilder { } } + pub fn ub_write_function(mut self, func: SapiUbWriteFunc) -> Self { + self.module.ub_write = Some(func); + self + } + /// Sets the send header function for this SAPI /// /// # Arguments @@ -81,8 +86,11 @@ impl SapiBuilder { } } -/// A function to be called when the extension is starting up or shutting down. +/// A function to be called when PHP send a header pub type SapiSendHeaderFunc = extern "C" fn(header: *mut sapi_header_struct, server_context: *mut c_void); +/// A function to be called when PHP write to the output buffer +pub type SapiUbWriteFunc = extern "C" fn(str: *const c_char, str_length: usize) -> usize; + extern "C" fn dummy_send_header(_header: *mut sapi_header_struct, _server_context: *mut c_void) {} diff --git a/tests/sapi.rs b/tests/sapi.rs index f6bdf38b18..cd579af68a 100644 --- a/tests/sapi.rs +++ b/tests/sapi.rs @@ -1,6 +1,8 @@ #![cfg_attr(windows, feature(abi_vectorcall))] extern crate ext_php_rs; +#[cfg(feature = "embed")] +use std::ffi::c_char; #[cfg(feature = "embed")] use ext_php_rs::builders::SapiBuilder; #[cfg(feature = "embed")] @@ -14,10 +16,29 @@ use ext_php_rs::prelude::*; #[cfg(feature = "embed")] use ext_php_rs::zend::try_catch; +#[cfg(feature = "embed")] +static mut LAST_OUTPUT: String = String::new(); + +#[cfg(feature = "embed")] +extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize { + let char = unsafe { std::slice::from_raw_parts(str as *const u8, str_length) }; + let string = String::from_utf8_lossy(char); + + println!("{}", string); + + unsafe { + LAST_OUTPUT = string.to_string(); + }; + + str_length +} + #[test] #[cfg(feature = "embed")] fn test_sapi() { - let builder = SapiBuilder::new("test", "Test"); + let mut builder = SapiBuilder::new("test", "Test"); + builder = builder.ub_write_function(output_tester); + let sapi = builder.build().unwrap().into_raw(); let module = get_module(); @@ -50,6 +71,10 @@ fn test_sapi() { let string = zval.string().unwrap(); assert_eq!(string.to_string(), "Hello, foo!"); + + let result = Embed::eval("var_dump($foo);"); + + assert!(result.is_ok()); }, true, ); @@ -58,6 +83,10 @@ fn test_sapi() { php_request_shutdown(std::ptr::null_mut()); } + unsafe { + assert_eq!(LAST_OUTPUT, "string(11) \"Hello, foo!\"\n"); + } + unsafe { php_module_shutdown(); } From 411ee6c1767c8196c3e0df8a3ca29e113320578d Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Tue, 31 Oct 2023 10:31:53 +0100 Subject: [PATCH 4/8] feat(sapi): split try_catch / try_catch_first, global feature for test --- src/embed/mod.rs | 19 ++++----- src/zend/mod.rs | 2 +- src/zend/try_catch.rs | 93 ++++++++++++++++++++++--------------------- tests/module.rs | 4 +- tests/sapi.rs | 36 ++++++----------- 5 files changed, 71 insertions(+), 83 deletions(-) diff --git a/src/embed/mod.rs b/src/embed/mod.rs index 96737611e2..6176e9f229 100644 --- a/src/embed/mod.rs +++ b/src/embed/mod.rs @@ -90,7 +90,7 @@ impl Embed { zend_stream_init_filename(&mut file_handle, path.as_ptr()); } - let exec_result = try_catch(|| unsafe { php_execute_script(&mut file_handle) }, false); + let exec_result = try_catch(|| unsafe { php_execute_script(&mut file_handle) }); match exec_result { Err(_) => Err(EmbedError::CatchError), @@ -184,16 +184,13 @@ impl Embed { let mut result = Zval::new(); - 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 _, - ) - }, - false, - ); + 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 _, + ) + }); match exec_result { Err(_) => Err(EmbedError::CatchError), diff --git a/src/zend/mod.rs b/src/zend/mod.rs index af8a5c2d8e..7e4838f1c6 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -25,7 +25,7 @@ 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}; +pub use try_catch::{bailout, try_catch, try_catch_first}; // 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 index b0822e417d..8639cff667 100644 --- a/src/zend/try_catch.rs +++ b/src/zend/try_catch.rs @@ -28,10 +28,28 @@ pub(crate) unsafe extern "C" fn panic_wrapper R + RefUnwindSafe /// /// * `Ok(R)` - The result of the function /// * `Err(CatchError)` - A bailout occurred during the execution -pub fn try_catch R + RefUnwindSafe>( - func: F, - first: bool, -) -> Result { +pub fn try_catch R + RefUnwindSafe>(func: F) -> Result { + do_try_catch(func, false) +} + +/// 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_first allow to use this mechanism +/// +/// This functions differs from ['try_catch'] as it also initialize the bailout mechanism +/// for the first time +/// +/// # Returns +/// +/// * `Ok(R)` - The result of the function +/// * `Err(CatchError)` - A bailout occurred during the execution +pub fn try_catch_first R + RefUnwindSafe>(func: F) -> Result { + do_try_catch(func, true) +} + +fn do_try_catch R + RefUnwindSafe>(func: F, first: bool) -> Result { let mut panic_ptr = null_mut(); let has_bailout = unsafe { if first { @@ -91,19 +109,16 @@ mod tests { #[test] fn test_catch() { Embed::run(|| { - let catch = try_catch( - || { - unsafe { - bailout(); - } - - #[allow(unreachable_code)] - { - assert!(false); - } - }, - false, - ); + let catch = try_catch(|| { + unsafe { + bailout(); + } + + #[allow(unreachable_code)] + { + assert!(false); + } + }); assert!(catch.is_err()); }); @@ -112,12 +127,9 @@ mod tests { #[test] fn test_no_catch() { Embed::run(|| { - let catch = try_catch( - || { - assert!(true); - }, - false, - ); + let catch = try_catch(|| { + assert!(true); + }); assert!(catch.is_ok()); }); @@ -141,24 +153,18 @@ mod tests { #[should_panic] fn test_panic() { Embed::run(|| { - let _ = try_catch( - || { - panic!("should panic"); - }, - false, - ); + let _ = try_catch(|| { + panic!("should panic"); + }); }); } #[test] fn test_return() { let foo = Embed::run(|| { - let result = try_catch( - || { - return "foo"; - }, - false, - ); + let result = try_catch(|| { + return "foo"; + }); assert!(result.is_ok()); @@ -172,17 +178,14 @@ mod tests { fn test_memory_leak() { let mut ptr = null_mut(); - let _ = try_catch( - || { - let mut result = "foo".to_string(); - ptr = &mut result; + let _ = try_catch(|| { + let mut result = "foo".to_string(); + ptr = &mut result; - unsafe { - bailout(); - } - }, - false, - ); + unsafe { + bailout(); + } + }); // Check that the string is never released let result = unsafe { &*ptr as &str }; diff --git a/tests/module.rs b/tests/module.rs index f1ef22b852..6407f998e6 100644 --- a/tests/module.rs +++ b/tests/module.rs @@ -1,14 +1,12 @@ #![cfg_attr(windows, feature(abi_vectorcall))] +#![cfg(feature = "embed")] 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 diff --git a/tests/sapi.rs b/tests/sapi.rs index cd579af68a..8aa0443517 100644 --- a/tests/sapi.rs +++ b/tests/sapi.rs @@ -1,25 +1,19 @@ #![cfg_attr(windows, feature(abi_vectorcall))] +#![cfg(feature = "embed")] extern crate ext_php_rs; -#[cfg(feature = "embed")] -use std::ffi::c_char; -#[cfg(feature = "embed")] use ext_php_rs::builders::SapiBuilder; -#[cfg(feature = "embed")] use ext_php_rs::embed::{ext_php_rs_sapi_startup, Embed}; -#[cfg(feature = "embed")] use ext_php_rs::ffi::{ php_module_shutdown, php_module_startup, php_request_shutdown, php_request_startup, sapi_shutdown, sapi_startup, ZEND_RESULT_CODE_SUCCESS, }; use ext_php_rs::prelude::*; -#[cfg(feature = "embed")] -use ext_php_rs::zend::try_catch; +use ext_php_rs::zend::try_catch_first; +use std::ffi::c_char; -#[cfg(feature = "embed")] static mut LAST_OUTPUT: String = String::new(); -#[cfg(feature = "embed")] extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize { let char = unsafe { std::slice::from_raw_parts(str as *const u8, str_length) }; let string = String::from_utf8_lossy(char); @@ -34,7 +28,6 @@ extern "C" fn output_tester(str: *const c_char, str_length: usize) -> usize { } #[test] -#[cfg(feature = "embed")] fn test_sapi() { let mut builder = SapiBuilder::new("test", "Test"); builder = builder.ub_write_function(output_tester); @@ -58,26 +51,23 @@ fn test_sapi() { assert_eq!(result, ZEND_RESULT_CODE_SUCCESS); - let _ = try_catch( - || { - let result = Embed::eval("$foo = hello_world('foo');"); + let _ = try_catch_first(|| { + let result = Embed::eval("$foo = hello_world('foo');"); - assert!(result.is_ok()); + assert!(result.is_ok()); - let zval = result.unwrap(); + let zval = result.unwrap(); - assert!(zval.is_string()); + assert!(zval.is_string()); - let string = zval.string().unwrap(); + let string = zval.string().unwrap(); - assert_eq!(string.to_string(), "Hello, foo!"); + assert_eq!(string.to_string(), "Hello, foo!"); - let result = Embed::eval("var_dump($foo);"); + let result = Embed::eval("var_dump($foo);"); - assert!(result.is_ok()); - }, - true, - ); + assert!(result.is_ok()); + }); unsafe { php_request_shutdown(std::ptr::null_mut()); From 1aeb065743d183879a2073e968818d17f158c7cd Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 17:14:47 +0100 Subject: [PATCH 5/8] Update docsrs --- docsrs_bindings.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index d6678ea069..8800d5adb0 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -746,6 +746,9 @@ extern "C" { filename: *const ::std::os::raw::c_char, ); } +extern "C" { + pub fn zend_destroy_file_handle(file_handle: *mut zend_file_handle); +} pub type zend_stat_t = stat; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -2514,6 +2517,12 @@ pub type sapi_globals_struct = _sapi_globals_struct; extern "C" { pub static mut sapi_globals: sapi_globals_struct; } +extern "C" { + pub fn sapi_startup(sf: *mut sapi_module_struct); +} +extern "C" { + pub fn sapi_shutdown(); +} 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; From 1beaae3156dfa17dd4f6afee7d41e646ea3e386f Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 17:20:20 +0100 Subject: [PATCH 6/8] Cleanup --- allowed_bindings.rs | 1 - docsrs_bindings.rs | 240 ++++++++++++++++++++++++-------------------- 2 files changed, 129 insertions(+), 112 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index a1670b0a0e..7e16a51407 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -298,7 +298,6 @@ bind! { php_execute_script, zend_register_module_ex, _zend_bailout, - sapi_module_struct, sapi_startup, sapi_shutdown, php_module_startup, diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 8800d5adb0..606ec0a4dc 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -2336,117 +2336,6 @@ extern "C" { module_number: ::std::os::raw::c_int, ); } -extern "C" { - pub fn php_info_print_table_header(num_cols: ::std::os::raw::c_int, ...); -} -extern "C" { - pub fn php_info_print_table_row(num_cols: ::std::os::raw::c_int, ...); -} -extern "C" { - pub fn php_info_print_table_start(); -} -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; -} -extern "C" { - pub static mut zend_ce_exception: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_error_exception: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_compile_error: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_parse_error: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_type_error: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_argument_count_error: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_value_error: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_arithmetic_error: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_division_by_zero_error: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_unhandled_match_error: *mut zend_class_entry; -} -extern "C" { - pub fn zend_throw_exception_ex( - exception_ce: *mut zend_class_entry, - code: zend_long, - format: *const ::std::os::raw::c_char, - ... - ) -> *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); -} -extern "C" { - pub static mut zend_ce_traversable: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_aggregate: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_iterator: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_arrayaccess: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_serializable: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_countable: *mut zend_class_entry; -} -extern "C" { - pub static mut zend_ce_stringable: *mut zend_class_entry; -} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct sapi_header_struct { @@ -2641,3 +2530,132 @@ pub struct _sapi_post_entry { ), >, } +extern "C" { + pub fn php_request_startup() -> zend_result; +} +extern "C" { + pub fn php_request_shutdown(dummy: *mut ::std::os::raw::c_void); +} +extern "C" { + pub fn php_module_startup( + sf: *mut sapi_module_struct, + additional_module: *mut zend_module_entry, + ) -> zend_result; +} +extern "C" { + pub fn php_module_shutdown(); +} +extern "C" { + pub fn php_execute_script(primary_file: *mut zend_file_handle) -> bool; +} +extern "C" { + pub fn php_info_print_table_header(num_cols: ::std::os::raw::c_int, ...); +} +extern "C" { + pub fn php_info_print_table_row(num_cols: ::std::os::raw::c_int, ...); +} +extern "C" { + pub fn php_info_print_table_start(); +} +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; +} +extern "C" { + pub static mut zend_ce_exception: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_error_exception: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_compile_error: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_parse_error: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_type_error: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_argument_count_error: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_value_error: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_arithmetic_error: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_division_by_zero_error: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_unhandled_match_error: *mut zend_class_entry; +} +extern "C" { + pub fn zend_throw_exception_ex( + exception_ce: *mut zend_class_entry, + code: zend_long, + format: *const ::std::os::raw::c_char, + ... + ) -> *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); +} +extern "C" { + pub static mut zend_ce_traversable: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_aggregate: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_iterator: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_arrayaccess: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_serializable: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_countable: *mut zend_class_entry; +} +extern "C" { + pub static mut zend_ce_stringable: *mut zend_class_entry; +} From bc4405de8ba1dab8a5e676e75f275c46dd1ea607 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 18:30:39 +0100 Subject: [PATCH 7/8] 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 3cb7b8fefd049c5e5173f3cc5292b5c16e2b8a49 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 24 Nov 2023 18:37:40 +0100 Subject: [PATCH 8/8] Fixup test --- tests/sapi.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/sapi.rs b/tests/sapi.rs index 8aa0443517..98a53e9530 100644 --- a/tests/sapi.rs +++ b/tests/sapi.rs @@ -73,10 +73,6 @@ fn test_sapi() { php_request_shutdown(std::ptr::null_mut()); } - unsafe { - assert_eq!(LAST_OUTPUT, "string(11) \"Hello, foo!\"\n"); - } - unsafe { php_module_shutdown(); }