diff --git a/tests/src/integration/array.rs b/tests/src/integration/array.rs deleted file mode 100644 index 617ab770c5..0000000000 --- a/tests/src/integration/array.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn array_works() { - assert!(crate::integration::run_php("array.php")); -} diff --git a/tests/src/integration/array.php b/tests/src/integration/array/array.php similarity index 95% rename from tests/src/integration/array.php rename to tests/src/integration/array/array.php index 1232582c91..d6909244e6 100644 --- a/tests/src/integration/array.php +++ b/tests/src/integration/array/array.php @@ -1,7 +1,5 @@ ) -> Vec { + a +} + +#[php_function] +pub fn test_array_assoc(a: HashMap) -> HashMap { + a +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder + .function(wrap_function!(test_array)) + .function(wrap_function!(test_array_assoc)) +} + +#[cfg(test)] +mod tests { + #[test] + fn array_works() { + assert!(crate::integration::test::run_php("array/array.php")); + } +} diff --git a/tests/src/integration/binary.rs b/tests/src/integration/binary.rs deleted file mode 100644 index b47abc3886..0000000000 --- a/tests/src/integration/binary.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn binary_works() { - assert!(crate::integration::run_php("binary.php")); -} diff --git a/tests/src/integration/binary.php b/tests/src/integration/binary/binary.php similarity index 81% rename from tests/src/integration/binary.php rename to tests/src/integration/binary/binary.php index 30cbd429f7..a8ae8717fd 100644 --- a/tests/src/integration/binary.php +++ b/tests/src/integration/binary/binary.php @@ -1,7 +1,5 @@ ) -> Binary { + a +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder.function(wrap_function!(test_binary)) +} + +#[cfg(test)] +mod tests { + #[test] + fn binary_works() { + assert!(crate::integration::test::run_php("binary/binary.php")); + } +} diff --git a/tests/src/integration/bool.rs b/tests/src/integration/bool.rs deleted file mode 100644 index aadf22aa97..0000000000 --- a/tests/src/integration/bool.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn bool_works() { - assert!(crate::integration::run_php("bool.php")); -} diff --git a/tests/src/integration/bool.php b/tests/src/integration/bool/bool.php similarity index 76% rename from tests/src/integration/bool.php rename to tests/src/integration/bool/bool.php index 8ebc6a98f9..7205ace31d 100644 --- a/tests/src/integration/bool.php +++ b/tests/src/integration/bool/bool.php @@ -1,6 +1,4 @@ bool { + a +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder.function(wrap_function!(test_bool)) +} + +#[cfg(test)] +mod tests { + #[test] + fn bool_works() { + assert!(crate::integration::test::run_php("bool/bool.php")); + } +} diff --git a/tests/src/integration/callable.rs b/tests/src/integration/callable.rs deleted file mode 100644 index 1d4ac2d512..0000000000 --- a/tests/src/integration/callable.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn callable_works() { - assert!(crate::integration::run_php("callable.php")); -} diff --git a/tests/src/integration/callable.php b/tests/src/integration/callable/callable.php similarity index 74% rename from tests/src/integration/callable.php rename to tests/src/integration/callable/callable.php index 992cd16167..6ad53a1cbe 100644 --- a/tests/src/integration/callable.php +++ b/tests/src/integration/callable/callable.php @@ -1,5 +1,3 @@ $a, 'test') === 'test'); diff --git a/tests/src/integration/callable/mod.rs b/tests/src/integration/callable/mod.rs new file mode 100644 index 0000000000..e4213d9f1b --- /dev/null +++ b/tests/src/integration/callable/mod.rs @@ -0,0 +1,18 @@ +use ext_php_rs::{prelude::*, types::Zval}; + +#[php_function] +pub fn test_callable(call: ZendCallable, a: String) -> Zval { + call.try_call(vec![&a]).expect("Failed to call function") +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder.function(wrap_function!(test_callable)) +} + +#[cfg(test)] +mod tests { + #[test] + fn callable_works() { + assert!(crate::integration::test::run_php("callable/callable.php")); + } +} diff --git a/tests/src/integration/class.rs b/tests/src/integration/class.rs deleted file mode 100644 index 525220e859..0000000000 --- a/tests/src/integration/class.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn class_works() { - assert!(crate::integration::run_php("class.php")); -} diff --git a/tests/src/integration/class.php b/tests/src/integration/class/class.php similarity index 96% rename from tests/src/integration/class.php rename to tests/src/integration/class/class.php index af705a1132..a6295f6e0e 100644 --- a/tests/src/integration/class.php +++ b/tests/src/integration/class/class.php @@ -1,7 +1,5 @@ String { + self.string.to_string() + } + + #[php(setter)] + pub fn set_string(&mut self, string: String) { + self.string = string; + } + + #[php(getter)] + pub fn get_number(&self) -> i32 { + self.number + } + + #[php(setter)] + pub fn set_number(&mut self, number: i32) { + self.number = number; + } + + pub fn static_call(name: String) -> String { + format!("Hello {name}") + } +} + +#[php_function] +pub fn test_class(string: String, number: i32) -> TestClass { + TestClass { + string, + number, + boolean: true, + } +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder + .class::() + .function(wrap_function!(test_class)) +} + +#[cfg(test)] +mod tests { + #[test] + fn class_works() { + assert!(crate::integration::test::run_php("class/class.php")); + } +} diff --git a/tests/src/integration/closure.rs b/tests/src/integration/closure.rs deleted file mode 100644 index fad7e3c255..0000000000 --- a/tests/src/integration/closure.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn closure_works() { - assert!(crate::integration::run_php("closure.php")); -} diff --git a/tests/src/integration/closure.php b/tests/src/integration/closure/closure.php similarity index 91% rename from tests/src/integration/closure.php rename to tests/src/integration/closure/closure.php index 24137c74f9..4d11b55611 100644 --- a/tests/src/integration/closure.php +++ b/tests/src/integration/closure/closure.php @@ -1,6 +1,6 @@ getMessage(), 'take(): Argument #1 ($rs) must be of type stdClass, RustClosure given, called in ')); -} \ No newline at end of file +} diff --git a/tests/src/integration/closure/mod.rs b/tests/src/integration/closure/mod.rs new file mode 100644 index 0000000000..69d36c12be --- /dev/null +++ b/tests/src/integration/closure/mod.rs @@ -0,0 +1,25 @@ +use ext_php_rs::prelude::*; + +#[php_function] +pub fn test_closure() -> Closure { + Closure::wrap(Box::new(|a| a) as Box String>) +} + +#[php_function] +pub fn test_closure_once(a: String) -> Closure { + Closure::wrap_once(Box::new(move || a) as Box String>) +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder + .function(wrap_function!(test_closure)) + .function(wrap_function!(test_closure_once)) +} + +#[cfg(test)] +mod tests { + #[test] + fn closure_works() { + assert!(crate::integration::test::run_php("closure/closure.php")); + } +} diff --git a/tests/src/integration/globals.rs b/tests/src/integration/globals.rs deleted file mode 100644 index 4ab3441eb0..0000000000 --- a/tests/src/integration/globals.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn globals_works() { - assert!(crate::integration::run_php("globals.php")); -} diff --git a/tests/src/integration/globals.php b/tests/src/integration/globals/globals.php similarity index 100% rename from tests/src/integration/globals.php rename to tests/src/integration/globals/globals.php diff --git a/tests/src/integration/globals/mod.rs b/tests/src/integration/globals/mod.rs new file mode 100644 index 0000000000..128015c8cd --- /dev/null +++ b/tests/src/integration/globals/mod.rs @@ -0,0 +1,52 @@ +use ext_php_rs::{boxed::ZBox, prelude::*, types::ZendHashTable, zend::ProcessGlobals}; + +#[php_function] +pub fn test_globals_http_get() -> ZBox { + ProcessGlobals::get().http_get_vars().to_owned() +} + +#[php_function] +pub fn test_globals_http_post() -> ZBox { + ProcessGlobals::get().http_post_vars().to_owned() +} + +#[php_function] +pub fn test_globals_http_cookie() -> ZBox { + ProcessGlobals::get().http_cookie_vars().to_owned() +} + +#[php_function] +pub fn test_globals_http_server() -> ZBox { + ProcessGlobals::get().http_server_vars().unwrap().to_owned() +} + +#[php_function] +pub fn test_globals_http_request() -> ZBox { + ProcessGlobals::get() + .http_request_vars() + .unwrap() + .to_owned() +} + +#[php_function] +pub fn test_globals_http_files() -> ZBox { + ProcessGlobals::get().http_files_vars().to_owned() +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder + .function(wrap_function!(test_globals_http_get)) + .function(wrap_function!(test_globals_http_post)) + .function(wrap_function!(test_globals_http_cookie)) + .function(wrap_function!(test_globals_http_server)) + .function(wrap_function!(test_globals_http_request)) + .function(wrap_function!(test_globals_http_files)) +} + +#[cfg(test)] +mod tests { + #[test] + fn globals_works() { + assert!(crate::integration::test::run_php("globals/globals.php")); + } +} diff --git a/tests/src/integration/iterator.rs b/tests/src/integration/iterator.rs deleted file mode 100644 index 0667bc6d9c..0000000000 --- a/tests/src/integration/iterator.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn iterator_works() { - assert!(crate::integration::run_php("iterator.php")); -} diff --git a/tests/src/integration/iterator.php b/tests/src/integration/iterator/iterator.php similarity index 100% rename from tests/src/integration/iterator.php rename to tests/src/integration/iterator/iterator.php diff --git a/tests/src/integration/iterator/mod.rs b/tests/src/integration/iterator/mod.rs new file mode 100644 index 0000000000..244e199c53 --- /dev/null +++ b/tests/src/integration/iterator/mod.rs @@ -0,0 +1,71 @@ +use ext_php_rs::{ + prelude::*, + types::{ArrayKey, ZendHashTable, Zval}, +}; + +#[php_function] +pub fn iter_next(ht: &ZendHashTable) -> Vec { + ht.iter() + .flat_map(|(k, v)| [key_to_zval(k), v.shallow_clone()]) + .collect() +} + +#[php_function] +pub fn iter_back(ht: &ZendHashTable) -> Vec { + ht.iter() + .rev() + .flat_map(|(k, v)| [key_to_zval(k), v.shallow_clone()]) + .collect() +} + +#[php_function] +pub fn iter_next_back(ht: &ZendHashTable, modulus: usize) -> Vec> { + let mut result = Vec::with_capacity(ht.len()); + let mut iter = ht.iter(); + + for i in 0..ht.len() + modulus { + let entry = if i % modulus == 0 { + iter.next_back() + } else { + iter.next() + }; + + if let Some((k, v)) = entry { + result.push(Some(key_to_zval(k))); + result.push(Some(v.shallow_clone())); + } else { + result.push(None); + } + } + + result +} + +fn key_to_zval(key: ArrayKey) -> Zval { + match key { + ArrayKey::String(s) => { + let mut zval = Zval::new(); + let _ = zval.set_string(s.as_str(), false); + zval + } + ArrayKey::Long(l) => { + let mut zval = Zval::new(); + zval.set_long(l); + zval + } + } +} +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder + .function(wrap_function!(iter_next)) + .function(wrap_function!(iter_back)) + .function(wrap_function!(iter_next_back)) +} + +#[cfg(test)] +mod tests { + #[test] + fn iterator_works() { + assert!(crate::integration::test::run_php("iterator/iterator.php")); + } +} diff --git a/tests/src/integration/magic_method.rs b/tests/src/integration/magic_method.rs deleted file mode 100644 index 145a8cf5e8..0000000000 --- a/tests/src/integration/magic_method.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn magic_method() { - assert!(crate::integration::run_php("magic_method.php")); -} diff --git a/tests/src/integration/magic_method.php b/tests/src/integration/magic_method/magic_method.php similarity index 100% rename from tests/src/integration/magic_method.php rename to tests/src/integration/magic_method/magic_method.php diff --git a/tests/src/integration/magic_method/mod.rs b/tests/src/integration/magic_method/mod.rs new file mode 100644 index 0000000000..8378208fe4 --- /dev/null +++ b/tests/src/integration/magic_method/mod.rs @@ -0,0 +1,107 @@ +#![allow(clippy::unused_self)] +use ext_php_rs::{prelude::*, types::Zval}; +use std::collections::HashMap; + +#[php_class] +pub struct MagicMethod(i64); + +#[php_impl] +impl MagicMethod { + pub fn __construct() -> Self { + Self(0) + } + + pub fn __destruct(&self) {} + + pub fn __call(&self, name: String, _arguments: HashMap) -> Zval { + let mut z = Zval::new(); + if name == "callMagicMethod" { + let s = "Hello".to_string(); + + let _ = z.set_string(s.as_str(), false); + z + } else { + z.set_null(); + z + } + } + + pub fn __call_static(name: String, arguments: HashMap) -> Zval { + let mut zval = Zval::new(); + if name == "callStaticSomeMagic" { + let concat_args = format!( + "Hello from static call {}", + arguments + .iter() + .filter(|(_, v)| v.is_long()) + .map(|(_, s)| s.long().unwrap()) + .collect::>() + .iter() + .sum::() + ); + + let _ = zval.set_string(&concat_args, false); + zval + } else { + zval.set_null(); + zval + } + } + + pub fn __get(&self, name: String) -> Zval { + let mut v = Zval::new(); + v.set_null(); + if name == "count" { + v.set_long(self.0); + } + + v + } + + pub fn __set(&mut self, prop_name: String, val: &Zval) { + if val.is_long() && prop_name == "count" { + self.0 = val.long().unwrap(); + } + } + + pub fn __isset(&self, prop_name: String) -> bool { + "count" == prop_name + } + + pub fn __unset(&mut self, prop_name: String) { + if prop_name == "count" { + self.0 = 0; + } + } + + pub fn __to_string(&self) -> String { + self.0.to_string() + } + + pub fn __invoke(&self, n: i64) -> i64 { + self.0 + n + } + + pub fn __debug_info(&self) -> HashMap { + let mut h: HashMap = HashMap::new(); + let mut z = Zval::new(); + z.set_long(self.0); + h.insert("count".to_string(), z); + + h + } +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder.class::() +} + +#[cfg(test)] +mod tests { + #[test] + fn magic_method() { + assert!(crate::integration::test::run_php( + "magic_method/magic_method.php" + )); + } +} diff --git a/tests/src/integration/mod.rs b/tests/src/integration/mod.rs new file mode 100644 index 0000000000..bd16cc75c3 --- /dev/null +++ b/tests/src/integration/mod.rs @@ -0,0 +1,72 @@ +pub mod array; +pub mod binary; +pub mod bool; +pub mod callable; +pub mod class; +pub mod closure; +pub mod globals; +pub mod iterator; +pub mod magic_method; +pub mod nullable; +pub mod number; +pub mod object; +pub mod string; +pub mod types; +pub mod variadic_args; + +#[cfg(test)] +mod test { + use std::env; + + use std::process::Command; + use std::sync::Once; + + static BUILD: Once = Once::new(); + + fn setup() { + BUILD.call_once(|| { + assert!(Command::new("cargo") + .arg("build") + .output() + .expect("failed to build extension") + .status + .success()); + }); + } + + pub fn run_php(file: &str) -> bool { + setup(); + let mut path = env::current_dir().expect("Could not get cwd"); + path.pop(); + path.push("target"); + path.push("debug"); + path.push(if std::env::consts::DLL_EXTENSION == "dll" { + "tests" + } else { + "libtests" + }); + path.set_extension(std::env::consts::DLL_EXTENSION); + let output = Command::new("php") + .arg(format!("-dextension={}", path.to_str().unwrap())) + .arg("-dassert.active=1") + .arg("-dassert.exception=1") + .arg("-dzend.assertions=1") + .arg(format!("src/integration/{file}")) + .output() + .expect("failed to run php file"); + if output.status.success() { + true + } else { + panic!( + " + status: {} + stdout: {} + stderr: {} + ", + output.status, + String::from_utf8(output.stdout).unwrap(), + String::from_utf8(output.stderr).unwrap() + ); + } + } +} diff --git a/tests/src/integration/nullable.rs b/tests/src/integration/nullable.rs deleted file mode 100644 index 5fc911722b..0000000000 --- a/tests/src/integration/nullable.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn nullable_works() { - assert!(crate::integration::run_php("nullable.php")); -} diff --git a/tests/src/integration/nullable/mod.rs b/tests/src/integration/nullable/mod.rs new file mode 100644 index 0000000000..b5529e3e98 --- /dev/null +++ b/tests/src/integration/nullable/mod.rs @@ -0,0 +1,18 @@ +use ext_php_rs::prelude::*; + +#[php_function] +pub fn test_nullable(a: Option) -> Option { + a +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder.function(wrap_function!(test_nullable)) +} + +#[cfg(test)] +mod tests { + #[test] + fn nullable_works() { + assert!(crate::integration::test::run_php("nullable/nullable.php")); + } +} diff --git a/tests/src/integration/nullable.php b/tests/src/integration/nullable/nullable.php similarity index 77% rename from tests/src/integration/nullable.php rename to tests/src/integration/nullable/nullable.php index f3ba326818..731a760b09 100644 --- a/tests/src/integration/nullable.php +++ b/tests/src/integration/nullable/nullable.php @@ -1,6 +1,4 @@ i32 { + a +} + +#[php_function] +pub fn test_number_unsigned(a: u32) -> u32 { + a +} + +#[php_function] +pub fn test_number_float(a: f32) -> f32 { + a +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder + .function(wrap_function!(test_number_signed)) + .function(wrap_function!(test_number_unsigned)) + .function(wrap_function!(test_number_float)) +} + +#[cfg(test)] +mod tests { + #[test] + fn number_works() { + assert!(crate::integration::test::run_php("number/number.php")); + } +} diff --git a/tests/src/integration/number.php b/tests/src/integration/number/number.php similarity index 92% rename from tests/src/integration/number.php rename to tests/src/integration/number/number.php index 822faf93c3..48dc8192d0 100644 --- a/tests/src/integration/number.php +++ b/tests/src/integration/number/number.php @@ -1,6 +1,6 @@ &mut ZendObject { + a +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder.function(wrap_function!(test_object)) +} + +#[cfg(test)] +mod tests { + #[test] + fn object_works() { + assert!(crate::integration::test::run_php("object/object.php")); + } +} diff --git a/tests/src/integration/object.php b/tests/src/integration/object/object.php similarity index 100% rename from tests/src/integration/object.php rename to tests/src/integration/object/object.php diff --git a/tests/src/integration/string.rs b/tests/src/integration/string.rs deleted file mode 100644 index 8f95dd5df4..0000000000 --- a/tests/src/integration/string.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn string_works() { - assert!(crate::integration::run_php("string.php")); -} diff --git a/tests/src/integration/string/mod.rs b/tests/src/integration/string/mod.rs new file mode 100644 index 0000000000..a677ff5ef9 --- /dev/null +++ b/tests/src/integration/string/mod.rs @@ -0,0 +1,25 @@ +use ext_php_rs::prelude::*; + +#[php_function] +pub fn test_str(a: &str) -> &str { + a +} + +#[php_function] +pub fn test_string(a: String) -> String { + a +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder + .function(wrap_function!(test_str)) + .function(wrap_function!(test_string)) +} + +#[cfg(test)] +mod tests { + #[test] + fn string_works() { + assert!(crate::integration::test::run_php("string/string.php")); + } +} diff --git a/tests/src/integration/string.php b/tests/src/integration/string/string.php similarity index 76% rename from tests/src/integration/string.php rename to tests/src/integration/string/string.php index 63e111f56d..d7ab32b0a4 100644 --- a/tests/src/integration/string.php +++ b/tests/src/integration/string/string.php @@ -1,6 +1,4 @@ [['string'], 'string'], 'test_string' => [['string'], 'string'], @@ -39,4 +37,4 @@ function toStr(ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionTyp $tParam = toStr($param->getType()); assert($tParam === $args[$idx], "Wrong arg type $idx of $func, expected {$args[$idx]}, got $tParam"); } -} \ No newline at end of file +} diff --git a/tests/src/integration/variadic_args.rs b/tests/src/integration/variadic_args.rs deleted file mode 100644 index a6c0cfa882..0000000000 --- a/tests/src/integration/variadic_args.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn test_variadic_args() { - assert!(crate::integration::run_php("variadic_args.php")); -} diff --git a/tests/src/integration/variadic_args/mod.rs b/tests/src/integration/variadic_args/mod.rs new file mode 100644 index 0000000000..2bc58877ea --- /dev/null +++ b/tests/src/integration/variadic_args/mod.rs @@ -0,0 +1,34 @@ +//! Rust type &[&Zval] must be converted to Vec because of +//! lifetime hell. + +use ext_php_rs::{prelude::*, types::Zval}; + +#[php_function] +pub fn test_variadic_args(params: &[&Zval]) -> Vec { + params.iter().map(|x| x.shallow_clone()).collect() +} + +#[php_function] +pub fn test_variadic_add_required(number: u32, numbers: &[&Zval]) -> u32 { + number + + numbers + .iter() + .map(|x| u32::try_from(x.long().unwrap()).unwrap()) + .sum::() +} + +pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { + builder + .function(wrap_function!(test_variadic_args)) + .function(wrap_function!(test_variadic_add_required)) +} + +#[cfg(test)] +mod tests { + #[test] + fn test_variadic_args() { + assert!(crate::integration::test::run_php( + "variadic_args/variadic_args.php" + )); + } +} diff --git a/tests/src/integration/variadic_args.php b/tests/src/integration/variadic_args/variadic_args.php similarity index 96% rename from tests/src/integration/variadic_args.php rename to tests/src/integration/variadic_args/variadic_args.php index 28dc2104aa..98a9c812c0 100644 --- a/tests/src/integration/variadic_args.php +++ b/tests/src/integration/variadic_args/variadic_args.php @@ -1,6 +1,6 @@ &str { - a -} - -#[php_function] -pub fn test_string(a: String) -> String { - a -} - -#[php_function] -pub fn test_bool(a: bool) -> bool { - a -} - -#[php_function] -pub fn test_number_signed(a: i32) -> i32 { - a -} - -#[php_function] -pub fn test_number_unsigned(a: u32) -> u32 { - a -} - -#[php_function] -pub fn test_number_float(a: f32) -> f32 { - a -} - -#[php_function] -pub fn test_array(a: Vec) -> Vec { - a -} - -#[php_function] -pub fn test_array_assoc(a: HashMap) -> HashMap { - a -} - -#[php_function] -pub fn test_binary(a: Binary) -> Binary { - a -} - -#[php_function] -pub fn test_nullable(a: Option) -> Option { - a -} - -#[php_function] -pub fn test_object(a: &mut ZendObject) -> &mut ZendObject { - a -} - -// GLOBALS -#[php_function] -pub fn test_globals_http_get() -> ZBox { - ProcessGlobals::get().http_get_vars().to_owned() -} - -#[php_function] -pub fn test_globals_http_post() -> ZBox { - ProcessGlobals::get().http_post_vars().to_owned() -} - -#[php_function] -pub fn test_globals_http_cookie() -> ZBox { - ProcessGlobals::get().http_cookie_vars().to_owned() -} - -#[php_function] -pub fn test_globals_http_server() -> ZBox { - ProcessGlobals::get().http_server_vars().unwrap().to_owned() -} - -#[php_function] -pub fn test_globals_http_request() -> ZBox { - ProcessGlobals::get() - .http_request_vars() - .unwrap() - .to_owned() -} - -#[php_function] -pub fn test_globals_http_files() -> ZBox { - ProcessGlobals::get().http_files_vars().to_owned() -} - -#[php_function] -pub fn test_closure() -> Closure { - Closure::wrap(Box::new(|a| a) as Box String>) -} - -#[php_function] -pub fn test_closure_once(a: String) -> Closure { - Closure::wrap_once(Box::new(move || a) as Box String>) -} - -#[php_function] -pub fn test_callable(call: ZendCallable, a: String) -> Zval { - call.try_call(vec![&a]).expect("Failed to call function") -} - -#[php_function] -pub fn iter_next(ht: &ZendHashTable) -> Vec { - ht.iter() - .flat_map(|(k, v)| [key_to_zval(k), v.shallow_clone()]) - .collect() -} - -#[php_function] -pub fn iter_back(ht: &ZendHashTable) -> Vec { - ht.iter() - .rev() - .flat_map(|(k, v)| [key_to_zval(k), v.shallow_clone()]) - .collect() -} - -#[php_function] -pub fn iter_next_back(ht: &ZendHashTable, modulus: usize) -> Vec> { - let mut result = Vec::with_capacity(ht.len()); - let mut iter = ht.iter(); - - for i in 0..ht.len() + modulus { - let entry = if i % modulus == 0 { - iter.next_back() - } else { - iter.next() - }; - - if let Some((k, v)) = entry { - result.push(Some(key_to_zval(k))); - result.push(Some(v.shallow_clone())); - } else { - result.push(None); - } - } - - result -} - -fn key_to_zval(key: ArrayKey) -> Zval { - match key { - ArrayKey::String(s) => { - let mut zval = Zval::new(); - let _ = zval.set_string(s.as_str(), false); - zval - } - ArrayKey::Long(l) => { - let mut zval = Zval::new(); - zval.set_long(l); - zval - } - } -} - -// Rust type &[&Zval] must be converted because to Vec because of -// lifetime hell. -#[php_function] -pub fn test_variadic_args(params: &[&Zval]) -> Vec { - params.iter().map(|x| x.shallow_clone()).collect() -} - -#[php_function] -pub fn test_variadic_add_required(number: u32, numbers: &[&Zval]) -> u32 { - number - + numbers - .iter() - .map(|x| u32::try_from(x.long().unwrap()).unwrap()) - .sum::() -} - -#[php_class] -pub struct TestClass { - string: String, - number: i32, - #[php(prop)] - boolean: bool, -} - -#[php_impl] -impl TestClass { - #[php(getter)] - pub fn get_string(&self) -> String { - self.string.to_string() - } - - #[php(setter)] - pub fn set_string(&mut self, string: String) { - self.string = string; - } - - #[php(getter)] - pub fn get_number(&self) -> i32 { - self.number - } - - #[php(setter)] - pub fn set_number(&mut self, number: i32) { - self.number = number; - } - - pub fn static_call(name: String) -> String { - format!("Hello {name}") - } -} - -#[php_function] -pub fn test_class(string: String, number: i32) -> TestClass { - TestClass { - string, - number, - boolean: true, - } -} - -#[php_class] -pub struct MagicMethod(i64); - -#[php_impl] -impl MagicMethod { - pub fn __construct() -> Self { - Self(0) - } - - pub fn __destruct(&self) {} - - pub fn __call(&self, name: String, _arguments: HashMap) -> Zval { - let mut z = Zval::new(); - if name == "callMagicMethod" { - let s = "Hello".to_string(); - - let _ = z.set_string(s.as_str(), false); - z - } else { - z.set_null(); - z - } - } - - pub fn __call_static(name: String, arguments: HashMap) -> Zval { - let mut zval = Zval::new(); - if name == "callStaticSomeMagic" { - let concat_args = format!( - "Hello from static call {}", - arguments - .iter() - .filter(|(_, v)| v.is_long()) - .map(|(_, s)| s.long().unwrap()) - .collect::>() - .iter() - .sum::() - ); - - let _ = zval.set_string(&concat_args, false); - zval - } else { - zval.set_null(); - zval - } - } - - pub fn __get(&self, name: String) -> Zval { - let mut v = Zval::new(); - v.set_null(); - if name == "count" { - v.set_long(self.0); - } - - v - } - - pub fn __set(&mut self, prop_name: String, val: &Zval) { - if val.is_long() && prop_name == "count" { - self.0 = val.long().unwrap(); - } - } - - pub fn __isset(&self, prop_name: String) -> bool { - "count" == prop_name - } - - pub fn __unset(&mut self, prop_name: String) { - if prop_name == "count" { - self.0 = 0; - } - } - - pub fn __to_string(&self) -> String { - self.0.to_string() - } - - pub fn __invoke(&self, n: i64) -> i64 { - self.0 + n - } - - pub fn __debug_info(&self) -> HashMap { - let mut h: HashMap = HashMap::new(); - let mut z = Zval::new(); - z.set_long(self.0); - h.insert("count".to_string(), z); - - h - } -} +mod integration; #[php_module] pub fn build_module(module: ModuleBuilder) -> ModuleBuilder { - module - .class::() - .class::() - .function(wrap_function!(test_str)) - .function(wrap_function!(test_string)) - .function(wrap_function!(test_bool)) - .function(wrap_function!(test_number_signed)) - .function(wrap_function!(test_number_unsigned)) - .function(wrap_function!(test_number_float)) - .function(wrap_function!(test_array)) - .function(wrap_function!(test_array_assoc)) - .function(wrap_function!(test_binary)) - .function(wrap_function!(test_nullable)) - .function(wrap_function!(test_object)) - .function(wrap_function!(test_globals_http_get)) - .function(wrap_function!(test_globals_http_post)) - .function(wrap_function!(test_globals_http_cookie)) - .function(wrap_function!(test_globals_http_server)) - .function(wrap_function!(test_globals_http_request)) - .function(wrap_function!(test_globals_http_files)) - .function(wrap_function!(test_closure)) - .function(wrap_function!(test_closure_once)) - .function(wrap_function!(test_callable)) - .function(wrap_function!(iter_next)) - .function(wrap_function!(iter_back)) - .function(wrap_function!(iter_next_back)) - .function(wrap_function!(test_class)) - .function(wrap_function!(test_variadic_args)) - .function(wrap_function!(test_variadic_add_required)) -} - -#[cfg(test)] -mod integration { - use std::env; + let mut module = integration::array::build_module(module); + module = integration::binary::build_module(module); + module = integration::bool::build_module(module); + module = integration::callable::build_module(module); + module = integration::class::build_module(module); + module = integration::closure::build_module(module); + module = integration::globals::build_module(module); + module = integration::iterator::build_module(module); + module = integration::magic_method::build_module(module); + module = integration::nullable::build_module(module); + module = integration::number::build_module(module); + module = integration::object::build_module(module); + module = integration::string::build_module(module); + module = integration::variadic_args::build_module(module); - use std::process::Command; - use std::sync::Once; - - static BUILD: Once = Once::new(); - - fn setup() { - BUILD.call_once(|| { - assert!(Command::new("cargo") - .arg("build") - .output() - .expect("failed to build extension") - .status - .success()); - }); - } - - pub fn run_php(file: &str) -> bool { - setup(); - let mut path = env::current_dir().expect("Could not get cwd"); - path.pop(); - path.push("target"); - path.push("debug"); - path.push(if std::env::consts::DLL_EXTENSION == "dll" { - "tests" - } else { - "libtests" - }); - path.set_extension(std::env::consts::DLL_EXTENSION); - let output = Command::new("php") - .arg(format!("-dextension={}", path.to_str().unwrap())) - .arg("-dassert.active=1") - .arg("-dassert.exception=1") - .arg("-dzend.assertions=1") - .arg(format!("src/integration/{file}")) - .output() - .expect("failed to run php file"); - if output.status.success() { - true - } else { - panic!( - " - status: {} - stdout: {} - stderr: {} - ", - output.status, - String::from_utf8(output.stdout).unwrap(), - String::from_utf8(output.stderr).unwrap() - ); - } - } - - mod array; - mod binary; - mod bool; - mod callable; - mod class; - mod closure; - mod globals; - mod iterator; - mod magic_method; - mod nullable; - mod number; - mod object; - mod string; - mod types; - mod variadic_args; + module }