From bc4a8d2b6beb1c062ea9a2e08f05b27d666c728e Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Fri, 2 Apr 2021 16:44:24 +0800 Subject: [PATCH 01/20] Adjust macros. --- .github/workflows/ci.yml | 1 + Cargo.toml | 4 +- examples/hello-class/src/lib.rs | 8 +- examples/hello/src/lib.rs | 20 ++-- examples/mini-curl/src/lib.rs | 4 +- phper-alloc/src/lib.rs | 150 +++++++++++++++--------------- phper-macros/src/inner.rs | 26 ++---- phper-macros/src/lib.rs | 69 ++++---------- phper-macros/tests/integration.rs | 14 +-- phper/src/lib.rs | 4 +- phper/src/zend/compile.rs | 6 +- phper/src/zend/ini.rs | 6 +- phper/src/zend/types.rs | 54 ++++++----- rustfmt.toml | 2 +- 14 files changed, 160 insertions(+), 208 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adb25c7f..4712348f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,7 @@ jobs: - "7.2" - "7.3" - "7.4" + - "8.0" runs-on: ${{ matrix.os }} steps: diff --git a/Cargo.toml b/Cargo.toml index 605e0727..06d743a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,6 @@ members = [ # internal "examples/hello", - "examples/hello-class", - "examples/mini-curl", +# "examples/hello-class", +# "examples/mini-curl", ] diff --git a/examples/hello-class/src/lib.rs b/examples/hello-class/src/lib.rs index 85245ca6..e0939154 100644 --- a/examples/hello-class/src/lib.rs +++ b/examples/hello-class/src/lib.rs @@ -1,6 +1,6 @@ use phper::{ - c_str_ptr, php_fn, php_function, php_minfo, php_minfo_function, php_minit, php_minit_function, - php_mshutdown, php_mshutdown_function, php_rinit, php_rinit_function, php_rshutdown, + c_str_ptr, php_minfo_function, php_minit_function, + php_mshutdown_function, php_rinit_function, php_rshutdown_function, sys::{ php_info_print_table_end, php_info_print_table_row, php_info_print_table_start, @@ -13,7 +13,7 @@ use phper::{ modules::{ModuleArgs, ModuleEntry, ModuleEntryBuilder}, types::{ClassEntry, ExecuteData, SetVal, Value}, }, - zend_get_module, + php_get_module, }; use std::{os::raw::c_char, ptr::null}; @@ -142,7 +142,7 @@ static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( .info_func(php_minfo!(module_info)) .build(); -#[zend_get_module] +#[php_get_module] pub fn get_module() -> &'static ModuleEntry { &MODULE_ENTRY } diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 2bb7f59f..96835755 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -1,7 +1,6 @@ use phper::{ - c_str_ptr, php_fn, php_function, php_minfo, php_minfo_function, php_minit, php_minit_function, - php_mshutdown, php_mshutdown_function, php_rinit, php_rinit_function, php_rshutdown, - php_rshutdown_function, + c_str_ptr, php_function, php_get_module, php_minfo_function, php_minit_function, + php_mshutdown_function, php_rinit_function, php_rshutdown_function, sys::{ php_info_print_table_end, php_info_print_table_row, php_info_print_table_start, zend_function_entry, OnUpdateBool, PHP_INI_SYSTEM, @@ -13,7 +12,6 @@ use phper::{ modules::{ModuleArgs, ModuleEntry, ModuleEntryBuilder}, types::{ExecuteData, SetVal}, }, - zend_get_module, }; static SIMPLE_ENABLE: ModuleGlobals = ModuleGlobals::new(false); @@ -77,7 +75,7 @@ static ARG_INFO_SAY_HELLO: MultiInternalArgInfo<1> = static FUNCTION_ENTRIES: FunctionEntries<1> = FunctionEntries::new([zend_function_entry { fname: c_str_ptr!("say_hello"), - handler: Some(php_fn!(say_hello)), + handler: Some(say_hello), arg_info: ARG_INFO_SAY_HELLO.as_ptr(), num_args: 2, flags: 0, @@ -88,14 +86,14 @@ static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( c_str_ptr!(env!("CARGO_PKG_VERSION")), ) .functions(FUNCTION_ENTRIES.as_ptr()) -.module_startup_func(php_minit!(module_init)) -.module_shutdown_func(php_mshutdown!(module_shutdown)) -.request_startup_func(php_rinit!(request_init)) -.request_shutdown_func(php_rshutdown!(request_shutdown)) -.info_func(php_minfo!(module_info)) +.module_startup_func(module_init) +.module_shutdown_func(module_shutdown) +.request_startup_func(request_init) +.request_shutdown_func(request_shutdown) +.info_func(module_info) .build(); -#[zend_get_module] +#[php_get_module] pub fn get_module() -> &'static ModuleEntry { &MODULE_ENTRY } diff --git a/examples/mini-curl/src/lib.rs b/examples/mini-curl/src/lib.rs index 8b4bb259..954d2b76 100644 --- a/examples/mini-curl/src/lib.rs +++ b/examples/mini-curl/src/lib.rs @@ -14,7 +14,7 @@ use phper::{ modules::{ModuleArgs, ModuleEntry, ModuleEntryBuilder}, types::{ClassEntry, ExecuteData, ReturnValue, SetVal, Value}, }, - zend_get_module, + php_get_module, }; static MINI_CURL_CE: ClassEntry = ClassEntry::new(); @@ -155,7 +155,7 @@ static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( .info_func(php_minfo!(module_info)) .build(); -#[zend_get_module] +#[php_get_module] pub fn get_module() -> &'static ModuleEntry { &MODULE_ENTRY } diff --git a/phper-alloc/src/lib.rs b/phper-alloc/src/lib.rs index d80f520a..7e9bae32 100644 --- a/phper-alloc/src/lib.rs +++ b/phper-alloc/src/lib.rs @@ -1,80 +1,78 @@ -#![feature(allocator_api)] #![warn(rust_2018_idioms, clippy::dbg_macro, clippy::print_stdout)] -use phper_sys::{_efree, _emalloc}; -use std::{ - alloc::{AllocError, AllocRef, Layout}, - ptr::{slice_from_raw_parts_mut, NonNull}, -}; +/// The Box which use php `emalloc` and `efree` to manage memory. +/// TODO now feature `allocator_api` is still unstable, using global allocator instead. +pub type EBox = Box; -pub type EBox = Box; -pub type EVec = Vec; +/// The Vec which use php `emalloc` and `efree` to manage memory. +/// TODO now feature `allocator_api` is still unstable, using global allocator instead. +pub type EVec = Vec; -pub struct Allocator { - #[cfg(phper_debug)] - zend_filename: *const std::os::raw::c_char, - #[cfg(phper_debug)] - zend_lineno: u32, - #[cfg(phper_debug)] - zend_orig_filename: *const std::os::raw::c_char, - #[cfg(phper_debug)] - zend_orig_lineno: u32, -} - -impl Allocator { - pub const fn new( - #[cfg(phper_debug)] zend_filename: *const std::os::raw::c_char, - #[cfg(phper_debug)] zend_lineno: u32, - #[cfg(phper_debug)] zend_orig_filename: *const std::os::raw::c_char, - #[cfg(phper_debug)] zend_orig_lineno: u32, - ) -> Self { - Self { - #[cfg(phper_debug)] - zend_filename, - #[cfg(phper_debug)] - zend_lineno, - #[cfg(phper_debug)] - zend_orig_filename, - #[cfg(phper_debug)] - zend_orig_lineno, - } - } -} - -unsafe impl AllocRef for Allocator { - fn alloc(&self, layout: Layout) -> Result, AllocError> { - unsafe { - #[cfg(phper_debug)] - let ptr = _emalloc( - layout.size(), - self.zend_filename, - self.zend_lineno, - self.zend_orig_filename, - self.zend_orig_lineno, - ); - #[cfg(not(phper_debug))] - let ptr = _emalloc(layout.size()); - - if ptr.is_null() { - Err(AllocError) - } else { - let ptr = slice_from_raw_parts_mut(ptr.cast(), layout.size()); - Ok(NonNull::new_unchecked(ptr)) - } - } - } - - unsafe fn dealloc(&self, ptr: NonNull, _layout: Layout) { - // Not the correct position of `efree`, but can work!. - #[cfg(phper_debug)] - _efree( - ptr.as_ptr().cast(), - self.zend_filename, - self.zend_lineno, - self.zend_orig_filename, - self.zend_orig_lineno, - ); - #[cfg(not(phper_debug))] - _efree(ptr.as_ptr().cast()); - } -} +// pub struct Allocator { +// #[cfg(phper_debug)] +// zend_filename: *const std::os::raw::c_char, +// #[cfg(phper_debug)] +// zend_lineno: u32, +// #[cfg(phper_debug)] +// zend_orig_filename: *const std::os::raw::c_char, +// #[cfg(phper_debug)] +// zend_orig_lineno: u32, +// } +// +// impl Allocator { +// pub const fn new( +// #[cfg(phper_debug)] zend_filename: *const std::os::raw::c_char, +// #[cfg(phper_debug)] zend_lineno: u32, +// #[cfg(phper_debug)] zend_orig_filename: *const std::os::raw::c_char, +// #[cfg(phper_debug)] zend_orig_lineno: u32, +// ) -> Self { +// Self { +// #[cfg(phper_debug)] +// zend_filename, +// #[cfg(phper_debug)] +// zend_lineno, +// #[cfg(phper_debug)] +// zend_orig_filename, +// #[cfg(phper_debug)] +// zend_orig_lineno, +// } +// } +// } +// +// unsafe impl AllocRef for Allocator { +// fn alloc(&self, layout: Layout) -> Result, AllocError> { +// unsafe { +// #[cfg(phper_debug)] +// let ptr = _emalloc( +// layout.size(), +// self.zend_filename, +// self.zend_lineno, +// self.zend_orig_filename, +// self.zend_orig_lineno, +// ); +// #[cfg(not(phper_debug))] +// let ptr = _emalloc(layout.size()); +// +// if ptr.is_null() { +// Err(AllocError) +// } else { +// let ptr = slice_from_raw_parts_mut(ptr.cast(), layout.size()); +// Ok(NonNull::new_unchecked(ptr)) +// } +// } +// } +// +// unsafe fn dealloc(&self, ptr: NonNull, _layout: Layout) { +// // Not the correct position of `efree`, but can work!. +// #[cfg(phper_debug)] +// _efree( +// ptr.as_ptr().cast(), +// self.zend_filename, +// self.zend_lineno, +// self.zend_orig_filename, +// self.zend_orig_lineno, +// ); +// #[cfg(not(phper_debug))] +// _efree(ptr.as_ptr().cast()); +// } +// } diff --git a/phper-macros/src/inner.rs b/phper-macros/src/inner.rs index 8e320c1a..4e7f2297 100644 --- a/phper-macros/src/inner.rs +++ b/phper-macros/src/inner.rs @@ -10,20 +10,16 @@ pub(crate) fn rename(input: TokenStream, prefix: impl ToString) -> TokenStream { result.into() } -pub(crate) fn hook_fn(input: TokenStream, prefix: impl ToString) -> TokenStream { +pub(crate) fn hook_fn(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as ItemFn); - let name = prefix.to_string() + &input.sig.ident.to_string(); - let name = Ident::new(&name, input.sig.ident.span()); + let name = &input.sig.ident; let inputs = &input.sig.inputs; let ret = &input.sig.output; let body = &input.block; let attrs = &input.attrs; let result = quote! { - #[allow(dead_code)] - #input - #(#attrs)* extern "C" fn #name(type_: ::std::os::raw::c_int, module_number: ::std::os::raw::c_int) -> ::std::os::raw::c_int { fn internal(#inputs) #ret { @@ -43,20 +39,16 @@ pub(crate) fn hook_fn(input: TokenStream, prefix: impl ToString) -> TokenStream result.into() } -pub(crate) fn info_fn(input: TokenStream, prefix: impl ToString) -> TokenStream { +pub(crate) fn info_fn(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as ItemFn); - let name = prefix.to_string() + &input.sig.ident.to_string(); - let name = Ident::new(&name, input.sig.ident.span()); + let name = &input.sig.ident; let inputs = &input.sig.inputs; let ret = &input.sig.output; let body = &input.block; let attrs = &input.attrs; let result = quote! { - #[allow(dead_code)] - #input - #(#attrs)* extern "C" fn #name(zend_module: *mut ::phper::sys::zend_module_entry) { fn internal(#inputs) #ret { @@ -77,17 +69,11 @@ pub(crate) fn php_function(_attr: TokenStream, input: TokenStream) -> TokenStrea let vis = &input.vis; let ret = &input.sig.output; let inputs = &input.sig.inputs; - let name = Ident::new( - &format!("zif_{}", &input.sig.ident.to_string()), - input.sig.ident.span().clone(), - ); + let name = &input.sig.ident; let body = &input.block; let attrs = &input.attrs; let result = quote! { - #[allow(dead_code)] - #input - #(#attrs)* #vis extern "C" fn #name( execute_data: *mut ::phper::sys::zend_execute_data, @@ -107,7 +93,7 @@ pub(crate) fn php_function(_attr: TokenStream, input: TokenStream) -> TokenStrea result.into() } -pub(crate) fn zend_get_module(_attr: TokenStream, input: TokenStream) -> TokenStream { +pub(crate) fn php_get_module(_attr: TokenStream, input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as ItemFn); let vis = &input.vis; diff --git a/phper-macros/src/lib.rs b/phper-macros/src/lib.rs index c75a00e8..9ecbc779 100644 --- a/phper-macros/src/lib.rs +++ b/phper-macros/src/lib.rs @@ -20,82 +20,47 @@ pub fn ebox(input: TokenStream) -> TokenStream { alloc::ebox(input) } -#[proc_macro] -pub fn php_fn(input: TokenStream) -> TokenStream { - rename(input, "zif_") -} - -#[proc_macro] -pub fn php_mn(input: TokenStream) -> TokenStream { - rename(input, "zim_") -} - -#[proc_macro] -pub fn php_minit(input: TokenStream) -> TokenStream { - rename(input, "zm_startup_") -} - -#[proc_macro] -pub fn php_mshutdown(input: TokenStream) -> TokenStream { - rename(input, "zm_shutdown_") -} - -#[proc_macro] -pub fn php_rinit(input: TokenStream) -> TokenStream { - rename(input, "zm_activate_") -} - -#[proc_macro] -pub fn php_rshutdown(input: TokenStream) -> TokenStream { - rename(input, "zm_deactivate_") -} - -#[proc_macro] -pub fn php_minfo(input: TokenStream) -> TokenStream { - rename(input, "zm_info_") -} - -#[proc_macro] -pub fn php_ginfo(input: TokenStream) -> TokenStream { - rename(input, "zm_globals_ctor_") -} - -#[proc_macro] -pub fn php_gshutdown(input: TokenStream) -> TokenStream { - rename(input, "zm_globals_dtor_") -} - #[proc_macro_attribute] pub fn php_function(attr: TokenStream, input: TokenStream) -> TokenStream { inner::php_function(attr, input) } #[proc_macro_attribute] -pub fn zend_get_module(attr: TokenStream, input: TokenStream) -> TokenStream { - inner::zend_get_module(attr, input) +pub fn php_get_module(attr: TokenStream, input: TokenStream) -> TokenStream { + inner::php_get_module(attr, input) } #[proc_macro_attribute] pub fn php_minit_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input, "zm_startup_") + hook_fn(input) } #[proc_macro_attribute] pub fn php_mshutdown_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input, "zm_shutdown_") + hook_fn(input) } #[proc_macro_attribute] pub fn php_rinit_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input, "zm_activate_") + hook_fn(input) } #[proc_macro_attribute] pub fn php_rshutdown_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input, "zm_deactivate_") + hook_fn(input) +} + +#[proc_macro_attribute] +pub fn php_ginit_function(_attr: TokenStream, input: TokenStream) -> TokenStream { + hook_fn(input) +} + +#[proc_macro_attribute] +pub fn php_gshutdown_function(_attr: TokenStream, input: TokenStream) -> TokenStream { + hook_fn(input) } #[proc_macro_attribute] pub fn php_minfo_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - info_fn(input, "zm_info_") + info_fn(input) } diff --git a/phper-macros/tests/integration.rs b/phper-macros/tests/integration.rs index dff19cc4..845053b2 100644 --- a/phper-macros/tests/integration.rs +++ b/phper-macros/tests/integration.rs @@ -1,4 +1,4 @@ -use phper_macros::{c_str, c_str_ptr, php_fn, php_mn}; +use phper_macros::{c_str, c_str_ptr}; use std::ffi::CStr; #[test] @@ -15,15 +15,3 @@ fn test_c_str() { fn test_c_str_ptr() { assert_eq!(c_str_ptr!("foo"), "foo\0".as_ptr().cast()); } - -#[test] -fn test_php_fn() { - let php_fn!(a): i32 = 1; - assert_eq!(zif_a, 1); -} - -#[test] -fn test_php_mn() { - let php_mn!(a): i32 = 1; - assert_eq!(zim_a, 1); -} diff --git a/phper/src/lib.rs b/phper/src/lib.rs index 3859428e..7cb43fe9 100644 --- a/phper/src/lib.rs +++ b/phper/src/lib.rs @@ -1,6 +1,4 @@ -#![feature(min_const_generics)] -#![feature(const_fn_fn_ptr_basics)] -#![feature(const_fn_transmute)] +#![feature(const_fn_transmute, const_fn_fn_ptr_basics)] #![warn(rust_2018_idioms, clippy::dbg_macro, clippy::print_stdout)] /*! diff --git a/phper/src/zend/compile.rs b/phper/src/zend/compile.rs index 5d5ea3ed..52ecc2d5 100644 --- a/phper/src/zend/compile.rs +++ b/phper/src/zend/compile.rs @@ -1,5 +1,6 @@ use crate::sys::{ - zend_internal_arg_info, zend_uchar, ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, + zend_internal_arg_info, zend_type, zend_uchar, ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, + ZEND_ACC_PUBLIC, }; use std::{cell::Cell, os::raw::c_char}; @@ -43,6 +44,7 @@ pub const fn create_zend_arg_info( pass_by_ref: bool, ) -> zend_internal_arg_info { #[cfg(any( + phper_php_version = "8.0", phper_php_version = "7.4", phper_php_version = "7.3", phper_php_version = "7.2" @@ -50,7 +52,7 @@ pub const fn create_zend_arg_info( { zend_internal_arg_info { name, - type_: 0, + type_: 0 as zend_type, pass_by_reference: pass_by_ref as zend_uchar, is_variadic: 0, } diff --git a/phper/src/zend/ini.rs b/phper/src/zend/ini.rs index ec757d78..f1207866 100644 --- a/phper/src/zend/ini.rs +++ b/phper/src/zend/ini.rs @@ -56,7 +56,11 @@ pub const fn create_ini_entry_ex( modifiable: u32, arg2: *mut c_void, ) -> zend_ini_entry_def { - #[cfg(any(phper_php_version = "7.4", phper_php_version = "7.3"))] + #[cfg(any( + phper_php_version = "8.0", + phper_php_version = "7.4", + phper_php_version = "7.3", + ))] let (modifiable, name_length) = (modifiable as std::os::raw::c_uchar, name.len() as u16); #[cfg(any( phper_php_version = "7.2", diff --git a/phper/src/zend/types.rs b/phper/src/zend/types.rs index 8626bf5e..19d616b2 100644 --- a/phper/src/zend/types.rs +++ b/phper/src/zend/types.rs @@ -448,29 +448,41 @@ fn zend_parse_fixed_parameters( assert!(parameters.len() <= 20); let type_spec = format!("{}\0", type_spec); + #[cfg(any( + phper_php_version = "7.4", + phper_php_version = "7.3", + phper_php_version = "7.2", + phper_php_version = "7.1", + phper_php_version = "7.0", + ))] + let num_args = num_args as c_int; + + #[cfg(phper_php_version = "8.0")] + let num_args = num_args as u32; + #[rustfmt::skip] let b = match parameters.len() { - 0 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters), - 1 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0), - 2 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1), - 3 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2), - 4 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3), - 5 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4), - 6 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5), - 7 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6), - 8 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7), - 9 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8), - 10 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9), - 11 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), - 12 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), - 13 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), - 14 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), - 15 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14), - 16 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), - 17 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), - 18 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), - 19 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18), - 20 => call_zend_parse_parameters!(num_args as c_int, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), + 0 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters), + 1 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0), + 2 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1), + 3 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2), + 4 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3), + 5 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4), + 6 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5), + 7 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6), + 8 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7), + 9 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8), + 10 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9), + 11 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + 12 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), + 13 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), + 14 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), + 15 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14), + 16 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + 17 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), + 18 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), + 19 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18), + 20 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), _ => unreachable!(), }; diff --git a/rustfmt.toml b/rustfmt.toml index 7d2cf549..d9ba5fdb 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1 @@ -merge_imports = true +imports_granularity = "Crate" \ No newline at end of file From 5b889238426fd11e952189746d8db7627f55c65a Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Fri, 2 Apr 2021 17:15:26 +0800 Subject: [PATCH 02/20] Rename ini entry. --- examples/hello/src/lib.rs | 8 ++++---- phper-macros/src/inner.rs | 10 +--------- phper-macros/src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 96835755..53dc5507 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -14,9 +14,9 @@ use phper::{ }, }; -static SIMPLE_ENABLE: ModuleGlobals = ModuleGlobals::new(false); +static HELLO_ENABLE: ModuleGlobals = ModuleGlobals::new(false); -static INI_ENTRIES: IniEntries<1> = IniEntries::new([SIMPLE_ENABLE.create_ini_entry( +static INI_ENTRIES: IniEntries<1> = IniEntries::new([HELLO_ENABLE.create_ini_entry( "hello.enable", "1", Some(OnUpdateBool), @@ -53,7 +53,7 @@ fn module_info(module: &ModuleEntry) { php_info_print_table_row( 2, c_str_ptr!("hello.enable"), - if SIMPLE_ENABLE.get() { + if HELLO_ENABLE.get() { c_str_ptr!("1") } else { c_str_ptr!("0") @@ -71,7 +71,7 @@ pub fn say_hello(execute_data: &mut ExecuteData) -> impl SetVal { } static ARG_INFO_SAY_HELLO: MultiInternalArgInfo<1> = - MultiInternalArgInfo::new(1, false, [create_zend_arg_info(c_str_ptr!("name"), false)]); + MultiInternalArgInfo::new(1, false, [create_zend_arg_info(c_str_ptr!("n_ame"), false)]); static FUNCTION_ENTRIES: FunctionEntries<1> = FunctionEntries::new([zend_function_entry { fname: c_str_ptr!("say_hello"), diff --git a/phper-macros/src/inner.rs b/phper-macros/src/inner.rs index 4e7f2297..425f0a13 100644 --- a/phper-macros/src/inner.rs +++ b/phper-macros/src/inner.rs @@ -1,14 +1,6 @@ use proc_macro::TokenStream; use quote::quote; -use syn::{parse_macro_input, Ident, ItemFn, Visibility}; - -pub(crate) fn rename(input: TokenStream, prefix: impl ToString) -> TokenStream { - let input = parse_macro_input!(input as Ident); - let name = prefix.to_string() + &input.to_string(); - let name = Ident::new(&name, input.span().clone()); - let result = quote! { #name }; - result.into() -} +use syn::{parse_macro_input, ItemFn, Visibility}; pub(crate) fn hook_fn(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as ItemFn); diff --git a/phper-macros/src/lib.rs b/phper-macros/src/lib.rs index 9ecbc779..7720d132 100644 --- a/phper-macros/src/lib.rs +++ b/phper-macros/src/lib.rs @@ -2,7 +2,7 @@ mod alloc; mod inner; mod utils; -use crate::inner::{hook_fn, info_fn, rename}; +use crate::inner::{hook_fn, info_fn}; use proc_macro::TokenStream; #[proc_macro] From 1ea7015ea662000190b69da6503878c52efd5ca8 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Fri, 9 Apr 2021 00:00:59 +0800 Subject: [PATCH 03/20] Finish refactoring the base dynamic framework, prepare to clean code. --- examples/hello/Cargo.toml | 1 + examples/hello/src/lib.rs | 213 ++++++++++++------ examples/mini-curl/Cargo.toml | 4 +- examples/mini-curl/src/lib.rs | 55 ++--- phper-macros/src/inner.rs | 2 +- phper-sys/php_wrapper.c | 2 +- phper/Cargo.toml | 1 + phper/src/zend/api.rs | 79 ++++++- phper/src/zend/classes.rs | 201 +++++++++++++++++ phper/src/zend/compile.rs | 5 +- phper/src/zend/ini.rs | 45 +++- phper/src/zend/mod.rs | 3 +- phper/src/zend/modules.rs | 233 +++++++++++++++++++- phper/src/zend/{exceptions.rs => throws.rs} | 0 phper/src/zend/types.rs | 59 +++-- 15 files changed, 772 insertions(+), 131 deletions(-) create mode 100644 phper/src/zend/classes.rs rename phper/src/zend/{exceptions.rs => throws.rs} (100%) diff --git a/examples/hello/Cargo.toml b/examples/hello/Cargo.toml index 87e3263e..d650a424 100644 --- a/examples/hello/Cargo.toml +++ b/examples/hello/Cargo.toml @@ -12,6 +12,7 @@ license = "Unlicense" crate-type = ["cdylib"] [dependencies] +once_cell = "1.7.2" phper = { version = "0.2", path = "../../phper" } [dev-dependencies] diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 53dc5507..0c1e2a11 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -1,3 +1,4 @@ +use once_cell::sync::Lazy; use phper::{ c_str_ptr, php_function, php_get_module, php_minfo_function, php_minit_function, php_mshutdown_function, php_rinit_function, php_rshutdown_function, @@ -7,93 +8,173 @@ use phper::{ }, zend::{ api::{FunctionEntries, ModuleGlobals}, + classes::{Class, MethodEntity, StdClass, This}, compile::{create_zend_arg_info, MultiInternalArgInfo}, - ini::IniEntries, - modules::{ModuleArgs, ModuleEntry, ModuleEntryBuilder}, - types::{ExecuteData, SetVal}, + ini::{IniEntries, Policy}, + modules::{ + read_global_module, write_global_module, Module, ModuleArgs, ModuleEntry, + ModuleEntryBuilder, + }, + types::{ExecuteData, SetVal, Val}, }, }; +use std::{fs::OpenOptions, io::Write}; -static HELLO_ENABLE: ModuleGlobals = ModuleGlobals::new(false); +// static HELLO_ENABLE: ModuleGlobals = ModuleGlobals::new(false); +// +// static INI_ENTRIES: IniEntries<1> = IniEntries::new([HELLO_ENABLE.create_ini_entry( +// "hello.enable", +// "1", +// Some(OnUpdateBool), +// PHP_INI_SYSTEM, +// )]); +// +// #[php_minit_function] +// fn module_init(args: ModuleArgs) -> bool { +// args.register_ini_entries(&INI_ENTRIES); +// true +// } +// +// #[php_mshutdown_function] +// fn module_shutdown(args: ModuleArgs) -> bool { +// args.unregister_ini_entries(); +// true +// } +// +// #[php_rinit_function] +// fn request_init(_args: ModuleArgs) -> bool { +// true +// } +// +// #[php_rshutdown_function] +// fn request_shutdown(_args: ModuleArgs) -> bool { +// true +// } +// +// #[php_minfo_function] +// fn module_info(module: &ModuleEntry) { +// unsafe { +// php_info_print_table_start(); +// php_info_print_table_row(2, c_str_ptr!("hello.version"), (*module.as_ptr()).version); +// php_info_print_table_row( +// 2, +// c_str_ptr!("hello.enable"), +// if HELLO_ENABLE.get() { +// c_str_ptr!("1") +// } else { +// c_str_ptr!("0") +// }, +// ); +// php_info_print_table_end(); +// } +// } +// +// #[php_function] +// pub fn say_hello(execute_data: &mut ExecuteData) -> impl SetVal { +// execute_data +// .parse_parameters::<&str>() +// .map(|name| format!("Hello, {}!", name)) +// } +// +// static ARG_INFO_SAY_HELLO: MultiInternalArgInfo<1> = +// MultiInternalArgInfo::new(1, false, [create_zend_arg_info(c_str_ptr!("n_ame"), false)]); +// +// static FUNCTION_ENTRIES: FunctionEntries<1> = FunctionEntries::new([zend_function_entry { +// fname: c_str_ptr!("say_hello"), +// handler: Some(say_hello), +// arg_info: ARG_INFO_SAY_HELLO.as_ptr(), +// num_args: 2, +// flags: 0, +// }]); +// +// static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( +// c_str_ptr!(env!("CARGO_PKG_NAME")), +// c_str_ptr!(env!("CARGO_PKG_VERSION")), +// ) +// .functions(FUNCTION_ENTRIES.as_ptr()) +// .module_startup_func(module_init) +// .module_shutdown_func(module_shutdown) +// .request_startup_func(request_init) +// .request_shutdown_func(request_shutdown) +// .info_func(module_info) +// .build(); -static INI_ENTRIES: IniEntries<1> = IniEntries::new([HELLO_ENABLE.create_ini_entry( - "hello.enable", - "1", - Some(OnUpdateBool), - PHP_INI_SYSTEM, -)]); +// pub fn get_module() -> Module { +// let mut module = Module::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); +// module.on_module_init(Box::new(|_| { +// println!("Are you ok?"); +// true +// })); +// module +// } -#[php_minit_function] fn module_init(args: ModuleArgs) -> bool { - args.register_ini_entries(&INI_ENTRIES); + // append_file("module_init"); true } -#[php_mshutdown_function] fn module_shutdown(args: ModuleArgs) -> bool { - args.unregister_ini_entries(); + // append_file("module_shutdown"); true } -#[php_rinit_function] -fn request_init(_args: ModuleArgs) -> bool { +fn request_init(args: ModuleArgs) -> bool { + // append_file("request_init"); true } -#[php_rshutdown_function] -fn request_shutdown(_args: ModuleArgs) -> bool { +fn request_shutdown(args: ModuleArgs) -> bool { + // append_file("request_shutdown"); true } -#[php_minfo_function] -fn module_info(module: &ModuleEntry) { - unsafe { - php_info_print_table_start(); - php_info_print_table_row(2, c_str_ptr!("hello.version"), (*module.as_ptr()).version); - php_info_print_table_row( - 2, - c_str_ptr!("hello.enable"), - if HELLO_ENABLE.get() { - c_str_ptr!("1") - } else { - c_str_ptr!("0") - }, - ); - php_info_print_table_end(); - } -} +fn append_file(s: &str) { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .open("/tmp/hello") + .unwrap(); -#[php_function] -pub fn say_hello(execute_data: &mut ExecuteData) -> impl SetVal { - execute_data - .parse_parameters::<&str>() - .map(|name| format!("Hello, {}!", name)) + writeln!(file, "{}", s).unwrap(); } -static ARG_INFO_SAY_HELLO: MultiInternalArgInfo<1> = - MultiInternalArgInfo::new(1, false, [create_zend_arg_info(c_str_ptr!("n_ame"), false)]); - -static FUNCTION_ENTRIES: FunctionEntries<1> = FunctionEntries::new([zend_function_entry { - fname: c_str_ptr!("say_hello"), - handler: Some(say_hello), - arg_info: ARG_INFO_SAY_HELLO.as_ptr(), - num_args: 2, - flags: 0, -}]); - -static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( - c_str_ptr!(env!("CARGO_PKG_NAME")), - c_str_ptr!(env!("CARGO_PKG_VERSION")), -) -.functions(FUNCTION_ENTRIES.as_ptr()) -.module_startup_func(module_init) -.module_shutdown_func(module_shutdown) -.request_startup_func(request_init) -.request_shutdown_func(request_shutdown) -.info_func(module_info) -.build(); - -#[php_get_module] -pub fn get_module() -> &'static ModuleEntry { - &MODULE_ENTRY +fn test_func() {} + +#[no_mangle] +pub extern "C" fn get_module() -> *const ::phper::sys::zend_module_entry { + // static module: Lazy = Lazy::new(|| { + // let mut module = Module::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + // module + // }); + // unsafe { + // module.create_zend_module_entry() + // } + + let f = |module: &mut Module| { + module.set_name(env!("CARGO_PKG_NAME")); + module.set_version(env!("CARGO_PKG_VERSION")); + + module.add_ini("hello.enable", "on", Policy::All); + + module.on_module_init(module_init); + module.on_module_shutdown(module_shutdown); + module.on_request_init(request_init); + module.on_request_shutdown(request_shutdown); + + module.add_function("hello_fuck", || { + println!("Fuck you!"); + }); + module.add_function("test_func", test_func); + + let mut std_class = StdClass::new(); + std_class.add_property("foo", 100); + std_class.add_method("test1", |_: &mut This| { + println!("hello test1"); + }); + module.add_class("Test1", std_class); + }; + + f(&mut *write_global_module()); + + unsafe { read_global_module().module_entry() } } diff --git a/examples/mini-curl/Cargo.toml b/examples/mini-curl/Cargo.toml index eb8023a2..bcd92df6 100644 --- a/examples/mini-curl/Cargo.toml +++ b/examples/mini-curl/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "mini_curl" -version = "0.2.0" +name = "mini-curl" +version = "0.0.0" authors = ["jmjoy <918734043@qq.com>"] edition = "2018" publish = false diff --git a/examples/mini-curl/src/lib.rs b/examples/mini-curl/src/lib.rs index 954d2b76..959f0180 100644 --- a/examples/mini-curl/src/lib.rs +++ b/examples/mini-curl/src/lib.rs @@ -1,27 +1,3 @@ -use curl::easy::Easy; -use phper::{ - c_str_ptr, - main::php::error_doc_ref, - php_fn, php_function, php_minfo, php_minfo_function, php_minit, php_minit_function, - php_mshutdown, php_mshutdown_function, php_rinit, php_rinit_function, php_rshutdown, - php_rshutdown_function, - sys::{php_info_print_table_end, php_info_print_table_start, PHP_INI_SYSTEM}, - zend::{ - api::{FunctionEntries, FunctionEntryBuilder}, - compile::{create_zend_arg_info, MultiInternalArgInfo, Visibility}, - errors::Level, - ini::{create_ini_entry, IniEntries}, - modules::{ModuleArgs, ModuleEntry, ModuleEntryBuilder}, - types::{ClassEntry, ExecuteData, ReturnValue, SetVal, Value}, - }, - php_get_module, -}; - -static MINI_CURL_CE: ClassEntry = ClassEntry::new(); - -static INI_ENTRIES: IniEntries<1> = - IniEntries::new([create_ini_entry("curl.cainfo", "", PHP_INI_SYSTEM)]); - #[php_minit_function] fn module_init(args: ModuleArgs) -> bool { args.register_ini_entries(&INI_ENTRIES); @@ -155,7 +131,32 @@ static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( .info_func(php_minfo!(module_info)) .build(); -#[php_get_module] -pub fn get_module() -> &'static ModuleEntry { - &MODULE_ENTRY + +fn curl_init() { + +} + + +#[get_module] +pub fn get_module() -> Module { + let module = Module::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + + module.on_module_startup(|module_args: ModuleArgs| -> bool { + true + }); + module.on_module_shutdown(|module_args: ModuleArgs| -> bool { + true + }); + module.on_request_startup(|module_args: ModuleArgs| -> bool { + true + }); + module.on_request_shutdown(|module_args: ModuleArgs| -> bool { + true + }); + + module.add_ini("curl.cainfo", "???", INI::System); + + module.add_function("curl_init", curl_init); + + module } diff --git a/phper-macros/src/inner.rs b/phper-macros/src/inner.rs index 425f0a13..1d68894c 100644 --- a/phper-macros/src/inner.rs +++ b/phper-macros/src/inner.rs @@ -110,7 +110,7 @@ pub(crate) fn php_get_module(_attr: TokenStream, input: TokenStream) -> TokenStr fn internal(#inputs) #ret { #body } - let internal: fn() -> &'static ::phper::zend::modules::ModuleEntry = internal; + let internal: fn() -> &::phper::zend::modules::ModuleEntry = internal; internal().as_ptr() } }; diff --git a/phper-sys/php_wrapper.c b/phper-sys/php_wrapper.c index 17fdc357..3d50d9ed 100644 --- a/phper-sys/php_wrapper.c +++ b/phper-sys/php_wrapper.c @@ -32,4 +32,4 @@ char *phper_z_strval_p(const zval *v) { zval *phper_get_this(zend_execute_data *execute_data) { return getThis(); -} +} \ No newline at end of file diff --git a/phper/Cargo.toml b/phper/Cargo.toml index e2d72418..acd104b3 100644 --- a/phper/Cargo.toml +++ b/phper/Cargo.toml @@ -12,6 +12,7 @@ keywords = ["php", "binding", "extension"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +once_cell = "1.7.2" phper-alloc = { version = "0.2", path = "../phper-alloc" } phper-macros = { version = "0.2", path = "../phper-macros" } phper-sys = { version = "0.2", path = "../phper-sys" } diff --git a/phper/src/zend/api.rs b/phper/src/zend/api.rs index e93dcad8..3d6650d3 100644 --- a/phper/src/zend/api.rs +++ b/phper/src/zend/api.rs @@ -1,17 +1,90 @@ use crate::{ - sys::{zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, zif_handler}, + sys::{ + _zend_get_parameters_array_ex, phper_z_strval_p, zend_arg_info, zend_execute_data, + zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, zif_handler, zval, + }, zend::{ + classes::Method, compile::MultiInternalArgInfo, ini::{create_ini_entry_ex, Mh}, + types::{ExecuteData, Val, Value}, }, }; use std::{ cell::Cell, - mem::{size_of, transmute}, - os::raw::c_char, + ffi::CStr, + mem::{size_of, transmute, zeroed}, + os::raw::{c_char, c_int}, ptr::null, }; +pub struct Parameters; + +pub trait Function: Send + Sync { + fn call(&self, arguments: &mut [Val], return_value: &mut Val); +} + +impl Function for F +where + F: Fn() + Send + Sync, +{ + fn call(&self, arguments: &mut [Val], return_value: &mut Val) { + self() + } +} + +#[repr(transparent)] +pub struct FunctionEntry { + inner: zend_function_entry, +} + +pub(crate) struct FunctionEntity { + pub(crate) name: String, + pub(crate) handler: Box, +} + +pub(crate) unsafe extern "C" fn invoke( + execute_data: *mut zend_execute_data, + return_value: *mut zval, +) { + let execute_data = ExecuteData::from_mut(execute_data); + let return_value = Val::from_mut(return_value); + + let num_args = execute_data.common_num_args(); + let arg_info = execute_data.common_arg_info(); + + let last_arg_info = arg_info.offset(num_args as isize); + let handler = (*last_arg_info).name as *const Box; + let handler = handler.as_ref().expect("handler is null"); + + // TODO Do num args check + + let mut arguments = execute_data.get_parameters_array(); + + handler.call(&mut arguments, return_value); +} + +pub(crate) unsafe extern "C" fn method_invoke( + execute_data: *mut zend_execute_data, + return_value: *mut zval, +) { + let execute_data = ExecuteData::from_mut(execute_data); + let return_value = Val::from_mut(return_value); + + let num_args = execute_data.common_num_args(); + let arg_info = execute_data.common_arg_info(); + + let last_arg_info = arg_info.offset(num_args as isize); + let handler = (*last_arg_info).name as *const Box; + let handler = handler.as_ref().expect("handler is null"); + + // TODO Do num args check + + let mut arguments = execute_data.get_parameters_array(); + + handler.call(execute_data.get_this(), &mut arguments, return_value); +} + const fn function_entry_end() -> zend_function_entry { unsafe { transmute([0u8; size_of::()]) } } diff --git a/phper/src/zend/classes.rs b/phper/src/zend/classes.rs new file mode 100644 index 00000000..52501b21 --- /dev/null +++ b/phper/src/zend/classes.rs @@ -0,0 +1,201 @@ +use crate::{ + sys::{ + phper_init_class_entry_ex, zend_class_entry, zend_declare_property_long, + zend_function_entry, zend_internal_arg_info, zend_register_internal_class, zval, + }, + zend::{ + api::{invoke, method_invoke, FunctionEntry}, + compile::Visibility, + types::Val, + }, +}; +use once_cell::sync::OnceCell; +use std::{ + mem::zeroed, + os::raw::c_int, + ptr::{null, null_mut}, + sync::{ + atomic::{AtomicPtr, Ordering}, + Arc, Once, RwLock, + }, +}; + +pub trait Method: Send + Sync { + fn call(&self, this: &mut This, arguments: &mut [Val], return_value: &mut Val); +} + +impl Method for F +where + F: Fn(&mut This) + Send + Sync, +{ + fn call(&self, this: &mut This, arguments: &mut [Val], return_value: &mut Val) { + self(this) + } +} + +pub struct MethodEntity { + pub(crate) name: String, + pub(crate) handler: Box, +} + +impl MethodEntity { + pub fn new(name: impl ToString, handler: impl Method + 'static) -> Self { + let mut name = name.to_string(); + name.push('\0'); + + Self { + name, + handler: Box::new(handler), + } + } + + unsafe fn function_entry(&self) -> zend_function_entry { + let mut infos = Vec::new(); + infos.push(zeroed::()); + + let mut last_arg_info = zeroed::(); + last_arg_info.name = &self.handler as *const _ as *mut _; + infos.push(last_arg_info); + + zend_function_entry { + fname: self.name.as_ptr().cast(), + handler: Some(method_invoke), + arg_info: Box::into_raw(infos.into_boxed_slice()).cast(), + num_args: 0, + flags: 0, + } + } +} + +pub trait Class: Send + Sync { + fn methods(&self) -> &[MethodEntity]; + fn properties(&self) -> &[PropertyEntity]; +} + +pub struct StdClass { + pub(crate) method_entities: Vec, + pub(crate) property_entities: Vec, +} + +impl StdClass { + pub fn new() -> Self { + Self { + method_entities: Vec::new(), + property_entities: Vec::new(), + } + } + + pub fn add_method(&mut self, name: impl ToString, handler: impl Method + 'static) { + self.method_entities.push(MethodEntity::new(name, handler)); + } + + pub fn add_property(&mut self, name: impl ToString, value: i32) { + self.property_entities + .push(PropertyEntity::new(name, value)); + } +} + +impl Class for StdClass { + fn methods(&self) -> &[MethodEntity] { + &self.method_entities + } + + fn properties(&self) -> &[PropertyEntity] { + &self.property_entities + } +} + +#[repr(transparent)] +pub struct ClassEntry { + inner: zend_class_entry, +} + +impl ClassEntry {} + +pub struct ClassEntity { + pub(crate) name: String, + pub(crate) entry: AtomicPtr, + pub(crate) class: Box, + pub(crate) init_once: Once, + pub(crate) function_entries: OnceCell>, +} + +impl ClassEntity { + pub(crate) unsafe fn new(name: impl ToString, class: impl Class + 'static) -> Self { + Self { + name: name.to_string(), + entry: AtomicPtr::new(null_mut()), + class: Box::new(class), + init_once: Once::new(), + function_entries: Default::default(), + } + } + + pub(crate) unsafe fn init(&self) { + self.init_once.call_once(|| { + let mut class_ce = phper_init_class_entry_ex( + self.name.as_ptr().cast(), + self.name.len(), + self.function_entries().load(Ordering::SeqCst).cast(), + ); + self.entry.store( + zend_register_internal_class(&mut class_ce).cast(), + Ordering::SeqCst, + ); + }); + } + + pub(crate) unsafe fn declare_properties(&self) { + let properties = self.class.properties(); + for property in properties { + zend_declare_property_long( + self.entry.load(Ordering::SeqCst).cast(), + property.name.as_ptr().cast(), + property.name.len(), + property.value.into(), + Visibility::Public as c_int, + ); + } + } + + unsafe fn function_entries(&self) -> &AtomicPtr { + self.function_entries.get_or_init(|| { + let mut methods = self + .class + .methods() + .iter() + .map(|method| method.function_entry()) + .collect::>(); + methods.push(zeroed::()); + let entry = Box::into_raw(methods.into_boxed_slice()).cast(); + AtomicPtr::new(entry) + }) + } +} + +#[repr(transparent)] +pub struct This { + inner: zval, +} + +impl This { + #[inline] + pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self { + assert!(!ptr.is_null(), "ptr should not be null"); + &mut *(ptr as *mut Self) + } +} + +pub struct PropertyEntity { + name: String, + value: i32, +} + +impl PropertyEntity { + pub fn new(name: impl ToString, value: i32) -> Self { + Self { + name: name.to_string(), + value, + } + } +} diff --git a/phper/src/zend/compile.rs b/phper/src/zend/compile.rs index 52ecc2d5..ae5d83af 100644 --- a/phper/src/zend/compile.rs +++ b/phper/src/zend/compile.rs @@ -1,6 +1,5 @@ use crate::sys::{ - zend_internal_arg_info, zend_type, zend_uchar, ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, - ZEND_ACC_PUBLIC, + zend_internal_arg_info, zend_uchar, ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, }; use std::{cell::Cell, os::raw::c_char}; @@ -52,7 +51,7 @@ pub const fn create_zend_arg_info( { zend_internal_arg_info { name, - type_: 0 as zend_type, + type_: 0 as crate::sys::zend_type, pass_by_reference: pass_by_ref as zend_uchar, is_variadic: 0, } diff --git a/phper/src/zend/ini.rs b/phper/src/zend/ini.rs index f1207866..3c016f16 100644 --- a/phper/src/zend/ini.rs +++ b/phper/src/zend/ini.rs @@ -1,11 +1,54 @@ -use crate::sys::{zend_ini_entry, zend_ini_entry_def, zend_string}; +use crate::sys::{ + zend_ini_entry, zend_ini_entry_def, zend_string, OnUpdateString, PHP_INI_ALL, PHP_INI_PERDIR, + PHP_INI_SYSTEM, PHP_INI_USER, +}; use std::{ cell::Cell, mem::{size_of, transmute}, os::raw::{c_int, c_void}, ptr::null_mut, + sync::atomic::AtomicPtr, }; +#[repr(u32)] +#[derive(Copy, Clone)] +pub enum Policy { + All = PHP_INI_ALL, + User = PHP_INI_USER, + Perdir = PHP_INI_PERDIR, + System = PHP_INI_SYSTEM, +} + +pub(crate) struct IniEntity { + name: String, + value: usize, + default_value: String, + policy: Policy, +} + +impl IniEntity { + pub(crate) fn new(name: impl ToString, default_value: impl ToString, policy: Policy) -> Self { + Self { + name: name.to_string(), + value: 0, + default_value: default_value.to_string(), + policy, + } + } + + // TODO Pass the logic of multi type item. + pub(crate) fn ini_entry_def(&self) -> zend_ini_entry_def { + let arg2: Box<*mut c_void> = Box::new(null_mut()); + create_ini_entry_ex( + &self.name, + &self.default_value, + Some(OnUpdateString), + self.policy as u32, + Box::leak(arg2).cast(), + ) + } +} + pub type Mh = unsafe extern "C" fn( *mut zend_ini_entry, *mut zend_string, diff --git a/phper/src/zend/mod.rs b/phper/src/zend/mod.rs index 58ceec22..b8d8c81a 100644 --- a/phper/src/zend/mod.rs +++ b/phper/src/zend/mod.rs @@ -1,8 +1,9 @@ pub mod api; +pub mod classes; pub mod compile; pub mod errors; -pub mod exceptions; pub mod ini; pub mod modules; pub mod portability; +pub mod throws; pub mod types; diff --git a/phper/src/zend/modules.rs b/phper/src/zend/modules.rs index 8a5a63ff..9d4d47ee 100644 --- a/phper/src/zend/modules.rs +++ b/phper/src/zend/modules.rs @@ -1,16 +1,25 @@ use crate::{ sys::{ - zend_function_entry, zend_module_entry, zend_register_ini_entries, - zend_unregister_ini_entries, PHP_MODULE_BUILD_ID, USING_ZTS, ZEND_DEBUG, - ZEND_MODULE_API_NO, + zend_class_entry, zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, + zend_module_entry, zend_register_ini_entries, zend_string, zend_unregister_ini_entries, + PHP_MODULE_BUILD_ID, USING_ZTS, ZEND_DEBUG, ZEND_MODULE_API_NO, + }, + zend::{ + api::{invoke, Function, FunctionEntity}, + classes::{Class, ClassEntity, ClassEntry}, + ini::{IniEntity, IniEntries, Policy}, }, - zend::ini::IniEntries, }; +use once_cell::sync::{Lazy, OnceCell}; use std::{ - cell::Cell, - mem::size_of, + borrow::BorrowMut, + cell::{Cell, RefCell, RefMut}, + collections::HashMap, + mem::{forget, size_of, transmute, zeroed}, + ops::DerefMut, os::raw::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void}, ptr::{null, null_mut}, + sync::{atomic::AtomicPtr, Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; pub struct ModuleEntryBuilder { @@ -170,27 +179,227 @@ impl ModuleEntry { unsafe impl Sync for ModuleEntry {} pub struct ModuleArgs { - _type_: c_int, + r#type: c_int, module_number: c_int, } impl ModuleArgs { - pub const fn new(_type_: c_int, module_number: c_int) -> Self { + pub const fn new(r#type: c_int, module_number: c_int) -> Self { Self { - _type_, + r#type, module_number, } } - pub fn register_ini_entries(&self, ini_entries: &IniEntries) { + pub fn register_ini_entries(&self, ini_entries: *const zend_ini_entry_def) { unsafe { - zend_register_ini_entries(ini_entries.as_ptr(), self.module_number); + zend_register_ini_entries(ini_entries, self.module_number); } } - pub fn unregister_ini_entries(&self) { + pub(crate) fn unregister_ini_entries(&self) { unsafe { zend_unregister_ini_entries(self.module_number); } } } + +static GLOBAL_MODULE: Lazy> = Lazy::new(Default::default); + +pub fn read_global_module() -> RwLockReadGuard<'static, Module> { + (&*GLOBAL_MODULE).read().expect("get write lock failed") +} + +pub fn write_global_module() -> RwLockWriteGuard<'static, Module> { + (&*GLOBAL_MODULE).write().expect("get write lock failed") +} + +unsafe extern "C" fn module_startup(r#type: c_int, module_number: c_int) -> c_int { + let args = ModuleArgs::new(r#type, module_number); + { + args.register_ini_entries(read_global_module().ini_entries()); + } + { + for class_entity in &read_global_module().class_entities { + class_entity.init(); + class_entity.declare_properties(); + } + } + match &read_global_module().module_init { + Some(f) => f(args) as c_int, + None => 1, + } +} + +unsafe extern "C" fn module_shutdown(r#type: c_int, module_number: c_int) -> c_int { + let args = ModuleArgs::new(r#type, module_number); + args.unregister_ini_entries(); + + match &read_global_module().module_shutdown { + Some(f) => f(args) as c_int, + None => 1, + } +} + +unsafe extern "C" fn request_startup(r#type: c_int, request_number: c_int) -> c_int { + match &read_global_module().request_init { + Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, + None => 1, + } +} + +unsafe extern "C" fn request_shutdown(r#type: c_int, request_number: c_int) -> c_int { + match &read_global_module().request_shutdown { + Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, + None => 1, + } +} + +#[derive(Default)] +pub struct Module { + name: String, + version: String, + module_init: Option bool + Send + Sync>>, + module_shutdown: Option bool + Send + Sync>>, + request_init: Option bool + Send + Sync>>, + request_shutdown: Option bool + Send + Sync>>, + ini_entities: HashMap, + function_entities: Vec, + class_entities: Vec, +} + +impl Module { + pub fn set_name(&mut self, name: impl ToString) { + let mut name = name.to_string(); + name.push('\0'); + self.name = name; + } + + pub fn set_version(&mut self, version: impl ToString) { + let mut version = version.to_string(); + version.push('\0'); + self.version = version; + } + + pub fn on_module_init(&mut self, func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static) { + self.module_init = Some(Box::new(func)); + } + + pub fn on_module_shutdown( + &mut self, + func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static, + ) { + self.module_shutdown = Some(Box::new(func)); + } + + pub fn on_request_init(&mut self, func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static) { + self.request_init = Some(Box::new(func)); + } + + pub fn on_request_shutdown( + &mut self, + func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static, + ) { + self.request_shutdown = Some(Box::new(func)); + } + + pub fn add_ini(&mut self, name: impl ToString, value: impl ToString, policy: Policy) { + self.ini_entities + .insert(name.to_string(), IniEntity::new(name, value, policy)); + } + + pub fn add_function(&mut self, name: impl ToString, handler: impl Function + 'static) { + let mut name = name.to_string(); + name.push('\0'); + + self.function_entities.push(FunctionEntity { + name, + handler: Box::new(handler), + }); + } + + pub fn add_class(&mut self, name: impl ToString, class: impl Class + 'static) { + self.class_entities + .push(unsafe { ClassEntity::new(name, class) }) + } + + pub unsafe fn module_entry(&self) -> *const zend_module_entry { + assert!(!self.name.is_empty(), "module name must be set"); + assert!(!self.version.is_empty(), "module version must be set"); + + let entry: Box = Box::new(zend_module_entry { + size: size_of::() as c_ushort, + zend_api: ZEND_MODULE_API_NO as c_uint, + zend_debug: ZEND_DEBUG as c_uchar, + zts: USING_ZTS as c_uchar, + ini_entry: null(), + deps: null(), + name: self.name.as_ptr().cast(), + functions: self.function_entries(), + module_startup_func: Some(module_startup), + module_shutdown_func: Some(module_shutdown), + request_startup_func: Some(request_startup), + request_shutdown_func: Some(request_shutdown), + info_func: None, + version: null(), + globals_size: 0usize, + #[cfg(phper_zts)] + globals_id_ptr: std::ptr::null_mut(), + #[cfg(not(phper_zts))] + globals_ptr: std::ptr::null_mut(), + globals_ctor: None, + globals_dtor: None, + post_deactivate_func: None, + module_started: 0, + type_: 0, + handle: null_mut(), + module_number: 0, + build_id: PHP_MODULE_BUILD_ID, + }); + + Box::into_raw(entry) + } + + fn function_entries(&self) -> *const zend_function_entry { + if self.function_entities.is_empty() { + return null(); + } + + let mut entries = Vec::new(); + + for f in &self.function_entities { + let mut infos = Vec::new(); + infos.push(unsafe { zeroed::() }); + + let mut last_arg_info = unsafe { zeroed::() }; + last_arg_info.name = ((&f.handler) as *const _ as *mut i8).cast(); + infos.push(last_arg_info); + + let entry = zend_function_entry { + fname: f.name.as_ptr().cast(), + handler: Some(invoke), + arg_info: Box::into_raw(infos.into_boxed_slice()).cast(), + num_args: 0, + flags: 0, + }; + + entries.push(entry); + } + + entries.push(unsafe { zeroed::() }); + + Box::into_raw(entries.into_boxed_slice()).cast() + } + + fn ini_entries(&self) -> *const zend_ini_entry_def { + let mut entries = Vec::new(); + + for (_, ini) in &self.ini_entities { + ini.ini_entry_def(); + } + + entries.push(unsafe { zeroed::() }); + + Box::into_raw(entries.into_boxed_slice()).cast() + } +} diff --git a/phper/src/zend/exceptions.rs b/phper/src/zend/throws.rs similarity index 100% rename from phper/src/zend/exceptions.rs rename to phper/src/zend/throws.rs diff --git a/phper/src/zend/types.rs b/phper/src/zend/types.rs index 19d616b2..eceb4de0 100644 --- a/phper/src/zend/types.rs +++ b/phper/src/zend/types.rs @@ -1,20 +1,22 @@ use crate::{ c_str_ptr, sys::{ - self, phper_get_this, phper_init_class_entry_ex, phper_z_strval_p, phper_zval_get_type, - phper_zval_stringl, zend_class_entry, zend_declare_property_bool, - zend_declare_property_long, zend_declare_property_null, zend_declare_property_stringl, - zend_execute_data, zend_long, zend_parse_parameters, zend_read_property, - zend_register_internal_class, zend_throw_exception, zend_update_property_bool, - zend_update_property_long, zend_update_property_null, zend_update_property_stringl, zval, - IS_DOUBLE, IS_FALSE, IS_LONG, IS_NULL, IS_TRUE, ZEND_RESULT_CODE_SUCCESS, + self, _zend_get_parameters_array_ex, phper_get_this, phper_init_class_entry_ex, + phper_z_strval_p, phper_zval_get_type, phper_zval_stringl, zend_arg_info, zend_class_entry, + zend_declare_property_bool, zend_declare_property_long, zend_declare_property_null, + zend_declare_property_stringl, zend_execute_data, zend_long, zend_parse_parameters, + zend_read_property, zend_register_internal_class, zend_throw_exception, + zend_update_property_bool, zend_update_property_long, zend_update_property_null, + zend_update_property_stringl, zval, IS_DOUBLE, IS_FALSE, IS_LONG, IS_NULL, IS_TRUE, + ZEND_RESULT_CODE_SUCCESS, }, - zend::{api::FunctionEntries, compile::Visibility, exceptions::Throwable}, + zend::{api::FunctionEntries, classes::This, compile::Visibility, throws::Throwable}, }; use std::{ borrow::Cow, cell::Cell, ffi::{c_void, CStr}, + mem::zeroed, os::raw::{c_char, c_int}, ptr::null_mut, slice, str, @@ -209,6 +211,11 @@ pub struct ExecuteData { } impl ExecuteData { + #[inline] + pub const fn new(inner: zend_execute_data) -> Self { + Self { inner } + } + pub unsafe fn from_mut<'a>(ptr: *mut zend_execute_data) -> &'a mut Self { &mut *(ptr as *mut Self) } @@ -218,24 +225,41 @@ impl ExecuteData { } #[inline] - pub fn num_args(&self) -> usize { - unsafe { self.inner.This.u2.num_args as usize } + pub unsafe fn common_num_args(&self) -> u32 { + (*self.inner.func).common.num_args + } + + #[inline] + pub unsafe fn common_arg_info(&self) -> *mut zend_arg_info { + (*self.inner.func).common.arg_info } #[inline] - pub fn get_this(&mut self) -> *mut zval { - unsafe { phper_get_this(&mut self.inner) } + pub unsafe fn num_args(&self) -> u32 { + self.inner.This.u2.num_args + } + + #[inline] + pub unsafe fn get_this(&mut self) -> &mut This { + This::from_mut(phper_get_this(&mut self.inner)) + } + + pub unsafe fn get_parameters_array(&mut self) -> Vec { + let num_args = self.num_args(); + let mut arguments = vec![zeroed::(); num_args as usize]; + _zend_get_parameters_array_ex(num_args as c_int, arguments.as_mut_ptr()); + arguments.into_iter().map(Val::new).collect() } pub fn parse_parameters(&self) -> Option { - ::parse(self.num_args(), ()) + ::parse(unsafe { self.num_args() } as usize, ()) } pub fn parse_parameters_optional( &self, default: O, ) -> Option { - ::parse(self.num_args(), default) + ::parse(unsafe { self.num_args() } as usize, default) } } @@ -582,7 +606,14 @@ pub struct Val { } impl Val { + #[inline] + pub const fn new(inner: zval) -> Self { + Self { inner } + } + + #[inline] pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self { + assert!(!ptr.is_null(), "ptr should not be null"); &mut *(ptr as *mut Self) } From cc66842fbf5176fe4eebe131db453bd5284fa104 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Fri, 9 Apr 2021 19:11:38 +0800 Subject: [PATCH 04/20] Remove a lot of unused codes. --- rustfmt.toml => .rustfmt.toml | 0 README.md | 1 - examples/hello/src/lib.rs | 35 +- examples/hello/src/main.rs | 5 + phper/Cargo.toml | 2 + phper/src/{zend => }/classes.rs | 34 +- phper/src/cmd.rs | 63 +++ phper/src/{error.rs => errors.rs} | 0 phper/src/functions.rs | 117 ++++ phper/src/{zend => }/ini.rs | 61 +- phper/src/lib.rs | 15 +- phper/src/{zend/errors.rs => logs.rs} | 0 phper/src/main/mod.rs | 1 - phper/src/main/php.rs | 20 - phper/src/{zend => }/modules.rs | 220 +------- phper/src/{zend => }/throws.rs | 0 phper/src/values.rs | 98 ++++ phper/src/zend/api.rs | 196 ------- phper/src/zend/compile.rs | 79 --- phper/src/zend/mod.rs | 9 - phper/src/zend/portability.rs | 1 - phper/src/zend/types.rs | 782 -------------------------- rust-toolchain | 1 - 23 files changed, 376 insertions(+), 1364 deletions(-) rename rustfmt.toml => .rustfmt.toml (100%) create mode 100644 examples/hello/src/main.rs rename phper/src/{zend => }/classes.rs (92%) create mode 100644 phper/src/cmd.rs rename phper/src/{error.rs => errors.rs} (100%) create mode 100644 phper/src/functions.rs rename phper/src/{zend => }/ini.rs (63%) rename phper/src/{zend/errors.rs => logs.rs} (100%) delete mode 100644 phper/src/main/mod.rs delete mode 100644 phper/src/main/php.rs rename phper/src/{zend => }/modules.rs (61%) rename phper/src/{zend => }/throws.rs (100%) create mode 100644 phper/src/values.rs delete mode 100644 phper/src/zend/api.rs delete mode 100644 phper/src/zend/compile.rs delete mode 100644 phper/src/zend/mod.rs delete mode 100644 phper/src/zend/portability.rs delete mode 100644 phper/src/zend/types.rs delete mode 100644 rust-toolchain diff --git a/rustfmt.toml b/.rustfmt.toml similarity index 100% rename from rustfmt.toml rename to .rustfmt.toml diff --git a/README.md b/README.md index e3abe3a4..dc49b4f5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ A library that allows us to write PHP extensions using pure Rust and using safe ## Requirement -- rust nightly channel (now) - libclang >= 9 ## Usage diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 0c1e2a11..31c82212 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -1,24 +1,19 @@ -use once_cell::sync::Lazy; +use std::{fs::OpenOptions, io::Write}; + use phper::{ - c_str_ptr, php_function, php_get_module, php_minfo_function, php_minit_function, - php_mshutdown_function, php_rinit_function, php_rshutdown_function, + c_str_ptr, + classes::{Class, MethodEntity, StdClass, This}, + functions::create_zend_arg_info, + ini::Policy, + modules::{read_global_module, write_global_module, Module, ModuleArgs}, + php_function, php_get_module, php_minfo_function, php_minit_function, php_mshutdown_function, + php_rinit_function, php_rshutdown_function, sys::{ php_info_print_table_end, php_info_print_table_row, php_info_print_table_start, zend_function_entry, OnUpdateBool, PHP_INI_SYSTEM, }, - zend::{ - api::{FunctionEntries, ModuleGlobals}, - classes::{Class, MethodEntity, StdClass, This}, - compile::{create_zend_arg_info, MultiInternalArgInfo}, - ini::{IniEntries, Policy}, - modules::{ - read_global_module, write_global_module, Module, ModuleArgs, ModuleEntry, - ModuleEntryBuilder, - }, - types::{ExecuteData, SetVal, Val}, - }, + values::{ExecuteData, Val}, }; -use std::{fs::OpenOptions, io::Write}; // static HELLO_ENABLE: ModuleGlobals = ModuleGlobals::new(false); // @@ -108,22 +103,22 @@ use std::{fs::OpenOptions, io::Write}; // module // } -fn module_init(args: ModuleArgs) -> bool { +fn module_init(_args: ModuleArgs) -> bool { // append_file("module_init"); true } -fn module_shutdown(args: ModuleArgs) -> bool { +fn module_shutdown(_args: ModuleArgs) -> bool { // append_file("module_shutdown"); true } -fn request_init(args: ModuleArgs) -> bool { +fn request_init(_args: ModuleArgs) -> bool { // append_file("request_init"); true } -fn request_shutdown(args: ModuleArgs) -> bool { +fn request_shutdown(_args: ModuleArgs) -> bool { // append_file("request_shutdown"); true } @@ -154,7 +149,7 @@ pub extern "C" fn get_module() -> *const ::phper::sys::zend_module_entry { module.set_name(env!("CARGO_PKG_NAME")); module.set_version(env!("CARGO_PKG_VERSION")); - module.add_ini("hello.enable", "on", Policy::All); + module.add_ini("hello.enable", "off", Policy::All); module.on_module_init(module_init); module.on_module_shutdown(module_shutdown); diff --git a/examples/hello/src/main.rs b/examples/hello/src/main.rs new file mode 100644 index 00000000..dd7ec056 --- /dev/null +++ b/examples/hello/src/main.rs @@ -0,0 +1,5 @@ +use phper::cmd::make; + +fn main() { + make(); +} diff --git a/phper/Cargo.toml b/phper/Cargo.toml index acd104b3..31f74e90 100644 --- a/phper/Cargo.toml +++ b/phper/Cargo.toml @@ -12,6 +12,8 @@ keywords = ["php", "binding", "extension"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.40" +clap = "3.0.0-beta.2" once_cell = "1.7.2" phper-alloc = { version = "0.2", path = "../phper-alloc" } phper-macros = { version = "0.2", path = "../phper-macros" } diff --git a/phper/src/zend/classes.rs b/phper/src/classes.rs similarity index 92% rename from phper/src/zend/classes.rs rename to phper/src/classes.rs index 52501b21..a3f124a4 100644 --- a/phper/src/zend/classes.rs +++ b/phper/src/classes.rs @@ -1,15 +1,3 @@ -use crate::{ - sys::{ - phper_init_class_entry_ex, zend_class_entry, zend_declare_property_long, - zend_function_entry, zend_internal_arg_info, zend_register_internal_class, zval, - }, - zend::{ - api::{invoke, method_invoke, FunctionEntry}, - compile::Visibility, - types::Val, - }, -}; -use once_cell::sync::OnceCell; use std::{ mem::zeroed, os::raw::c_int, @@ -20,6 +8,18 @@ use std::{ }, }; +use once_cell::sync::OnceCell; + +use crate::{ + functions::{method_invoke, FunctionEntry}, + sys::{ + phper_init_class_entry_ex, zend_class_entry, zend_declare_property_long, + zend_function_entry, zend_internal_arg_info, zend_register_internal_class, zval, + ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, + }, + values::Val, +}; + pub trait Method: Send + Sync { fn call(&self, this: &mut This, arguments: &mut [Val], return_value: &mut Val); } @@ -28,7 +28,7 @@ impl Method for F where F: Fn(&mut This) + Send + Sync, { - fn call(&self, this: &mut This, arguments: &mut [Val], return_value: &mut Val) { + fn call(&self, this: &mut This, _arguments: &mut [Val], _return_value: &mut Val) { self(this) } } @@ -199,3 +199,11 @@ impl PropertyEntity { } } } + +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum Visibility { + Public = ZEND_ACC_PUBLIC, + Protected = ZEND_ACC_PROTECTED, + Private = ZEND_ACC_PRIVATE, +} diff --git a/phper/src/cmd.rs b/phper/src/cmd.rs new file mode 100644 index 00000000..7ba2dbd7 --- /dev/null +++ b/phper/src/cmd.rs @@ -0,0 +1,63 @@ +use crate::sys::PHP_EXTENSION_DIR; +use anyhow::Context; +use clap::Clap; +use std::{ + env, + ffi::{CStr, OsString}, + fs, + path::{Path, PathBuf}, +}; + +/// Make utility. +#[derive(Clap)] +struct Make { + #[clap(subcommand)] + sub: SubCommand, +} + +#[derive(Clap)] +enum SubCommand { + Install(InstallCommand), +} + +#[derive(Clap)] +struct InstallCommand {} + +pub fn make() { + try_make().expect("make failed"); +} + +pub fn try_make() -> anyhow::Result<()> { + let make: Make = Make::parse(); + match make.sub { + SubCommand::Install(_) => { + let (lib_path, ext_name) = get_lib_path_and_ext_name()?; + let extension_dir = CStr::from_bytes_with_nul(PHP_EXTENSION_DIR)?.to_str()?; + println!("Installing shared extensions: {}", extension_dir); + let ext_path = Path::new(extension_dir).join(ext_name); + fs::copy(lib_path, ext_path)?; + } + } + Ok(()) +} + +fn get_lib_path_and_ext_name() -> anyhow::Result<(PathBuf, OsString)> { + let exe_path = env::current_exe()?; + let exe_stem = exe_path + .file_stem() + .context("failed to get current exe stem")?; + let target_dir = exe_path + .parent() + .context("failed to get current exe directory")?; + + let mut exe_name = OsString::new(); + exe_name.push("lib"); + exe_name.push(exe_stem); + exe_name.push(".so"); + + let mut ext_name = OsString::new(); + ext_name.push(exe_stem); + ext_name.push(".so"); + + Ok((target_dir.join(exe_name), ext_name)) +} diff --git a/phper/src/error.rs b/phper/src/errors.rs similarity index 100% rename from phper/src/error.rs rename to phper/src/errors.rs diff --git a/phper/src/functions.rs b/phper/src/functions.rs new file mode 100644 index 00000000..a16a778c --- /dev/null +++ b/phper/src/functions.rs @@ -0,0 +1,117 @@ +use std::{ + cell::Cell, + ffi::CStr, + mem::{size_of, transmute, zeroed}, + os::raw::{c_char, c_int}, + ptr::null, +}; + +use crate::{ + classes::Method, + ini::create_ini_entry_ex, + sys::{ + _zend_get_parameters_array_ex, phper_z_strval_p, zend_arg_info, zend_execute_data, + zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, zend_uchar, zif_handler, + zval, + }, + values::{ExecuteData, Val}, +}; + +pub struct Parameters; + +pub trait Function: Send + Sync { + fn call(&self, arguments: &mut [Val], return_value: &mut Val); +} + +impl Function for F +where + F: Fn() + Send + Sync, +{ + fn call(&self, _arguments: &mut [Val], _return_value: &mut Val) { + self() + } +} + +#[repr(transparent)] +pub struct FunctionEntry { + inner: zend_function_entry, +} + +pub(crate) struct FunctionEntity { + pub(crate) name: String, + pub(crate) handler: Box, +} + +pub(crate) unsafe extern "C" fn invoke( + execute_data: *mut zend_execute_data, + return_value: *mut zval, +) { + let execute_data = ExecuteData::from_mut(execute_data); + let return_value = Val::from_mut(return_value); + + let num_args = execute_data.common_num_args(); + let arg_info = execute_data.common_arg_info(); + + let last_arg_info = arg_info.offset(num_args as isize); + let handler = (*last_arg_info).name as *const Box; + let handler = handler.as_ref().expect("handler is null"); + + // TODO Do num args check + + let mut arguments = execute_data.get_parameters_array(); + + handler.call(&mut arguments, return_value); +} + +pub(crate) unsafe extern "C" fn method_invoke( + execute_data: *mut zend_execute_data, + return_value: *mut zval, +) { + let execute_data = ExecuteData::from_mut(execute_data); + let return_value = Val::from_mut(return_value); + + let num_args = execute_data.common_num_args(); + let arg_info = execute_data.common_arg_info(); + + let last_arg_info = arg_info.offset(num_args as isize); + let handler = (*last_arg_info).name as *const Box; + let handler = handler.as_ref().expect("handler is null"); + + // TODO Do num args check + + let mut arguments = execute_data.get_parameters_array(); + + handler.call(execute_data.get_this(), &mut arguments, return_value); +} + +pub const fn create_zend_arg_info( + name: *const c_char, + pass_by_ref: bool, +) -> zend_internal_arg_info { + #[cfg(any( + phper_php_version = "8.0", + phper_php_version = "7.4", + phper_php_version = "7.3", + phper_php_version = "7.2" + ))] + { + zend_internal_arg_info { + name, + type_: 0 as crate::sys::zend_type, + pass_by_reference: pass_by_ref as zend_uchar, + is_variadic: 0, + } + } + + #[cfg(any(phper_php_version = "7.1", phper_php_version = "7.0",))] + { + zend_internal_arg_info { + name, + class_name: std::ptr::null(), + type_hint: 0, + allow_null: 0, + pass_by_reference: pass_by_ref as zend_uchar, + is_variadic: 0, + } + } +} diff --git a/phper/src/zend/ini.rs b/phper/src/ini.rs similarity index 63% rename from phper/src/zend/ini.rs rename to phper/src/ini.rs index 3c016f16..6ffa6706 100644 --- a/phper/src/zend/ini.rs +++ b/phper/src/ini.rs @@ -39,63 +39,30 @@ impl IniEntity { // TODO Pass the logic of multi type item. pub(crate) fn ini_entry_def(&self) -> zend_ini_entry_def { let arg2: Box<*mut c_void> = Box::new(null_mut()); + let arg2 = Box::into_raw(arg2); create_ini_entry_ex( &self.name, &self.default_value, Some(OnUpdateString), self.policy as u32, - Box::leak(arg2).cast(), + arg2.cast(), ) } } -pub type Mh = unsafe extern "C" fn( - *mut zend_ini_entry, - *mut zend_string, - *mut c_void, - *mut c_void, - *mut c_void, - c_int, -) -> c_int; - -const fn ini_entry_def_end() -> zend_ini_entry_def { - unsafe { transmute([0u8; size_of::()]) } -} - -#[repr(C)] -struct ZendIniEntriesWithEnd([zend_ini_entry_def; N], zend_ini_entry_def); - -pub struct IniEntries { - inner: Cell>, -} - -impl IniEntries { - pub const fn new(inner: [zend_ini_entry_def; N]) -> Self { - Self { - inner: Cell::new(ZendIniEntriesWithEnd(inner, ini_entry_def_end())), - } - } - - #[inline] - pub const fn as_ptr(&self) -> *const zend_ini_entry_def { - self.inner.as_ptr().cast() - } -} - -unsafe impl Sync for IniEntries {} - -pub const fn create_ini_entry( - name: &str, - default_value: &str, - modifiable: u32, -) -> zend_ini_entry_def { - create_ini_entry_ex(name, default_value, None, modifiable, null_mut()) -} - -pub const fn create_ini_entry_ex( +pub(crate) fn create_ini_entry_ex( name: &str, default_value: &str, - on_modify: Option, + on_modify: Option< + unsafe extern "C" fn( + *mut zend_ini_entry, + *mut zend_string, + *mut c_void, + *mut c_void, + *mut c_void, + c_int, + ) -> c_int, + >, modifiable: u32, arg2: *mut c_void, ) -> zend_ini_entry_def { @@ -115,7 +82,7 @@ pub const fn create_ini_entry_ex( zend_ini_entry_def { name: name.as_ptr().cast(), on_modify, - mh_arg1: 0 as *mut _, + mh_arg1: null_mut(), mh_arg2: arg2, mh_arg3: null_mut(), value: default_value.as_ptr().cast(), diff --git a/phper/src/lib.rs b/phper/src/lib.rs index 7cb43fe9..716b855a 100644 --- a/phper/src/lib.rs +++ b/phper/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(const_fn_transmute, const_fn_fn_ptr_basics)] #![warn(rust_2018_idioms, clippy::dbg_macro, clippy::print_stdout)] /*! @@ -27,12 +26,18 @@ Now the library don't support `ZTS`, the template is using `thread_local!` inste Version `0.1.x` will be a preview version. */ -mod error; -pub mod main; +pub mod classes; +pub mod cmd; +mod errors; +pub mod functions; +pub mod ini; +pub mod logs; +pub mod modules; +pub mod throws; mod utils; -pub mod zend; +pub mod values; -pub use crate::error::*; +pub use crate::errors::*; pub use phper_alloc as alloc; pub use phper_macros::*; pub use phper_sys as sys; diff --git a/phper/src/zend/errors.rs b/phper/src/logs.rs similarity index 100% rename from phper/src/zend/errors.rs rename to phper/src/logs.rs diff --git a/phper/src/main/mod.rs b/phper/src/main/mod.rs deleted file mode 100644 index ec85295d..00000000 --- a/phper/src/main/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod php; diff --git a/phper/src/main/php.rs b/phper/src/main/php.rs deleted file mode 100644 index 44972b02..00000000 --- a/phper/src/main/php.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::zend::errors::Level; -use std::{os::raw::c_int, ptr::null}; - -pub fn error_doc_ref(level: Level, message: impl ToString) { - let mut message = message.to_string(); - message.push('\0'); - - unsafe { - #[cfg(phper_php_version = "7.4")] - crate::sys::php_error_docref(null(), level as c_int, message.as_ptr().cast()); - - #[cfg(any( - phper_php_version = "7.3", - phper_php_version = "7.2", - phper_php_version = "7.1", - phper_php_version = "7.0", - ))] - crate::sys::php_error_docref0(null(), level as c_int, message.as_ptr().cast()); - } -} diff --git a/phper/src/zend/modules.rs b/phper/src/modules.rs similarity index 61% rename from phper/src/zend/modules.rs rename to phper/src/modules.rs index 9d4d47ee..2eff992e 100644 --- a/phper/src/zend/modules.rs +++ b/phper/src/modules.rs @@ -1,16 +1,14 @@ use crate::{ + classes::{Class, ClassEntity, ClassEntry}, + functions::{invoke, Function, FunctionEntity}, + ini::{IniEntity, Policy}, sys::{ zend_class_entry, zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, zend_module_entry, zend_register_ini_entries, zend_string, zend_unregister_ini_entries, PHP_MODULE_BUILD_ID, USING_ZTS, ZEND_DEBUG, ZEND_MODULE_API_NO, }, - zend::{ - api::{invoke, Function, FunctionEntity}, - classes::{Class, ClassEntity, ClassEntry}, - ini::{IniEntity, IniEntries, Policy}, - }, }; -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::Lazy; use std::{ borrow::BorrowMut, cell::{Cell, RefCell, RefMut}, @@ -22,188 +20,6 @@ use std::{ sync::{atomic::AtomicPtr, Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; -pub struct ModuleEntryBuilder { - name: *const c_char, - version: *const c_char, - functions: *const zend_function_entry, - module_startup_func: Option c_int>, - module_shutdown_func: Option c_int>, - request_startup_func: Option c_int>, - request_shutdown_func: Option c_int>, - info_func: Option, - globals_ctor: Option, - globals_dtor: Option, -} - -impl ModuleEntryBuilder { - pub const fn new(name: *const c_char, version: *const c_char) -> Self { - Self { - name, - version, - functions: null(), - module_startup_func: None, - module_shutdown_func: None, - request_startup_func: None, - request_shutdown_func: None, - info_func: None, - globals_ctor: None, - globals_dtor: None, - } - } - - pub const fn functions(self, functions: *const zend_function_entry) -> Self { - Self { functions, ..self } - } - - pub const fn module_startup_func( - self, - module_startup_func: unsafe extern "C" fn(c_int, c_int) -> c_int, - ) -> Self { - Self { - module_startup_func: Some(module_startup_func), - ..self - } - } - - pub const fn module_shutdown_func( - self, - module_shutdown_func: unsafe extern "C" fn(c_int, c_int) -> c_int, - ) -> Self { - Self { - module_shutdown_func: Some(module_shutdown_func), - ..self - } - } - - pub const fn request_startup_func( - self, - request_startup_func: unsafe extern "C" fn(c_int, c_int) -> c_int, - ) -> Self { - Self { - request_startup_func: Some(request_startup_func), - ..self - } - } - - pub const fn request_shutdown_func( - self, - request_shutdown_func: unsafe extern "C" fn(c_int, c_int) -> c_int, - ) -> Self { - Self { - request_shutdown_func: Some(request_shutdown_func), - ..self - } - } - - pub const fn info_func(self, info_func: unsafe extern "C" fn(*mut zend_module_entry)) -> Self { - Self { - info_func: Some(info_func), - ..self - } - } - - pub const fn globals_ctor( - self, - globals_ctor: unsafe extern "C" fn(global: *mut c_void), - ) -> Self { - Self { - globals_ctor: Some(globals_ctor), - ..self - } - } - - pub const fn globals_dtor( - self, - globals_dtor: unsafe extern "C" fn(global: *mut c_void), - ) -> Self { - Self { - globals_dtor: Some(globals_dtor), - ..self - } - } - - pub const fn build(self) -> ModuleEntry { - ModuleEntry::new(zend_module_entry { - size: size_of::() as c_ushort, - zend_api: ZEND_MODULE_API_NO as c_uint, - zend_debug: ZEND_DEBUG as c_uchar, - zts: USING_ZTS as c_uchar, - ini_entry: null(), - deps: null(), - name: self.name, - functions: self.functions, - module_startup_func: self.module_startup_func, - module_shutdown_func: self.module_shutdown_func, - request_startup_func: self.request_startup_func, - request_shutdown_func: self.request_shutdown_func, - info_func: self.info_func, - version: self.version, - globals_size: 0usize, - #[cfg(phper_zts)] - globals_id_ptr: std::ptr::null_mut(), - #[cfg(not(phper_zts))] - globals_ptr: std::ptr::null_mut(), - globals_ctor: self.globals_ctor, - globals_dtor: self.globals_dtor, - post_deactivate_func: None, - module_started: 0, - type_: 0, - handle: null_mut(), - module_number: 0, - build_id: PHP_MODULE_BUILD_ID, - }) - } -} - -#[repr(transparent)] -pub struct ModuleEntry { - raw: Cell, -} - -impl ModuleEntry { - pub const fn new(raw: zend_module_entry) -> Self { - Self { - raw: Cell::new(raw), - } - } - - pub const fn as_ptr(&self) -> *mut zend_module_entry { - self.raw.as_ptr() - } - - pub fn from_ptr<'a>(ptr: *const zend_module_entry) -> &'a Self { - unsafe { &*(ptr as *const Self) } - } -} - -unsafe impl Sync for ModuleEntry {} - -pub struct ModuleArgs { - r#type: c_int, - module_number: c_int, -} - -impl ModuleArgs { - pub const fn new(r#type: c_int, module_number: c_int) -> Self { - Self { - r#type, - module_number, - } - } - - pub fn register_ini_entries(&self, ini_entries: *const zend_ini_entry_def) { - unsafe { - zend_register_ini_entries(ini_entries, self.module_number); - } - } - - pub(crate) fn unregister_ini_entries(&self) { - unsafe { - zend_unregister_ini_entries(self.module_number); - } - } -} - static GLOBAL_MODULE: Lazy> = Lazy::new(Default::default); pub fn read_global_module() -> RwLockReadGuard<'static, Module> { @@ -395,7 +211,7 @@ impl Module { let mut entries = Vec::new(); for (_, ini) in &self.ini_entities { - ini.ini_entry_def(); + entries.push(ini.ini_entry_def()); } entries.push(unsafe { zeroed::() }); @@ -403,3 +219,29 @@ impl Module { Box::into_raw(entries.into_boxed_slice()).cast() } } + +pub struct ModuleArgs { + r#type: c_int, + module_number: c_int, +} + +impl ModuleArgs { + pub const fn new(r#type: c_int, module_number: c_int) -> Self { + Self { + r#type, + module_number, + } + } + + pub fn register_ini_entries(&self, ini_entries: *const zend_ini_entry_def) { + unsafe { + zend_register_ini_entries(ini_entries, self.module_number); + } + } + + pub(crate) fn unregister_ini_entries(&self) { + unsafe { + zend_unregister_ini_entries(self.module_number); + } + } +} diff --git a/phper/src/zend/throws.rs b/phper/src/throws.rs similarity index 100% rename from phper/src/zend/throws.rs rename to phper/src/throws.rs diff --git a/phper/src/values.rs b/phper/src/values.rs new file mode 100644 index 00000000..c745deb7 --- /dev/null +++ b/phper/src/values.rs @@ -0,0 +1,98 @@ +use std::{ + borrow::Cow, + cell::Cell, + ffi::{c_void, CStr}, + mem::zeroed, + os::raw::{c_char, c_int}, + ptr::null_mut, + slice, str, +}; + +use crate::{ + c_str_ptr, + classes::{This, Visibility}, + sys::{ + self, _zend_get_parameters_array_ex, phper_get_this, phper_init_class_entry_ex, + phper_z_strval_p, phper_zval_get_type, phper_zval_stringl, zend_arg_info, zend_class_entry, + zend_declare_property_bool, zend_declare_property_long, zend_declare_property_null, + zend_declare_property_stringl, zend_execute_data, zend_long, zend_parse_parameters, + zend_read_property, zend_register_internal_class, zend_throw_exception, + zend_update_property_bool, zend_update_property_long, zend_update_property_null, + zend_update_property_stringl, zval, IS_DOUBLE, IS_FALSE, IS_LONG, IS_NULL, IS_TRUE, + ZEND_RESULT_CODE_SUCCESS, + }, + throws::Throwable, +}; + +#[repr(transparent)] +pub struct ExecuteData { + inner: zend_execute_data, +} + +impl ExecuteData { + #[inline] + pub const fn new(inner: zend_execute_data) -> Self { + Self { inner } + } + + pub unsafe fn from_mut<'a>(ptr: *mut zend_execute_data) -> &'a mut Self { + &mut *(ptr as *mut Self) + } + + pub fn as_mut(&mut self) -> *mut zend_execute_data { + &mut self.inner + } + + #[inline] + pub unsafe fn common_num_args(&self) -> u32 { + (*self.inner.func).common.num_args + } + + #[inline] + pub unsafe fn common_arg_info(&self) -> *mut zend_arg_info { + (*self.inner.func).common.arg_info + } + + #[inline] + pub unsafe fn num_args(&self) -> u32 { + self.inner.This.u2.num_args + } + + #[inline] + pub unsafe fn get_this(&mut self) -> &mut This { + This::from_mut(phper_get_this(&mut self.inner)) + } + + pub unsafe fn get_parameters_array(&mut self) -> Vec { + let num_args = self.num_args(); + let mut arguments = vec![zeroed::(); num_args as usize]; + _zend_get_parameters_array_ex(num_args as c_int, arguments.as_mut_ptr()); + arguments.into_iter().map(Val::new).collect() + } +} + +#[repr(transparent)] +pub struct Val { + inner: zval, +} + +impl Val { + #[inline] + pub const fn new(inner: zval) -> Self { + Self { inner } + } + + #[inline] + pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self { + assert!(!ptr.is_null(), "ptr should not be null"); + &mut *(ptr as *mut Self) + } + + pub fn as_mut(&mut self) -> *mut zval { + &mut self.inner + } + + unsafe fn type_info(&mut self) -> &mut u32 { + &mut self.inner.u1.type_info + } +} diff --git a/phper/src/zend/api.rs b/phper/src/zend/api.rs deleted file mode 100644 index 3d6650d3..00000000 --- a/phper/src/zend/api.rs +++ /dev/null @@ -1,196 +0,0 @@ -use crate::{ - sys::{ - _zend_get_parameters_array_ex, phper_z_strval_p, zend_arg_info, zend_execute_data, - zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, zif_handler, zval, - }, - zend::{ - classes::Method, - compile::MultiInternalArgInfo, - ini::{create_ini_entry_ex, Mh}, - types::{ExecuteData, Val, Value}, - }, -}; -use std::{ - cell::Cell, - ffi::CStr, - mem::{size_of, transmute, zeroed}, - os::raw::{c_char, c_int}, - ptr::null, -}; - -pub struct Parameters; - -pub trait Function: Send + Sync { - fn call(&self, arguments: &mut [Val], return_value: &mut Val); -} - -impl Function for F -where - F: Fn() + Send + Sync, -{ - fn call(&self, arguments: &mut [Val], return_value: &mut Val) { - self() - } -} - -#[repr(transparent)] -pub struct FunctionEntry { - inner: zend_function_entry, -} - -pub(crate) struct FunctionEntity { - pub(crate) name: String, - pub(crate) handler: Box, -} - -pub(crate) unsafe extern "C" fn invoke( - execute_data: *mut zend_execute_data, - return_value: *mut zval, -) { - let execute_data = ExecuteData::from_mut(execute_data); - let return_value = Val::from_mut(return_value); - - let num_args = execute_data.common_num_args(); - let arg_info = execute_data.common_arg_info(); - - let last_arg_info = arg_info.offset(num_args as isize); - let handler = (*last_arg_info).name as *const Box; - let handler = handler.as_ref().expect("handler is null"); - - // TODO Do num args check - - let mut arguments = execute_data.get_parameters_array(); - - handler.call(&mut arguments, return_value); -} - -pub(crate) unsafe extern "C" fn method_invoke( - execute_data: *mut zend_execute_data, - return_value: *mut zval, -) { - let execute_data = ExecuteData::from_mut(execute_data); - let return_value = Val::from_mut(return_value); - - let num_args = execute_data.common_num_args(); - let arg_info = execute_data.common_arg_info(); - - let last_arg_info = arg_info.offset(num_args as isize); - let handler = (*last_arg_info).name as *const Box; - let handler = handler.as_ref().expect("handler is null"); - - // TODO Do num args check - - let mut arguments = execute_data.get_parameters_array(); - - handler.call(execute_data.get_this(), &mut arguments, return_value); -} - -const fn function_entry_end() -> zend_function_entry { - unsafe { transmute([0u8; size_of::()]) } -} - -pub struct ModuleGlobals { - inner: Cell, -} - -impl ModuleGlobals { - pub const fn new(inner: T) -> Self { - Self { - inner: Cell::new(inner), - } - } - - pub const fn as_ptr(&self) -> *mut T { - self.inner.as_ptr() - } - - pub const fn create_ini_entry( - &self, - name: &str, - default_value: &str, - on_modify: Option, - modifiable: u32, - ) -> zend_ini_entry_def { - create_ini_entry_ex( - name, - default_value, - on_modify, - modifiable, - self.as_ptr().cast(), - ) - } -} - -impl ModuleGlobals { - pub fn get(&self) -> T { - self.inner.get() - } -} - -unsafe impl Sync for ModuleGlobals {} - -#[repr(C)] -struct ZendFunctionEntriesWithEnd([zend_function_entry; N], zend_function_entry); - -pub struct FunctionEntries { - inner: Cell>, -} - -impl FunctionEntries { - pub const fn new(inner: [zend_function_entry; N]) -> Self { - Self { - inner: Cell::new(ZendFunctionEntriesWithEnd(inner, function_entry_end())), - } - } - - pub const fn as_ptr(&self) -> *mut zend_function_entry { - self.inner.as_ptr().cast() - } -} - -unsafe impl Sync for FunctionEntries {} - -pub struct FunctionEntryBuilder { - fname: *const c_char, - handler: zif_handler, - arg_info: *const zend_internal_arg_info, - num_args: u32, - flags: u32, -} - -impl FunctionEntryBuilder { - pub const fn new(fname: *const c_char, handler: zif_handler) -> Self { - Self { - fname, - handler, - arg_info: null(), - num_args: 0, - flags: 0, - } - } - - pub const fn arg_info( - self, - arg_info: &'static MultiInternalArgInfo, - ) -> Self { - Self { - arg_info: arg_info.as_ptr(), - num_args: arg_info.len() as u32, - ..self - } - } - - pub const fn flags(self, flags: u32) -> Self { - Self { flags, ..self } - } - - pub const fn build(self) -> zend_function_entry { - zend_function_entry { - fname: self.fname, - handler: self.handler, - arg_info: self.arg_info, - num_args: self.num_args, - flags: self.flags, - } - } -} diff --git a/phper/src/zend/compile.rs b/phper/src/zend/compile.rs deleted file mode 100644 index ae5d83af..00000000 --- a/phper/src/zend/compile.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::sys::{ - zend_internal_arg_info, zend_uchar, ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, -}; -use std::{cell::Cell, os::raw::c_char}; - -#[repr(C)] -struct ZendInternalArgInfosWithEnd( - zend_internal_arg_info, - [zend_internal_arg_info; N], -); - -pub struct MultiInternalArgInfo { - inner: Cell>, -} - -impl MultiInternalArgInfo { - pub const fn new( - required_num_args: usize, - return_reference: bool, - inner: [zend_internal_arg_info; N], - ) -> Self { - Self { - inner: Cell::new(ZendInternalArgInfosWithEnd( - create_zend_arg_info(required_num_args as *const _, return_reference), - inner, - )), - } - } - - pub const fn as_ptr(&self) -> *const zend_internal_arg_info { - self.inner.as_ptr().cast() - } - - pub const fn len(&self) -> usize { - N - } -} - -unsafe impl Sync for MultiInternalArgInfo {} - -pub const fn create_zend_arg_info( - name: *const c_char, - pass_by_ref: bool, -) -> zend_internal_arg_info { - #[cfg(any( - phper_php_version = "8.0", - phper_php_version = "7.4", - phper_php_version = "7.3", - phper_php_version = "7.2" - ))] - { - zend_internal_arg_info { - name, - type_: 0 as crate::sys::zend_type, - pass_by_reference: pass_by_ref as zend_uchar, - is_variadic: 0, - } - } - - #[cfg(any(phper_php_version = "7.1", phper_php_version = "7.0",))] - { - zend_internal_arg_info { - name, - class_name: std::ptr::null(), - type_hint: 0, - allow_null: 0, - pass_by_reference: pass_by_ref as zend_uchar, - is_variadic: 0, - } - } -} - -#[repr(u32)] -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Visibility { - Public = ZEND_ACC_PUBLIC, - Protected = ZEND_ACC_PROTECTED, - Private = ZEND_ACC_PRIVATE, -} diff --git a/phper/src/zend/mod.rs b/phper/src/zend/mod.rs deleted file mode 100644 index b8d8c81a..00000000 --- a/phper/src/zend/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod api; -pub mod classes; -pub mod compile; -pub mod errors; -pub mod ini; -pub mod modules; -pub mod portability; -pub mod throws; -pub mod types; diff --git a/phper/src/zend/portability.rs b/phper/src/zend/portability.rs deleted file mode 100644 index 8b137891..00000000 --- a/phper/src/zend/portability.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/phper/src/zend/types.rs b/phper/src/zend/types.rs deleted file mode 100644 index eceb4de0..00000000 --- a/phper/src/zend/types.rs +++ /dev/null @@ -1,782 +0,0 @@ -use crate::{ - c_str_ptr, - sys::{ - self, _zend_get_parameters_array_ex, phper_get_this, phper_init_class_entry_ex, - phper_z_strval_p, phper_zval_get_type, phper_zval_stringl, zend_arg_info, zend_class_entry, - zend_declare_property_bool, zend_declare_property_long, zend_declare_property_null, - zend_declare_property_stringl, zend_execute_data, zend_long, zend_parse_parameters, - zend_read_property, zend_register_internal_class, zend_throw_exception, - zend_update_property_bool, zend_update_property_long, zend_update_property_null, - zend_update_property_stringl, zval, IS_DOUBLE, IS_FALSE, IS_LONG, IS_NULL, IS_TRUE, - ZEND_RESULT_CODE_SUCCESS, - }, - zend::{api::FunctionEntries, classes::This, compile::Visibility, throws::Throwable}, -}; -use std::{ - borrow::Cow, - cell::Cell, - ffi::{c_void, CStr}, - mem::zeroed, - os::raw::{c_char, c_int}, - ptr::null_mut, - slice, str, -}; - -pub struct ClassEntry { - inner: Cell<*mut zend_class_entry>, -} - -impl ClassEntry { - pub const fn new() -> Self { - Self { - inner: Cell::new(null_mut()), - } - } - - pub const fn as_ptr(&self) -> *mut *mut zend_class_entry { - self.inner.as_ptr() - } - - pub fn get(&self) -> *mut zend_class_entry { - self.inner.get() - } - - pub fn init( - &self, - class_name: impl AsRef, - functions: &FunctionEntries, - ) { - let class_name = class_name.as_ref(); - unsafe { - let mut class_ce = phper_init_class_entry_ex( - class_name.as_ptr().cast(), - class_name.len(), - functions.as_ptr(), - ); - *self.as_ptr() = zend_register_internal_class(&mut class_ce); - } - } - - pub fn declare_property( - &self, - name: impl AsRef, - value: impl HandleProperty, - access_type: Visibility, - ) -> bool { - unsafe { - value.declare_property(self.get(), name.as_ref(), access_type as c_int) - == ZEND_RESULT_CODE_SUCCESS - } - } - - pub fn update_property( - &self, - object: *mut zval, - name: impl AsRef, - value: impl HandleProperty, - ) { - unsafe { value.update_property(self.get(), object, name.as_ref()) } - } - - pub fn read_property(&self, this: *mut zval, name: impl AsRef) -> &mut Val { - let name = name.as_ref(); - unsafe { - let v = zend_read_property( - self.get(), - this, - name.as_ptr().cast(), - name.len(), - 1, - null_mut(), - ); - Val::from_mut(v) - } - } -} - -unsafe impl Sync for ClassEntry {} - -pub trait HandleProperty { - unsafe fn declare_property( - self, - ce: *mut zend_class_entry, - name: &str, - access_type: c_int, - ) -> c_int; - - unsafe fn update_property(self, scope: *mut zend_class_entry, object: *mut zval, name: &str); -} - -impl HandleProperty for () { - unsafe fn declare_property( - self, - ce: *mut zend_class_entry, - name: &str, - access_type: i32, - ) -> i32 { - zend_declare_property_null(ce, name.as_ptr().cast(), name.len(), access_type) - } - - unsafe fn update_property(self, scope: *mut zend_class_entry, object: *mut zval, name: &str) { - zend_update_property_null(scope, object, name.as_ptr().cast(), name.len()) - } -} - -impl HandleProperty for bool { - unsafe fn declare_property( - self, - ce: *mut zend_class_entry, - name: &str, - access_type: i32, - ) -> i32 { - zend_declare_property_bool( - ce, - name.as_ptr().cast(), - name.len(), - self as zend_long, - access_type, - ) - } - - unsafe fn update_property(self, scope: *mut zend_class_entry, object: *mut zval, name: &str) { - zend_update_property_bool( - scope, - object, - name.as_ptr().cast(), - name.len(), - self as zend_long, - ) - } -} - -impl HandleProperty for i64 { - unsafe fn declare_property( - self, - ce: *mut zend_class_entry, - name: &str, - access_type: i32, - ) -> i32 { - zend_declare_property_long( - ce, - name.as_ptr().cast(), - name.len(), - self as zend_long, - access_type, - ) - } - - unsafe fn update_property(self, scope: *mut zend_class_entry, object: *mut zval, name: &str) { - zend_update_property_long( - scope, - object, - name.as_ptr().cast(), - name.len(), - self as zend_long, - ) - } -} - -impl HandleProperty for &str { - unsafe fn declare_property( - self, - ce: *mut zend_class_entry, - name: &str, - access_type: i32, - ) -> c_int { - zend_declare_property_stringl( - ce, - name.as_ptr().cast(), - name.len(), - self.as_ptr().cast(), - self.len(), - access_type, - ) - } - - unsafe fn update_property(self, scope: *mut zend_class_entry, object: *mut zval, name: &str) { - zend_update_property_stringl( - scope, - object, - name.as_ptr().cast(), - name.len(), - self.as_ptr().cast(), - self.len(), - ) - } -} - -#[repr(transparent)] -pub struct ExecuteData { - inner: zend_execute_data, -} - -impl ExecuteData { - #[inline] - pub const fn new(inner: zend_execute_data) -> Self { - Self { inner } - } - - pub unsafe fn from_mut<'a>(ptr: *mut zend_execute_data) -> &'a mut Self { - &mut *(ptr as *mut Self) - } - - pub fn as_mut(&mut self) -> *mut zend_execute_data { - &mut self.inner - } - - #[inline] - pub unsafe fn common_num_args(&self) -> u32 { - (*self.inner.func).common.num_args - } - - #[inline] - pub unsafe fn common_arg_info(&self) -> *mut zend_arg_info { - (*self.inner.func).common.arg_info - } - - #[inline] - pub unsafe fn num_args(&self) -> u32 { - self.inner.This.u2.num_args - } - - #[inline] - pub unsafe fn get_this(&mut self) -> &mut This { - This::from_mut(phper_get_this(&mut self.inner)) - } - - pub unsafe fn get_parameters_array(&mut self) -> Vec { - let num_args = self.num_args(); - let mut arguments = vec![zeroed::(); num_args as usize]; - _zend_get_parameters_array_ex(num_args as c_int, arguments.as_mut_ptr()); - arguments.into_iter().map(Val::new).collect() - } - - pub fn parse_parameters(&self) -> Option { - ::parse(unsafe { self.num_args() } as usize, ()) - } - - pub fn parse_parameters_optional( - &self, - default: O, - ) -> Option { - ::parse(unsafe { self.num_args() } as usize, default) - } -} - -pub trait ParseParameter: Sized { - fn spec() -> Cow<'static, str>; - - fn num_parameters() -> usize; - - fn parameters() -> Vec<*mut c_void>; - - fn from_parameters(parameters: &[*mut c_void]) -> Option; - - fn parse(num_args: usize, optional: O) -> Option { - let parameters = Self::parameters(); - let mut spec = Self::spec(); - - let num_optional = ::num_optional(); - if num_optional > 0 { - let s = spec.to_mut(); - s.insert(s.len() - num_optional, '|'); - unsafe { - optional.set_optional(¶meters); - } - } - - if zend_parse_fixed_parameters(num_args, &spec, ¶meters) { - Self::from_parameters(¶meters) - } else { - None - } - } -} - -impl ParseParameter for () { - #[inline] - fn spec() -> Cow<'static, str> { - Cow::Borrowed("") - } - - fn num_parameters() -> usize { - 0 - } - - #[inline] - fn parameters() -> Vec<*mut c_void> { - Vec::new() - } - - #[inline] - fn from_parameters(_parameters: &[*mut c_void]) -> Option { - Some(()) - } -} - -impl ParseParameter for bool { - #[inline] - fn spec() -> Cow<'static, str> { - Cow::Borrowed("b") - } - - #[inline] - fn num_parameters() -> usize { - 1 - } - - #[inline] - fn parameters() -> Vec<*mut c_void> { - vec![Box::into_raw(Box::new(false)).cast()] - } - - fn from_parameters(parameters: &[*mut c_void]) -> Option { - let b = unsafe { Box::from_raw(parameters[0] as *mut bool) }; - Some(*b) - } -} - -impl ParseParameter for i64 { - #[inline] - fn spec() -> Cow<'static, str> { - Cow::Borrowed("l") - } - - #[inline] - fn num_parameters() -> usize { - 1 - } - - #[inline] - fn parameters() -> Vec<*mut c_void> { - vec![Box::into_raw(Box::new(0i64)).cast()] - } - - fn from_parameters(parameters: &[*mut c_void]) -> Option { - let i = unsafe { Box::from_raw(parameters[0] as *mut i64) }; - Some(*i) - } -} - -impl ParseParameter for f64 { - #[inline] - fn spec() -> Cow<'static, str> { - Cow::Borrowed("d") - } - - #[inline] - fn num_parameters() -> usize { - 1 - } - - #[inline] - fn parameters() -> Vec<*mut c_void> { - vec![Box::into_raw(Box::new(0f64)).cast()] - } - - fn from_parameters(parameters: &[*mut c_void]) -> Option { - let i = unsafe { Box::from_raw(parameters[0] as *mut f64) }; - Some(*i) - } -} - -impl ParseParameter for &str { - #[inline] - fn spec() -> Cow<'static, str> { - Cow::Borrowed("s") - } - - #[inline] - fn num_parameters() -> usize { - 2 - } - - #[inline] - fn parameters() -> Vec<*mut c_void> { - vec![ - Box::into_raw(Box::new(null_mut::())).cast(), - Box::into_raw(Box::new(0u32)).cast(), - ] - } - - fn from_parameters(parameters: &[*mut c_void]) -> Option { - unsafe { - let ptr = Box::from_raw(parameters[0] as *mut *mut c_char); - let len = Box::from_raw(parameters[1] as *mut c_int); - let bytes = slice::from_raw_parts(*ptr as *const u8, *len as usize); - str::from_utf8(bytes).ok() - } - } -} - -macro_rules! impl_parse_parameter_for_tuple { - ( $(($t:ident,$T:ident)),* ) => { - impl<$($T: ParseParameter,)*> ParseParameter for ($($T,)*) { - fn spec() -> Cow<'static, str> { - let mut s= String::new(); - $(s.push_str(&<$T>::spec());)* - Cow::Owned(s) - } - - #[inline] - fn num_parameters() -> usize { - 0 $( + <$T>::num_parameters())* - } - - fn parameters() -> Vec<*mut c_void> { - let mut parameters = Vec::new(); - $(parameters.extend_from_slice(&<$T>::parameters());)* - parameters - } - - fn from_parameters(parameters: &[*mut c_void]) -> Option { - let mut i = 0; - - $(let $t = { - let j = i; - i += <$T>::num_parameters(); - match <$T>::from_parameters(¶meters[j..i]) { - Some(item) => item, - None => return None, - } - };)* - - Some(($($t,)*)) - } - } - }; -} - -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B), (c, C)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F), (g, G)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F), (g, G), (h, H)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F), (g, G), (h, H), (i, I)); -#[rustfmt::skip] impl_parse_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F), (g, G), (h, H), (i, I), (j, J)); - -macro_rules! call_zend_parse_parameters { - ( $num_args:expr, $type_spec:expr, $parameters:expr $(,$i:expr)* ) => { - unsafe { zend_parse_parameters($num_args, $type_spec, $($parameters.get_unchecked($i).clone(),)*) } - } -} - -fn zend_parse_fixed_parameters( - num_args: usize, - type_spec: &str, - parameters: &[*mut c_void], -) -> bool { - assert!(parameters.len() <= 20); - let type_spec = format!("{}\0", type_spec); - - #[cfg(any( - phper_php_version = "7.4", - phper_php_version = "7.3", - phper_php_version = "7.2", - phper_php_version = "7.1", - phper_php_version = "7.0", - ))] - let num_args = num_args as c_int; - - #[cfg(phper_php_version = "8.0")] - let num_args = num_args as u32; - - #[rustfmt::skip] - let b = match parameters.len() { - 0 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters), - 1 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0), - 2 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1), - 3 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2), - 4 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3), - 5 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4), - 6 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5), - 7 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6), - 8 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7), - 9 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8), - 10 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9), - 11 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), - 12 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), - 13 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), - 14 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), - 15 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14), - 16 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), - 17 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), - 18 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), - 19 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18), - 20 => call_zend_parse_parameters!(num_args, type_spec.as_ptr().cast(), parameters, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), - _ => unreachable!(), - }; - - b == ZEND_RESULT_CODE_SUCCESS -} - -pub trait OptionalParameter: ParseParameter { - fn num_optional() -> usize; - unsafe fn set_optional(self, parameters: &[*mut c_void]); -} - -impl OptionalParameter for () { - fn num_optional() -> usize { - 0 - } - - unsafe fn set_optional(self, _parameters: &[*mut c_void]) {} -} - -impl OptionalParameter for bool { - fn num_optional() -> usize { - 1 - } - - unsafe fn set_optional(self, parameters: &[*mut c_void]) { - *(parameters[parameters.len() - 1] as *mut Self) = self; - } -} - -impl OptionalParameter for i64 { - fn num_optional() -> usize { - 1 - } - - unsafe fn set_optional(self, parameters: &[*mut c_void]) { - *(parameters[parameters.len() - 1] as *mut Self) = self; - } -} - -impl OptionalParameter for f64 { - fn num_optional() -> usize { - 1 - } - - unsafe fn set_optional(self, parameters: &[*mut c_void]) { - *(parameters[parameters.len() - 1] as *mut Self) = self; - } -} - -impl OptionalParameter for &'static str { - fn num_optional() -> usize { - 1 - } - - unsafe fn set_optional(self, parameters: &[*mut c_void]) { - *(parameters[parameters.len() - 2] as *mut *const c_char) = self.as_ptr().cast(); - *(parameters[parameters.len() - 1] as *mut c_int) = self.len() as c_int; - } -} - -macro_rules! impl_optional_parameter_for_tuple { - ( $(($i:ident,$T:ident)),* ) => { - impl<$($T: OptionalParameter,)*> OptionalParameter for ($($T,)*) { - fn num_optional() -> usize { - 0 $( + <$T>::num_optional())* - } - - #[allow(unused_assignments)] - unsafe fn set_optional(self, parameters: &[*mut c_void]) { - let mut i = parameters.len() - ::num_parameters(); - let ($($i, )*) = self; - - $({ - let j = i + <$T as ParseParameter>::num_parameters(); - $i.set_optional(¶meters[i..j]); - i = j; - })* - } - } - } -} - -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B), (c, C)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F), (g, G)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F), (g, G), (h, H)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F), (g, G), (h, H), (i, I)); -#[rustfmt::skip] impl_optional_parameter_for_tuple!((a, A), (b, B), (c, C), (d, D), (e, E), (f, F), (g, G), (h, H), (i, I), (j, J)); - -#[repr(transparent)] -pub struct Val { - inner: zval, -} - -impl Val { - #[inline] - pub const fn new(inner: zval) -> Self { - Self { inner } - } - - #[inline] - pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self { - assert!(!ptr.is_null(), "ptr should not be null"); - &mut *(ptr as *mut Self) - } - - pub fn as_mut(&mut self) -> *mut zval { - &mut self.inner - } - - pub fn try_into_value<'a>(&self) -> crate::Result> { - Value::from_ptr(&self.inner) - } - - unsafe fn type_info(&mut self) -> &mut u32 { - &mut self.inner.u1.type_info - } -} - -pub trait SetVal { - fn set_val(self, val: &mut Val); -} - -impl SetVal for () { - fn set_val(self, val: &mut Val) { - unsafe { - *val.type_info() = IS_NULL; - } - } -} - -impl SetVal for bool { - fn set_val(self, val: &mut Val) { - unsafe { - *val.type_info() = if self { IS_TRUE } else { IS_FALSE }; - } - } -} - -impl SetVal for i32 { - fn set_val(self, val: &mut Val) { - (self as i64).set_val(val) - } -} - -impl SetVal for u32 { - fn set_val(self, val: &mut Val) { - (self as i64).set_val(val) - } -} - -impl SetVal for i64 { - fn set_val(self, val: &mut Val) { - unsafe { - (*val.as_mut()).value.lval = self; - (*val.as_mut()).u1.type_info = IS_LONG; - } - } -} - -impl SetVal for f64 { - fn set_val(self, val: &mut Val) { - unsafe { - (*val.as_mut()).value.dval = self; - (*val.as_mut()).u1.type_info = IS_DOUBLE; - } - } -} - -impl SetVal for &str { - fn set_val(self, val: &mut Val) { - unsafe { - phper_zval_stringl(val.as_mut(), self.as_ptr().cast(), self.len()); - } - } -} - -impl SetVal for String { - fn set_val(self, val: &mut Val) { - unsafe { - phper_zval_stringl(val.as_mut(), self.as_ptr().cast(), self.len()); - } - } -} - -impl SetVal for Option { - fn set_val(self, val: &mut Val) { - match self { - Some(t) => t.set_val(val), - None => ().set_val(val), - } - } -} - -impl SetVal for Result { - fn set_val(self, val: &mut Val) { - match self { - Ok(t) => t.set_val(val), - Err(_e) => unsafe { - zend_throw_exception(null_mut(), c_str_ptr!(""), 0); - todo!(); - }, - } - } -} - -#[derive(Debug)] -pub enum Value<'a> { - Null, - Bool(bool), - Long(i64), - Double(f64), - CStr(&'a CStr), - Array(()), - Object(()), - Resource(()), -} - -impl<'a> Value<'a> { - pub fn from_ptr(v: *const zval) -> crate::Result { - unsafe { - match phper_zval_get_type(v) as u32 { - sys::IS_NULL => Ok(Self::Null), - sys::IS_FALSE => Ok(Self::Bool(false)), - sys::IS_TRUE => Ok(Self::Bool(true)), - sys::IS_LONG => Ok(Self::Long((*v).value.lval)), - sys::IS_DOUBLE => Ok(Self::Double((*v).value.dval)), - sys::IS_STRING | sys::IS_STRING_EX => { - let s = phper_z_strval_p(v); - Ok(Self::CStr(CStr::from_ptr(s))) - } - t => Err(crate::Error::UnKnownValueType(t)), - } - } - } - - pub fn into_long(self) -> Option { - match self { - Self::Long(l) => Some(l), - _ => None, - } - } -} - -pub enum ReturnValue<'a> { - Null, - Bool(bool), - Long(i64), - Double(f64), - Str(&'a str), - String(String), - Array(()), - Object(()), - Resource(()), -} - -impl SetVal for ReturnValue<'_> { - fn set_val(self, val: &mut Val) { - match self { - ReturnValue::Null => SetVal::set_val((), val), - ReturnValue::Bool(b) => SetVal::set_val(b, val), - ReturnValue::Long(l) => SetVal::set_val(l, val), - ReturnValue::Double(f) => SetVal::set_val(f, val), - ReturnValue::Str(s) => SetVal::set_val(s.as_ref(), val), - ReturnValue::String(s) => SetVal::set_val(s.as_str(), val), - _ => todo!(), - } - } -} diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 07ade694..00000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly \ No newline at end of file From b9aa4f25c0699371756a394054db702b969da7c6 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sat, 10 Apr 2021 11:10:08 +0800 Subject: [PATCH 05/20] Pass the ini api. --- examples/hello/src/lib.rs | 19 ++++--- phper/src/ini.rs | 102 ++++++++++++++++++++++++++++++-------- phper/src/modules.rs | 76 +++++++++++++++++++++++++--- 3 files changed, 158 insertions(+), 39 deletions(-) diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 31c82212..eaa52437 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -137,19 +137,14 @@ fn test_func() {} #[no_mangle] pub extern "C" fn get_module() -> *const ::phper::sys::zend_module_entry { - // static module: Lazy = Lazy::new(|| { - // let mut module = Module::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); - // module - // }); - // unsafe { - // module.create_zend_module_entry() - // } - let f = |module: &mut Module| { module.set_name(env!("CARGO_PKG_NAME")); module.set_version(env!("CARGO_PKG_VERSION")); - module.add_ini("hello.enable", "off", Policy::All); + module.add_bool_ini("hello.enable", false, Policy::All); + module.add_long_ini("hello.len", 100, Policy::All); + module.add_real_ini("hello.ratio", 1.5, Policy::All); + module.add_str_ini("hello.description", "empty", Policy::All); module.on_module_init(module_init); module.on_module_shutdown(module_shutdown); @@ -157,7 +152,11 @@ pub extern "C" fn get_module() -> *const ::phper::sys::zend_module_entry { module.on_request_shutdown(request_shutdown); module.add_function("hello_fuck", || { - println!("Fuck you!"); + let hello_enable = Module::get_bool_ini("hello.enable"); + dbg!(hello_enable); + + let hello_description = Module::get_str_ini("hello.description"); + dbg!(hello_description); }); module.add_function("test_func", test_func); diff --git a/phper/src/ini.rs b/phper/src/ini.rs index 6ffa6706..80e4b639 100644 --- a/phper/src/ini.rs +++ b/phper/src/ini.rs @@ -1,6 +1,6 @@ use crate::sys::{ zend_ini_entry, zend_ini_entry_def, zend_string, OnUpdateString, PHP_INI_ALL, PHP_INI_PERDIR, - PHP_INI_SYSTEM, PHP_INI_USER, + PHP_INI_SYSTEM, PHP_INI_USER, OnUpdateBool, OnUpdateLong, OnUpdateReal, }; use std::{ cell::Cell, @@ -9,6 +9,20 @@ use std::{ ptr::null_mut, sync::atomic::AtomicPtr, }; +use std::os::raw::c_char; +use std::ffi::CStr; +use std::str; + +type OnModify = Option< +unsafe extern "C" fn( +*mut zend_ini_entry, +*mut zend_string, +*mut c_void, +*mut c_void, +*mut c_void, +c_int, +) -> c_int, +>; #[repr(u32)] #[derive(Copy, Clone)] @@ -19,33 +33,88 @@ pub enum Policy { System = PHP_INI_SYSTEM, } -pub(crate) struct IniEntity { +pub(crate) struct StrPtrBox { + inner: Box<*mut c_char>, +} + +impl StrPtrBox { + pub(crate) unsafe fn to_string(&self) -> Result { + Ok(CStr::from_ptr(*self.inner).to_str()?.to_string() ) + } +} + +impl Default for StrPtrBox { + fn default() -> Self { + Self { + inner: Box::new(null_mut()), + } + } +} + +pub trait IniValue: Default { + fn on_modify() -> OnModify; + + fn arg2(&mut self) -> *mut c_void { + &mut *self as *mut _ as *mut c_void + } +} + +impl IniValue for bool { + fn on_modify() -> OnModify { + Some(OnUpdateBool) + } +} + +impl IniValue for i64 { + fn on_modify() -> OnModify { + Some(OnUpdateLong) + } +} + +impl IniValue for f64 { + fn on_modify() -> OnModify { + Some(OnUpdateReal) + } +} + +impl IniValue for StrPtrBox { + fn on_modify() -> OnModify { + Some(OnUpdateString) + } + + fn arg2(&mut self) -> *mut c_void { + Box::as_mut(&mut self.inner) as *mut _ as *mut c_void + } +} + +pub(crate) struct IniEntity { name: String, - value: usize, + value: T, default_value: String, policy: Policy, } -impl IniEntity { +impl IniEntity { pub(crate) fn new(name: impl ToString, default_value: impl ToString, policy: Policy) -> Self { Self { name: name.to_string(), - value: 0, + value: Default::default(), default_value: default_value.to_string(), policy, } } - // TODO Pass the logic of multi type item. - pub(crate) fn ini_entry_def(&self) -> zend_ini_entry_def { - let arg2: Box<*mut c_void> = Box::new(null_mut()); - let arg2 = Box::into_raw(arg2); + pub(crate) fn value(&self) -> &T { + &self.value + } + + pub(crate) unsafe fn ini_entry_def(&mut self) -> zend_ini_entry_def { create_ini_entry_ex( &self.name, &self.default_value, - Some(OnUpdateString), + ::on_modify(), self.policy as u32, - arg2.cast(), + self.value.arg2(), ) } } @@ -53,16 +122,7 @@ impl IniEntity { pub(crate) fn create_ini_entry_ex( name: &str, default_value: &str, - on_modify: Option< - unsafe extern "C" fn( - *mut zend_ini_entry, - *mut zend_string, - *mut c_void, - *mut c_void, - *mut c_void, - c_int, - ) -> c_int, - >, + on_modify: OnModify, modifiable: u32, arg2: *mut c_void, ) -> zend_ini_entry_def { diff --git a/phper/src/modules.rs b/phper/src/modules.rs index 2eff992e..debc5c98 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -6,6 +6,7 @@ use crate::{ zend_class_entry, zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, zend_module_entry, zend_register_ini_entries, zend_string, zend_unregister_ini_entries, PHP_MODULE_BUILD_ID, USING_ZTS, ZEND_DEBUG, ZEND_MODULE_API_NO, + OnUpdateBool, OnUpdateLong, OnUpdateReal, OnUpdateString, }, }; use once_cell::sync::Lazy; @@ -19,6 +20,9 @@ use std::{ ptr::{null, null_mut}, sync::{atomic::AtomicPtr, Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; +use std::ffi::CStr; +use crate::ini::{StrPtrBox, IniValue}; +use std::thread::LocalKey; static GLOBAL_MODULE: Lazy> = Lazy::new(Default::default); @@ -79,12 +83,18 @@ pub struct Module { module_shutdown: Option bool + Send + Sync>>, request_init: Option bool + Send + Sync>>, request_shutdown: Option bool + Send + Sync>>, - ini_entities: HashMap, function_entities: Vec, class_entities: Vec, } impl Module { + thread_local! { + static BOOL_INI_ENTITIES: RefCell>> = Default::default(); + static LONG_INI_ENTITIES: RefCell>> = Default::default(); + static REAL_INI_ENTITIES: RefCell>> = Default::default(); + static STR_INI_ENTITIES: RefCell>> = Default::default(); + } + pub fn set_name(&mut self, name: impl ToString) { let mut name = name.to_string(); name.push('\0'); @@ -119,9 +129,52 @@ impl Module { self.request_shutdown = Some(Box::new(func)); } - pub fn add_ini(&mut self, name: impl ToString, value: impl ToString, policy: Policy) { - self.ini_entities - .insert(name.to_string(), IniEntity::new(name, value, policy)); + pub fn add_bool_ini(&mut self, name: impl ToString, default_value: bool, policy: Policy) { + Self::BOOL_INI_ENTITIES.with(|entities| { + entities.borrow_mut().insert(name.to_string(), IniEntity::new(name, default_value, policy)); + }) + } + + pub fn get_bool_ini(name: &str) -> Option { + Self::BOOL_INI_ENTITIES.with(|entities| { + entities.borrow().get(name).map(|entity| *entity.value()) + }) + } + + pub fn add_long_ini(&mut self, name: impl ToString, default_value: i64, policy: Policy) { + Self::LONG_INI_ENTITIES.with(|entities| { + entities.borrow_mut().insert(name.to_string(), IniEntity::new(name, default_value, policy)); + }) + } + + pub fn get_long_ini(name: &str) -> Option { + Self::LONG_INI_ENTITIES.with(|entities| { + entities.borrow().get(name).map(|entity| *entity.value()) + }) + } + + pub fn add_real_ini(&mut self, name: impl ToString, default_value: f64, policy: Policy) { + Self::REAL_INI_ENTITIES.with(|entities| { + entities.borrow_mut().insert(name.to_string(), IniEntity::new(name, default_value, policy)); + }) + } + + pub fn get_real_ini(name: &str) -> Option { + Self::REAL_INI_ENTITIES.with(|entities| { + entities.borrow().get(name).map(|entity| *entity.value()) + }) + } + + pub fn add_str_ini(&mut self, name: impl ToString, default_value: impl ToString, policy: Policy) { + Self::STR_INI_ENTITIES.with(|entities| { + entities.borrow_mut().insert(name.to_string(), IniEntity::new(name, default_value, policy)); + }) + } + + pub fn get_str_ini(name: &str) -> Option { + Self::STR_INI_ENTITIES.with(|entities| { + entities.borrow().get(name).and_then(|entity| unsafe { entity.value().to_string() }.ok()) + }) } pub fn add_function(&mut self, name: impl ToString, handler: impl Function + 'static) { @@ -207,17 +260,24 @@ impl Module { Box::into_raw(entries.into_boxed_slice()).cast() } - fn ini_entries(&self) -> *const zend_ini_entry_def { + unsafe fn ini_entries(&self) -> *const zend_ini_entry_def { let mut entries = Vec::new(); - for (_, ini) in &self.ini_entities { - entries.push(ini.ini_entry_def()); - } + Self::BOOL_INI_ENTITIES.with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); + Self::LONG_INI_ENTITIES.with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); + Self::REAL_INI_ENTITIES.with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); + Self::STR_INI_ENTITIES.with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); entries.push(unsafe { zeroed::() }); Box::into_raw(entries.into_boxed_slice()).cast() } + + unsafe fn push_ini_entry(entries: &mut Vec, entities: &mut HashMap>) { + for (_, entry) in &mut *entities.borrow_mut() { + entries.push(entry.ini_entry_def()); + } + } } pub struct ModuleArgs { From ba5464ba753c74bee5f5b8b0aa475108cae0c907 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sun, 11 Apr 2021 00:52:17 +0800 Subject: [PATCH 06/20] Refacotr global module read and write api. --- examples/hello/src/lib.rs | 12 ++++---- phper/src/modules.rs | 59 +++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index eaa52437..c755853f 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -137,7 +137,7 @@ fn test_func() {} #[no_mangle] pub extern "C" fn get_module() -> *const ::phper::sys::zend_module_entry { - let f = |module: &mut Module| { + write_global_module(|module| { module.set_name(env!("CARGO_PKG_NAME")); module.set_version(env!("CARGO_PKG_VERSION")); @@ -166,9 +166,11 @@ pub extern "C" fn get_module() -> *const ::phper::sys::zend_module_entry { println!("hello test1"); }); module.add_class("Test1", std_class); - }; + }); - f(&mut *write_global_module()); - - unsafe { read_global_module().module_entry() } + unsafe { + read_global_module(|module| { + module.module_entry() + }) + } } diff --git a/phper/src/modules.rs b/phper/src/modules.rs index debc5c98..f022b099 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -26,53 +26,58 @@ use std::thread::LocalKey; static GLOBAL_MODULE: Lazy> = Lazy::new(Default::default); -pub fn read_global_module() -> RwLockReadGuard<'static, Module> { - (&*GLOBAL_MODULE).read().expect("get write lock failed") +pub fn read_global_module(f: impl FnOnce(&Module) -> R) -> R { + let module = (&*GLOBAL_MODULE).read().expect("get write lock failed"); + f(&module) } -pub fn write_global_module() -> RwLockWriteGuard<'static, Module> { - (&*GLOBAL_MODULE).write().expect("get write lock failed") +pub fn write_global_module(f: impl FnOnce(&mut Module) -> R) -> R { + let mut module = (&*GLOBAL_MODULE).write().expect("get write lock failed"); + f(&mut module) } unsafe extern "C" fn module_startup(r#type: c_int, module_number: c_int) -> c_int { let args = ModuleArgs::new(r#type, module_number); - { - args.register_ini_entries(read_global_module().ini_entries()); - } - { - for class_entity in &read_global_module().class_entities { + read_global_module(|module| { + args.register_ini_entries(module.ini_entries()); + for class_entity in &module.class_entities { class_entity.init(); class_entity.declare_properties(); } - } - match &read_global_module().module_init { - Some(f) => f(args) as c_int, - None => 1, - } + match &module.module_init { + Some(f) => f(args) as c_int, + None => 1, + } + }) } unsafe extern "C" fn module_shutdown(r#type: c_int, module_number: c_int) -> c_int { let args = ModuleArgs::new(r#type, module_number); args.unregister_ini_entries(); - - match &read_global_module().module_shutdown { - Some(f) => f(args) as c_int, - None => 1, - } + read_global_module(|module| { + match &module.module_shutdown { + Some(f) => f(args) as c_int, + None => 1, + } + }) } unsafe extern "C" fn request_startup(r#type: c_int, request_number: c_int) -> c_int { - match &read_global_module().request_init { - Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, - None => 1, - } + read_global_module(|module| { + match &module.request_init { + Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, + None => 1, + } + }) } unsafe extern "C" fn request_shutdown(r#type: c_int, request_number: c_int) -> c_int { - match &read_global_module().request_shutdown { - Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, - None => 1, - } + read_global_module(|module| { + match &module.request_shutdown { + Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, + None => 1, + } + }) } #[derive(Default)] From 0f86a71782e2f2aa7645eec2782e13edd6be4a6a Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sun, 11 Apr 2021 00:54:55 +0800 Subject: [PATCH 07/20] Remove unused codes. --- README.md | 1 + phper-alloc/src/lib.rs | 69 ------------------------------------------ 2 files changed, 1 insertion(+), 69 deletions(-) diff --git a/README.md b/README.md index dc49b4f5..c3014195 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ A library that allows us to write PHP extensions using pure Rust and using safe ## Requirement +- php version: - libclang >= 9 ## Usage diff --git a/phper-alloc/src/lib.rs b/phper-alloc/src/lib.rs index 7e9bae32..772c0b46 100644 --- a/phper-alloc/src/lib.rs +++ b/phper-alloc/src/lib.rs @@ -7,72 +7,3 @@ pub type EBox = Box; /// The Vec which use php `emalloc` and `efree` to manage memory. /// TODO now feature `allocator_api` is still unstable, using global allocator instead. pub type EVec = Vec; - -// pub struct Allocator { -// #[cfg(phper_debug)] -// zend_filename: *const std::os::raw::c_char, -// #[cfg(phper_debug)] -// zend_lineno: u32, -// #[cfg(phper_debug)] -// zend_orig_filename: *const std::os::raw::c_char, -// #[cfg(phper_debug)] -// zend_orig_lineno: u32, -// } -// -// impl Allocator { -// pub const fn new( -// #[cfg(phper_debug)] zend_filename: *const std::os::raw::c_char, -// #[cfg(phper_debug)] zend_lineno: u32, -// #[cfg(phper_debug)] zend_orig_filename: *const std::os::raw::c_char, -// #[cfg(phper_debug)] zend_orig_lineno: u32, -// ) -> Self { -// Self { -// #[cfg(phper_debug)] -// zend_filename, -// #[cfg(phper_debug)] -// zend_lineno, -// #[cfg(phper_debug)] -// zend_orig_filename, -// #[cfg(phper_debug)] -// zend_orig_lineno, -// } -// } -// } -// -// unsafe impl AllocRef for Allocator { -// fn alloc(&self, layout: Layout) -> Result, AllocError> { -// unsafe { -// #[cfg(phper_debug)] -// let ptr = _emalloc( -// layout.size(), -// self.zend_filename, -// self.zend_lineno, -// self.zend_orig_filename, -// self.zend_orig_lineno, -// ); -// #[cfg(not(phper_debug))] -// let ptr = _emalloc(layout.size()); -// -// if ptr.is_null() { -// Err(AllocError) -// } else { -// let ptr = slice_from_raw_parts_mut(ptr.cast(), layout.size()); -// Ok(NonNull::new_unchecked(ptr)) -// } -// } -// } -// -// unsafe fn dealloc(&self, ptr: NonNull, _layout: Layout) { -// // Not the correct position of `efree`, but can work!. -// #[cfg(phper_debug)] -// _efree( -// ptr.as_ptr().cast(), -// self.zend_filename, -// self.zend_lineno, -// self.zend_orig_filename, -// self.zend_orig_lineno, -// ); -// #[cfg(not(phper_debug))] -// _efree(ptr.as_ptr().cast()); -// } -// } From c663ca6344c15196ae6dec136c34ef37727f3965 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sun, 11 Apr 2021 19:48:27 +0800 Subject: [PATCH 08/20] Add SetVal trait, module info hook, and remove unused codes. --- Cargo.toml | 1 - README.md | 21 +-- examples/hello-class/Cargo.toml | 21 --- examples/hello-class/Makefile.toml | 30 ---- examples/hello-class/build.rs | 3 - examples/hello-class/src/lib.rs | 148 ----------------- examples/hello-class/tests/integration.rs | 17 -- examples/hello-class/tests/php/test.php | 13 -- examples/hello/src/lib.rs | 189 +++++----------------- phper-macros/src/alloc.rs | 10 -- phper-macros/src/inner.rs | 93 +---------- phper-macros/src/lib.rs | 46 ------ phper-sys/php_wrapper.c | 18 ++- phper-sys/php_wrapper.h | 6 +- phper/src/cmd.rs | 4 +- phper/src/errors.rs | 12 +- phper/src/functions.rs | 36 ++--- phper/src/ini.rs | 26 +-- phper/src/modules.rs | 126 +++++++++------ phper/src/throws.rs | 4 + phper/src/values.rs | 112 ++++++++++++- 21 files changed, 305 insertions(+), 631 deletions(-) delete mode 100644 examples/hello-class/Cargo.toml delete mode 100644 examples/hello-class/Makefile.toml delete mode 100644 examples/hello-class/build.rs delete mode 100644 examples/hello-class/src/lib.rs delete mode 100644 examples/hello-class/tests/integration.rs delete mode 100644 examples/hello-class/tests/php/test.php diff --git a/Cargo.toml b/Cargo.toml index 06d743a9..c1249312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,5 @@ members = [ # internal "examples/hello", -# "examples/hello-class", # "examples/mini-curl", ] diff --git a/README.md b/README.md index c3014195..5d7d7fec 100644 --- a/README.md +++ b/README.md @@ -2,31 +2,16 @@ A library that allows us to write PHP extensions using pure Rust and using safe Rust whenever possible. -***Now the project is still under development.*** - ## Requirement -- php version: -- libclang >= 9 +- os: linux +- libclang: version >= 9 +- php: version in (7.1, 7.2, 7.3, 7.4, 8.0), mode: nts ## Usage Now see [examples](examples). - - ## License [Unlicense](LICENSE). diff --git a/examples/hello-class/Cargo.toml b/examples/hello-class/Cargo.toml deleted file mode 100644 index f541aa45..00000000 --- a/examples/hello-class/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "hello_class" -version = "0.2.0" -authors = ["jmjoy <918734043@qq.com>"] -edition = "2018" -publish = false -license = "Unlicense" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["cdylib"] - -[dependencies] -phper = { version = "0.2", path = "../../phper" } - -[dev-dependencies] -phper-test = { version = "0.2", path = "../../phper-test" } - -[build-dependencies] -phper-build = { version = "0.2", path = "../../phper-build" } diff --git a/examples/hello-class/Makefile.toml b/examples/hello-class/Makefile.toml deleted file mode 100644 index 5f4e9061..00000000 --- a/examples/hello-class/Makefile.toml +++ /dev/null @@ -1,30 +0,0 @@ -[env] -PHP_CONFIG = "php-config" -TARGET_DIR = "${CARGO_MAKE_WORKING_DIRECTORY}/../../target" - -[env.development] -TARGET_BUILD_DIR = "${TARGET_DIR}/debug" -BUILD_ARGS = "--" -TEST_ARGS = "--" - -[env.production] -TARGET_BUILD_DIR = "${TARGET_DIR}/release" -BUILD_ARGS = "--release" -TEST_ARGS = "--release" - -[tasks.build] -command = "cargo" -args = ["build", "${BUILD_ARGS}"] - -[tasks.test] -command = "cargo" -args = ["test", "${TEST_ARGS}", "--", "--nocapture"] - -[tasks.install] -dependencies = ["build"] -script = [ - """ - cp ${TARGET_BUILD_DIR}/lib${CARGO_MAKE_CRATE_NAME}.so `${PHP_CONFIG} --extension-dir`/${CARGO_MAKE_CRATE_NAME}.so && \ - echo Installing shared extensions: `${PHP_CONFIG} --extension-dir` - """ -] diff --git a/examples/hello-class/build.rs b/examples/hello-class/build.rs deleted file mode 100644 index 8be39f4f..00000000 --- a/examples/hello-class/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - phper_build::register_configures(); -} diff --git a/examples/hello-class/src/lib.rs b/examples/hello-class/src/lib.rs deleted file mode 100644 index e0939154..00000000 --- a/examples/hello-class/src/lib.rs +++ /dev/null @@ -1,148 +0,0 @@ -use phper::{ - c_str_ptr, php_minfo_function, php_minit_function, - php_mshutdown_function, php_rinit_function, - php_rshutdown_function, - sys::{ - php_info_print_table_end, php_info_print_table_row, php_info_print_table_start, - zend_function_entry, OnUpdateBool, OnUpdateString, PHP_INI_SYSTEM, - }, - zend::{ - api::{FunctionEntries, ModuleGlobals}, - compile::{create_zend_arg_info, MultiInternalArgInfo, Visibility}, - ini::IniEntries, - modules::{ModuleArgs, ModuleEntry, ModuleEntryBuilder}, - types::{ClassEntry, ExecuteData, SetVal, Value}, - }, - php_get_module, -}; -use std::{os::raw::c_char, ptr::null}; - -static HELLO_CLASS_CE: ClassEntry = ClassEntry::new(); - -static HELLO_CLASS_ENABLE: ModuleGlobals = ModuleGlobals::new(false); -static HELLO_CLASS_DESCRIPTION: ModuleGlobals<*const c_char> = ModuleGlobals::new(null()); - -static INI_ENTRIES: IniEntries<2> = IniEntries::new([ - HELLO_CLASS_ENABLE.create_ini_entry( - "hello_class.enable", - "1", - Some(OnUpdateBool), - PHP_INI_SYSTEM, - ), - HELLO_CLASS_DESCRIPTION.create_ini_entry( - "hello_class.description", - "", - Some(OnUpdateString), - PHP_INI_SYSTEM, - ), -]); - -#[php_minit_function] -fn module_init(args: ModuleArgs) -> bool { - args.register_ini_entries(&INI_ENTRIES); - HELLO_CLASS_CE.init("HelloClass", &HELLO_CLASS_METHODS); - HELLO_CLASS_CE.declare_property("name", "world", Visibility::Public); - true -} - -#[php_mshutdown_function] -fn module_shutdown(args: ModuleArgs) -> bool { - args.unregister_ini_entries(); - true -} - -#[php_rinit_function] -fn request_init(_args: ModuleArgs) -> bool { - true -} - -#[php_rshutdown_function] -fn request_shutdown(_args: ModuleArgs) -> bool { - true -} - -#[php_minfo_function] -fn module_info(module: &ModuleEntry) { - unsafe { - php_info_print_table_start(); - php_info_print_table_row( - 2, - c_str_ptr!("hello_class.version"), - (*module.as_ptr()).version, - ); - php_info_print_table_row( - 2, - c_str_ptr!("hello_class.build_id"), - (*module.as_ptr()).build_id, - ); - php_info_print_table_row( - 2, - c_str_ptr!("hello_class.enable"), - if HELLO_CLASS_ENABLE.get() { - c_str_ptr!("1") - } else { - c_str_ptr!("0") - }, - ); - php_info_print_table_row( - 2, - c_str_ptr!("hello_class.description"), - HELLO_CLASS_DESCRIPTION.get(), - ); - php_info_print_table_end(); - } -} - -static ARG_INFO_HELLO_CLASS_SAY_HELLO: MultiInternalArgInfo<1> = MultiInternalArgInfo::new( - 1, - false, - [create_zend_arg_info(c_str_ptr!("prefix"), false)], -); - -static HELLO_CLASS_METHODS: FunctionEntries<1> = FunctionEntries::new([zend_function_entry { - fname: c_str_ptr!("sayHello"), - handler: Some(php_fn!(hello_class_get_hello)), - arg_info: ARG_INFO_HELLO_CLASS_SAY_HELLO.as_ptr(), - num_args: 1, - flags: 0, -}]); - -#[php_function] -pub fn hello_class_get_hello(execute_data: &mut ExecuteData) -> impl SetVal { - execute_data.parse_parameters::<()>().map(|_| { - let this = execute_data.get_this(); - let val = HELLO_CLASS_CE.read_property(this, "name"); - let value = val.try_into_value().unwrap(); - - if let Value::CStr(value) = value { - Some(format!("Hello, {}!", value.to_str().unwrap())) - } else { - None - } - }) -} - -static FUNCTION_ENTRIES: FunctionEntries<1> = FunctionEntries::new([zend_function_entry { - fname: c_str_ptr!("hello_class_get_hello"), - handler: Some(php_fn!(hello_class_get_hello)), - arg_info: null(), - num_args: 0, - flags: 0, -}]); - -static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( - c_str_ptr!(env!("CARGO_PKG_NAME")), - c_str_ptr!(env!("CARGO_PKG_VERSION")), -) -.functions(FUNCTION_ENTRIES.as_ptr()) -.module_startup_func(php_minit!(module_init)) -.module_shutdown_func(php_mshutdown!(module_shutdown)) -.request_startup_func(php_rinit!(request_init)) -.request_shutdown_func(php_rshutdown!(request_shutdown)) -.info_func(php_minfo!(module_info)) -.build(); - -#[php_get_module] -pub fn get_module() -> &'static ModuleEntry { - &MODULE_ENTRY -} diff --git a/examples/hello-class/tests/integration.rs b/examples/hello-class/tests/integration.rs deleted file mode 100644 index 573b6660..00000000 --- a/examples/hello-class/tests/integration.rs +++ /dev/null @@ -1,17 +0,0 @@ -use phper_test::test_php_scripts; -use std::{env, path::Path}; - -#[test] -fn test_php() { - test_php_scripts( - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("target"), - env!("CARGO_PKG_NAME"), - &[Path::new(env!("CARGO_MANIFEST_DIR")) - .join("tests") - .join("php") - .join("test.php")], - ); -} diff --git a/examples/hello-class/tests/php/test.php b/examples/hello-class/tests/php/test.php deleted file mode 100644 index 65f03f2c..00000000 --- a/examples/hello-class/tests/php/test.php +++ /dev/null @@ -1,13 +0,0 @@ -sayHello(), "Hello, world!"); - -function assert_eq($left, $right) { - if ($left !== $right) { - throw new Exception("left != right,\n left: {$left},\n right: {$right};"); - } -} \ No newline at end of file diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index c755853f..a32f44a4 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -1,176 +1,67 @@ -use std::{fs::OpenOptions, io::Write}; - use phper::{ c_str_ptr, classes::{Class, MethodEntity, StdClass, This}, functions::create_zend_arg_info, ini::Policy, modules::{read_global_module, write_global_module, Module, ModuleArgs}, - php_function, php_get_module, php_minfo_function, php_minit_function, php_mshutdown_function, - php_rinit_function, php_rshutdown_function, + php_get_module, sys::{ php_info_print_table_end, php_info_print_table_row, php_info_print_table_start, zend_function_entry, OnUpdateBool, PHP_INI_SYSTEM, }, + throws::{Exception, Throwable}, values::{ExecuteData, Val}, }; - -// static HELLO_ENABLE: ModuleGlobals = ModuleGlobals::new(false); -// -// static INI_ENTRIES: IniEntries<1> = IniEntries::new([HELLO_ENABLE.create_ini_entry( -// "hello.enable", -// "1", -// Some(OnUpdateBool), -// PHP_INI_SYSTEM, -// )]); -// -// #[php_minit_function] -// fn module_init(args: ModuleArgs) -> bool { -// args.register_ini_entries(&INI_ENTRIES); -// true -// } -// -// #[php_mshutdown_function] -// fn module_shutdown(args: ModuleArgs) -> bool { -// args.unregister_ini_entries(); -// true -// } -// -// #[php_rinit_function] -// fn request_init(_args: ModuleArgs) -> bool { -// true -// } -// -// #[php_rshutdown_function] -// fn request_shutdown(_args: ModuleArgs) -> bool { -// true -// } -// -// #[php_minfo_function] -// fn module_info(module: &ModuleEntry) { -// unsafe { -// php_info_print_table_start(); -// php_info_print_table_row(2, c_str_ptr!("hello.version"), (*module.as_ptr()).version); -// php_info_print_table_row( -// 2, -// c_str_ptr!("hello.enable"), -// if HELLO_ENABLE.get() { -// c_str_ptr!("1") -// } else { -// c_str_ptr!("0") -// }, -// ); -// php_info_print_table_end(); -// } -// } -// -// #[php_function] -// pub fn say_hello(execute_data: &mut ExecuteData) -> impl SetVal { -// execute_data -// .parse_parameters::<&str>() -// .map(|name| format!("Hello, {}!", name)) -// } -// -// static ARG_INFO_SAY_HELLO: MultiInternalArgInfo<1> = -// MultiInternalArgInfo::new(1, false, [create_zend_arg_info(c_str_ptr!("n_ame"), false)]); -// -// static FUNCTION_ENTRIES: FunctionEntries<1> = FunctionEntries::new([zend_function_entry { -// fname: c_str_ptr!("say_hello"), -// handler: Some(say_hello), -// arg_info: ARG_INFO_SAY_HELLO.as_ptr(), -// num_args: 2, -// flags: 0, -// }]); -// -// static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( -// c_str_ptr!(env!("CARGO_PKG_NAME")), -// c_str_ptr!(env!("CARGO_PKG_VERSION")), -// ) -// .functions(FUNCTION_ENTRIES.as_ptr()) -// .module_startup_func(module_init) -// .module_shutdown_func(module_shutdown) -// .request_startup_func(request_init) -// .request_shutdown_func(request_shutdown) -// .info_func(module_info) -// .build(); - -// pub fn get_module() -> Module { -// let mut module = Module::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); -// module.on_module_init(Box::new(|_| { -// println!("Are you ok?"); -// true -// })); -// module -// } +use std::{fs::OpenOptions, io::Write}; fn module_init(_args: ModuleArgs) -> bool { - // append_file("module_init"); true } -fn module_shutdown(_args: ModuleArgs) -> bool { - // append_file("module_shutdown"); - true +fn say_hello(arguments: &mut [Val]) -> String { + let name = arguments[0].as_string(); + format!("Hello, {}\n", name) } -fn request_init(_args: ModuleArgs) -> bool { - // append_file("request_init"); - true -} - -fn request_shutdown(_args: ModuleArgs) -> bool { - // append_file("request_shutdown"); - true -} - -fn append_file(s: &str) { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .open("/tmp/hello") - .unwrap(); - - writeln!(file, "{}", s).unwrap(); -} - -fn test_func() {} - -#[no_mangle] -pub extern "C" fn get_module() -> *const ::phper::sys::zend_module_entry { - write_global_module(|module| { - module.set_name(env!("CARGO_PKG_NAME")); - module.set_version(env!("CARGO_PKG_VERSION")); - - module.add_bool_ini("hello.enable", false, Policy::All); - module.add_long_ini("hello.len", 100, Policy::All); - module.add_real_ini("hello.ratio", 1.5, Policy::All); - module.add_str_ini("hello.description", "empty", Policy::All); - - module.on_module_init(module_init); - module.on_module_shutdown(module_shutdown); - module.on_request_init(request_init); - module.on_request_shutdown(request_shutdown); - - module.add_function("hello_fuck", || { +#[php_get_module] +pub extern "C" fn get_module(module: &mut Module) { + // set module metadata + module.set_name(env!("CARGO_PKG_NAME")); + module.set_version(env!("CARGO_PKG_VERSION")); + module.set_author(env!("CARGO_PKG_AUTHORS")); + + // register module ini + module.add_bool_ini("hello.enable", false, Policy::All); + module.add_long_ini("hello.num", 100, Policy::All); + module.add_real_ini("hello.ratio", 1.5, Policy::All); + module.add_str_ini("hello.description", "hello world.", Policy::All); + + // register hook functions + module.on_module_init(module_init); + module.on_module_shutdown(|_| true); + module.on_request_init(|_| true); + module.on_request_shutdown(|_| true); + + // register functions + module.add_function("hello_say_hello", say_hello); + module.add_function( + "hello_get_all_ini", + |_: &mut [Val]| -> Result { let hello_enable = Module::get_bool_ini("hello.enable"); dbg!(hello_enable); let hello_description = Module::get_str_ini("hello.description"); dbg!(hello_description); - }); - module.add_function("test_func", test_func); - let mut std_class = StdClass::new(); - std_class.add_property("foo", 100); - std_class.add_method("test1", |_: &mut This| { - println!("hello test1"); - }); - module.add_class("Test1", std_class); - }); + Ok(String::new()) + }, + ); - unsafe { - read_global_module(|module| { - module.module_entry() - }) - } + // register classes + let mut std_class = StdClass::new(); + std_class.add_property("foo", 100); + std_class.add_method("test1", |_: &mut This| { + println!("hello test1"); + }); + module.add_class("Test1", std_class); } diff --git a/phper-macros/src/alloc.rs b/phper-macros/src/alloc.rs index 0ae42a2e..8b137891 100644 --- a/phper-macros/src/alloc.rs +++ b/phper-macros/src/alloc.rs @@ -1,11 +1 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, Expr}; -pub(crate) fn ebox(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as Expr); - let result = quote! { - ::phper::alloc::EBox::new_in(#input, ::phper::alloc::Allocator::new(#[cfg(phper_debug)] ::phper::c_str_ptr!(file!()), #[cfg(phper_debug)] ::std::line!(), #[cfg(phper_debug)] ::std::ptr::null(), #[cfg(phper_debug)] 0)) - }; - result.into() -} diff --git a/phper-macros/src/inner.rs b/phper-macros/src/inner.rs index 1d68894c..f30b1c4b 100644 --- a/phper-macros/src/inner.rs +++ b/phper-macros/src/inner.rs @@ -2,89 +2,6 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, ItemFn, Visibility}; -pub(crate) fn hook_fn(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as ItemFn); - - let name = &input.sig.ident; - let inputs = &input.sig.inputs; - let ret = &input.sig.output; - let body = &input.block; - let attrs = &input.attrs; - - let result = quote! { - #(#attrs)* - extern "C" fn #name(type_: ::std::os::raw::c_int, module_number: ::std::os::raw::c_int) -> ::std::os::raw::c_int { - fn internal(#inputs) #ret { - #body - } - - let internal: fn(::phper::zend::modules::ModuleArgs) -> bool = internal; - - if internal(::phper::zend::modules::ModuleArgs::new(type_, module_number)) { - ::phper::sys::ZEND_RESULT_CODE_SUCCESS - } else { - ::phper::sys::ZEND_RESULT_CODE_FAILURE - } - } - }; - - result.into() -} - -pub(crate) fn info_fn(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as ItemFn); - - let name = &input.sig.ident; - let inputs = &input.sig.inputs; - let ret = &input.sig.output; - let body = &input.block; - let attrs = &input.attrs; - - let result = quote! { - #(#attrs)* - extern "C" fn #name(zend_module: *mut ::phper::sys::zend_module_entry) { - fn internal(#inputs) #ret { - #body - } - - let internal: fn(&::phper::zend::modules::ModuleEntry) = internal; - internal(::phper::zend::modules::ModuleEntry::from_ptr(zend_module)) - } - }; - - result.into() -} - -pub(crate) fn php_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as ItemFn); - - let vis = &input.vis; - let ret = &input.sig.output; - let inputs = &input.sig.inputs; - let name = &input.sig.ident; - let body = &input.block; - let attrs = &input.attrs; - - let result = quote! { - #(#attrs)* - #vis extern "C" fn #name( - execute_data: *mut ::phper::sys::zend_execute_data, - return_value: *mut ::phper::sys::zval - ) { - fn internal(#inputs) #ret { - #body - } - let internal: fn(&mut ::phper::zend::types::ExecuteData) -> _ = internal; - unsafe { - let value = internal(::phper::zend::types::ExecuteData::from_mut(execute_data)); - ::phper::zend::types::SetVal::set_val(value, ::phper::zend::types::Val::from_mut(return_value)); - } - } - }; - - result.into() -} - pub(crate) fn php_get_module(_attr: TokenStream, input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as ItemFn); @@ -110,8 +27,14 @@ pub(crate) fn php_get_module(_attr: TokenStream, input: TokenStream) -> TokenStr fn internal(#inputs) #ret { #body } - let internal: fn() -> &::phper::zend::modules::ModuleEntry = internal; - internal().as_ptr() + let internal: fn(module: &mut ::phper::modules::Module) = internal; + + ::phper::modules::write_global_module(internal); + unsafe { + ::phper::modules::read_global_module(|module| { + module.module_entry() + }) + } } }; diff --git a/phper-macros/src/lib.rs b/phper-macros/src/lib.rs index 7720d132..2ad5ff0a 100644 --- a/phper-macros/src/lib.rs +++ b/phper-macros/src/lib.rs @@ -2,7 +2,6 @@ mod alloc; mod inner; mod utils; -use crate::inner::{hook_fn, info_fn}; use proc_macro::TokenStream; #[proc_macro] @@ -15,52 +14,7 @@ pub fn c_str_ptr(input: TokenStream) -> TokenStream { utils::c_str_ptr(input) } -#[proc_macro] -pub fn ebox(input: TokenStream) -> TokenStream { - alloc::ebox(input) -} - -#[proc_macro_attribute] -pub fn php_function(attr: TokenStream, input: TokenStream) -> TokenStream { - inner::php_function(attr, input) -} - #[proc_macro_attribute] pub fn php_get_module(attr: TokenStream, input: TokenStream) -> TokenStream { inner::php_get_module(attr, input) } - -#[proc_macro_attribute] -pub fn php_minit_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input) -} - -#[proc_macro_attribute] -pub fn php_mshutdown_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input) -} - -#[proc_macro_attribute] -pub fn php_rinit_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input) -} - -#[proc_macro_attribute] -pub fn php_rshutdown_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input) -} - -#[proc_macro_attribute] -pub fn php_ginit_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input) -} - -#[proc_macro_attribute] -pub fn php_gshutdown_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - hook_fn(input) -} - -#[proc_macro_attribute] -pub fn php_minfo_function(_attr: TokenStream, input: TokenStream) -> TokenStream { - info_fn(input) -} diff --git a/phper-sys/php_wrapper.c b/phper-sys/php_wrapper.c index 3d50d9ed..de9829d0 100644 --- a/phper-sys/php_wrapper.c +++ b/phper-sys/php_wrapper.c @@ -32,4 +32,20 @@ char *phper_z_strval_p(const zval *v) { zval *phper_get_this(zend_execute_data *execute_data) { return getThis(); -} \ No newline at end of file +} + +void phper_zval_zval(zval *return_value, zval *zv, int copy, int dtor) { + ZVAL_ZVAL(return_value, zv, copy, dtor); +} + +zend_string *phper_zval_get_string(zval *op) { + return zval_get_string(op); +} + +void phper_zend_string_release(zend_string *s) { + return zend_string_release(s); +} + +zend_long phper_zval_get_long(zval *op) { + return zval_get_long(op); +} diff --git a/phper-sys/php_wrapper.h b/phper-sys/php_wrapper.h index 04e07803..b231f401 100644 --- a/phper-sys/php_wrapper.h +++ b/phper-sys/php_wrapper.h @@ -6,7 +6,7 @@ #include #include -typedef void (ZEND_FASTCALL *zif_handler)(INTERNAL_FUNCTION_PARAMETERS); +typedef ZEND_INI_MH(phper_zend_ini_mh); zend_string *zend_string_init_(const char *str, size_t len, int persistent); zend_string *zend_new_interned_string_(zend_string *str); @@ -16,5 +16,9 @@ zend_uchar phper_zval_get_type(const zval* pz); void phper_zval_stringl(zval *return_value, const char *s, size_t len); char *phper_z_strval_p(const zval *v); zval *phper_get_this(zend_execute_data *execute_data); +void phper_zval_zval(zval *return_value, zval *zv, int copy, int dtor); +zend_string *phper_zval_get_string(zval *op); +void phper_zend_string_release(zend_string *s); +zend_long phper_zval_get_long(zval *op); #endif //PHPER_PHP_WRAPPER_H diff --git a/phper/src/cmd.rs b/phper/src/cmd.rs index 7ba2dbd7..f8173b33 100644 --- a/phper/src/cmd.rs +++ b/phper/src/cmd.rs @@ -27,7 +27,7 @@ pub fn make() { try_make().expect("make failed"); } -pub fn try_make() -> anyhow::Result<()> { +pub fn try_make() -> crate::Result<()> { let make: Make = Make::parse(); match make.sub { SubCommand::Install(_) => { @@ -41,7 +41,7 @@ pub fn try_make() -> anyhow::Result<()> { Ok(()) } -fn get_lib_path_and_ext_name() -> anyhow::Result<(PathBuf, OsString)> { +fn get_lib_path_and_ext_name() -> crate::Result<(PathBuf, OsString)> { let exe_path = env::current_exe()?; let exe_stem = exe_path .file_stem() diff --git a/phper/src/errors.rs b/phper/src/errors.rs index efdc02a3..3d5534fa 100644 --- a/phper/src/errors.rs +++ b/phper/src/errors.rs @@ -1,12 +1,18 @@ -use std::str::Utf8Error; +use std::{ffi::FromBytesWithNulError, io, str::Utf8Error}; pub type Result = std::result::Result; #[derive(thiserror::Error, Debug)] pub enum Error { + #[error(transparent)] + Io(#[from] io::Error), + #[error(transparent)] Utf8(#[from] Utf8Error), - #[error("unknown value type `{0}`")] - UnKnownValueType(u32), + #[error(transparent)] + FromBytesWithNul(#[from] FromBytesWithNulError), + + #[error(transparent)] + Other(#[from] anyhow::Error), } diff --git a/phper/src/functions.rs b/phper/src/functions.rs index a16a778c..7c655d3d 100644 --- a/phper/src/functions.rs +++ b/phper/src/functions.rs @@ -1,34 +1,34 @@ -use std::{ - cell::Cell, - ffi::CStr, - mem::{size_of, transmute, zeroed}, - os::raw::{c_char, c_int}, - ptr::null, -}; - use crate::{ classes::Method, ini::create_ini_entry_ex, sys::{ - _zend_get_parameters_array_ex, phper_z_strval_p, zend_arg_info, zend_execute_data, - zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, zend_uchar, zif_handler, - zval, + _zend_get_parameters_array_ex, phper_z_strval_p, phper_zval_zval, zend_arg_info, + zend_ce_exception, zend_execute_data, zend_function_entry, zend_ini_entry_def, + zend_internal_arg_info, zend_throw_exception, zend_uchar, zif_handler, zval, }, - values::{ExecuteData, Val}, + throws::Throwable, + values::{ExecuteData, SetVal, Val}, +}; +use std::{ + cell::Cell, + ffi::CStr, + mem::{size_of, transmute, zeroed}, + os::raw::{c_char, c_int}, + ptr::{null, null_mut}, }; - -pub struct Parameters; pub trait Function: Send + Sync { fn call(&self, arguments: &mut [Val], return_value: &mut Val); } -impl Function for F +impl Function for F where - F: Fn() + Send + Sync, + F: Fn(&mut [Val]) -> R + Send + Sync, + R: SetVal, { - fn call(&self, _arguments: &mut [Val], _return_value: &mut Val) { - self() + fn call(&self, arguments: &mut [Val], return_value: &mut Val) { + let r = self(arguments); + r.set_val(return_value); } } diff --git a/phper/src/ini.rs b/phper/src/ini.rs index 80e4b639..dcde0c44 100644 --- a/phper/src/ini.rs +++ b/phper/src/ini.rs @@ -1,28 +1,18 @@ use crate::sys::{ - zend_ini_entry, zend_ini_entry_def, zend_string, OnUpdateString, PHP_INI_ALL, PHP_INI_PERDIR, - PHP_INI_SYSTEM, PHP_INI_USER, OnUpdateBool, OnUpdateLong, OnUpdateReal, + phper_zend_ini_mh, zend_ini_entry, zend_ini_entry_def, zend_string, OnUpdateBool, OnUpdateLong, + OnUpdateReal, OnUpdateString, PHP_INI_ALL, PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, }; use std::{ cell::Cell, + ffi::CStr, mem::{size_of, transmute}, - os::raw::{c_int, c_void}, + os::raw::{c_char, c_int, c_void}, ptr::null_mut, + str, sync::atomic::AtomicPtr, }; -use std::os::raw::c_char; -use std::ffi::CStr; -use std::str; - -type OnModify = Option< -unsafe extern "C" fn( -*mut zend_ini_entry, -*mut zend_string, -*mut c_void, -*mut c_void, -*mut c_void, -c_int, -) -> c_int, ->; + +type OnModify = phper_zend_ini_mh; #[repr(u32)] #[derive(Copy, Clone)] @@ -39,7 +29,7 @@ pub(crate) struct StrPtrBox { impl StrPtrBox { pub(crate) unsafe fn to_string(&self) -> Result { - Ok(CStr::from_ptr(*self.inner).to_str()?.to_string() ) + Ok(CStr::from_ptr(*self.inner).to_str()?.to_string()) } } diff --git a/phper/src/modules.rs b/phper/src/modules.rs index f022b099..3486da25 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -1,28 +1,23 @@ use crate::{ + c_str_ptr, classes::{Class, ClassEntity, ClassEntry}, functions::{invoke, Function, FunctionEntity}, - ini::{IniEntity, Policy}, - sys::{ - zend_class_entry, zend_function_entry, zend_ini_entry_def, zend_internal_arg_info, - zend_module_entry, zend_register_ini_entries, zend_string, zend_unregister_ini_entries, - PHP_MODULE_BUILD_ID, USING_ZTS, ZEND_DEBUG, ZEND_MODULE_API_NO, - OnUpdateBool, OnUpdateLong, OnUpdateReal, OnUpdateString, - }, + ini::{IniEntity, IniValue, Policy, StrPtrBox}, + sys::*, }; use once_cell::sync::Lazy; use std::{ borrow::BorrowMut, cell::{Cell, RefCell, RefMut}, collections::HashMap, + ffi::CStr, mem::{forget, size_of, transmute, zeroed}, ops::DerefMut, os::raw::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void}, ptr::{null, null_mut}, sync::{atomic::AtomicPtr, Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, + thread::LocalKey, }; -use std::ffi::CStr; -use crate::ini::{StrPtrBox, IniValue}; -use std::thread::LocalKey; static GLOBAL_MODULE: Lazy> = Lazy::new(Default::default); @@ -54,36 +49,45 @@ unsafe extern "C" fn module_startup(r#type: c_int, module_number: c_int) -> c_in unsafe extern "C" fn module_shutdown(r#type: c_int, module_number: c_int) -> c_int { let args = ModuleArgs::new(r#type, module_number); args.unregister_ini_entries(); - read_global_module(|module| { - match &module.module_shutdown { - Some(f) => f(args) as c_int, - None => 1, - } + read_global_module(|module| match &module.module_shutdown { + Some(f) => f(args) as c_int, + None => 1, }) } unsafe extern "C" fn request_startup(r#type: c_int, request_number: c_int) -> c_int { - read_global_module(|module| { - match &module.request_init { - Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, - None => 1, - } + read_global_module(|module| match &module.request_init { + Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, + None => 1, }) } unsafe extern "C" fn request_shutdown(r#type: c_int, request_number: c_int) -> c_int { + read_global_module(|module| match &module.request_shutdown { + Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, + None => 1, + }) +} + +unsafe extern "C" fn module_info(zend_module: *mut zend_module_entry) { read_global_module(|module| { - match &module.request_shutdown { - Some(f) => f(ModuleArgs::new(r#type, request_number)) as c_int, - None => 1, + php_info_print_table_start(); + if !module.version.is_empty() { + php_info_print_table_row(2, c_str_ptr!("version"), module.version.as_ptr()); } - }) + if !module.author.is_empty() { + php_info_print_table_row(2, c_str_ptr!("authors"), module.author.as_ptr()); + } + php_info_print_table_end(); + }); + display_ini_entries(zend_module); } #[derive(Default)] pub struct Module { name: String, version: String, + author: String, module_init: Option bool + Send + Sync>>, module_shutdown: Option bool + Send + Sync>>, request_init: Option bool + Send + Sync>>, @@ -112,6 +116,12 @@ impl Module { self.version = version; } + pub fn set_author(&mut self, author: impl ToString) { + let mut author = author.to_string(); + author.push('\0'); + self.author = author; + } + pub fn on_module_init(&mut self, func: impl Fn(ModuleArgs) -> bool + Send + Sync + 'static) { self.module_init = Some(Box::new(func)); } @@ -136,49 +146,66 @@ impl Module { pub fn add_bool_ini(&mut self, name: impl ToString, default_value: bool, policy: Policy) { Self::BOOL_INI_ENTITIES.with(|entities| { - entities.borrow_mut().insert(name.to_string(), IniEntity::new(name, default_value, policy)); + entities.borrow_mut().insert( + name.to_string(), + IniEntity::new(name, default_value, policy), + ); }) } pub fn get_bool_ini(name: &str) -> Option { - Self::BOOL_INI_ENTITIES.with(|entities| { - entities.borrow().get(name).map(|entity| *entity.value()) - }) + Self::BOOL_INI_ENTITIES + .with(|entities| entities.borrow().get(name).map(|entity| *entity.value())) } pub fn add_long_ini(&mut self, name: impl ToString, default_value: i64, policy: Policy) { Self::LONG_INI_ENTITIES.with(|entities| { - entities.borrow_mut().insert(name.to_string(), IniEntity::new(name, default_value, policy)); + entities.borrow_mut().insert( + name.to_string(), + IniEntity::new(name, default_value, policy), + ); }) } pub fn get_long_ini(name: &str) -> Option { - Self::LONG_INI_ENTITIES.with(|entities| { - entities.borrow().get(name).map(|entity| *entity.value()) - }) + Self::LONG_INI_ENTITIES + .with(|entities| entities.borrow().get(name).map(|entity| *entity.value())) } pub fn add_real_ini(&mut self, name: impl ToString, default_value: f64, policy: Policy) { Self::REAL_INI_ENTITIES.with(|entities| { - entities.borrow_mut().insert(name.to_string(), IniEntity::new(name, default_value, policy)); + entities.borrow_mut().insert( + name.to_string(), + IniEntity::new(name, default_value, policy), + ); }) } pub fn get_real_ini(name: &str) -> Option { - Self::REAL_INI_ENTITIES.with(|entities| { - entities.borrow().get(name).map(|entity| *entity.value()) - }) + Self::REAL_INI_ENTITIES + .with(|entities| entities.borrow().get(name).map(|entity| *entity.value())) } - pub fn add_str_ini(&mut self, name: impl ToString, default_value: impl ToString, policy: Policy) { + pub fn add_str_ini( + &mut self, + name: impl ToString, + default_value: impl ToString, + policy: Policy, + ) { Self::STR_INI_ENTITIES.with(|entities| { - entities.borrow_mut().insert(name.to_string(), IniEntity::new(name, default_value, policy)); + entities.borrow_mut().insert( + name.to_string(), + IniEntity::new(name, default_value, policy), + ); }) } pub fn get_str_ini(name: &str) -> Option { Self::STR_INI_ENTITIES.with(|entities| { - entities.borrow().get(name).and_then(|entity| unsafe { entity.value().to_string() }.ok()) + entities + .borrow() + .get(name) + .and_then(|entity| unsafe { entity.value().to_string() }.ok()) }) } @@ -214,7 +241,7 @@ impl Module { module_shutdown_func: Some(module_shutdown), request_startup_func: Some(request_startup), request_shutdown_func: Some(request_shutdown), - info_func: None, + info_func: Some(module_info), version: null(), globals_size: 0usize, #[cfg(phper_zts)] @@ -268,17 +295,24 @@ impl Module { unsafe fn ini_entries(&self) -> *const zend_ini_entry_def { let mut entries = Vec::new(); - Self::BOOL_INI_ENTITIES.with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); - Self::LONG_INI_ENTITIES.with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); - Self::REAL_INI_ENTITIES.with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); - Self::STR_INI_ENTITIES.with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); + Self::BOOL_INI_ENTITIES + .with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); + Self::LONG_INI_ENTITIES + .with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); + Self::REAL_INI_ENTITIES + .with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); + Self::STR_INI_ENTITIES + .with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); entries.push(unsafe { zeroed::() }); Box::into_raw(entries.into_boxed_slice()).cast() } - unsafe fn push_ini_entry(entries: &mut Vec, entities: &mut HashMap>) { + unsafe fn push_ini_entry( + entries: &mut Vec, + entities: &mut HashMap>, + ) { for (_, entry) in &mut *entities.borrow_mut() { entries.push(entry.ini_entry_def()); } @@ -298,7 +332,7 @@ impl ModuleArgs { } } - pub fn register_ini_entries(&self, ini_entries: *const zend_ini_entry_def) { + pub(crate) fn register_ini_entries(&self, ini_entries: *const zend_ini_entry_def) { unsafe { zend_register_ini_entries(ini_entries, self.module_number); } diff --git a/phper/src/throws.rs b/phper/src/throws.rs index 81a72a16..435a23b9 100644 --- a/phper/src/throws.rs +++ b/phper/src/throws.rs @@ -3,3 +3,7 @@ pub trait Throwable {} pub struct MyException; impl Throwable for MyException {} + +pub struct Exception {} + +impl Throwable for Exception {} diff --git a/phper/src/values.rs b/phper/src/values.rs index c745deb7..a8b43794 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -13,7 +13,8 @@ use crate::{ classes::{This, Visibility}, sys::{ self, _zend_get_parameters_array_ex, phper_get_this, phper_init_class_entry_ex, - phper_z_strval_p, phper_zval_get_type, phper_zval_stringl, zend_arg_info, zend_class_entry, + phper_z_strval_p, phper_zend_string_release, phper_zval_get_long, phper_zval_get_string, + phper_zval_get_type, phper_zval_stringl, phper_zval_zval, zend_arg_info, zend_class_entry, zend_declare_property_bool, zend_declare_property_long, zend_declare_property_null, zend_declare_property_stringl, zend_execute_data, zend_long, zend_parse_parameters, zend_read_property, zend_register_internal_class, zend_throw_exception, @@ -23,6 +24,7 @@ use crate::{ }, throws::Throwable, }; +use std::slice::from_raw_parts; #[repr(transparent)] pub struct ExecuteData { @@ -95,4 +97,112 @@ impl Val { unsafe fn type_info(&mut self) -> &mut u32 { &mut self.inner.u1.type_info } + + pub fn as_string(&mut self) -> String { + unsafe { + let s = phper_zval_get_string(&mut self.inner); + let buf = from_raw_parts(&(*s).val as *const i8 as *const u8, (*s).len); + phper_zend_string_release(s); + str::from_utf8(buf).unwrap().to_string() + } + } + + pub fn as_i64(&mut self) -> i64 { + unsafe { phper_zval_get_long(&mut self.inner) } + } +} + +pub trait SetVal { + fn set_val(self, val: &mut Val); +} + +impl SetVal for () { + fn set_val(self, val: &mut Val) { + unsafe { + *val.type_info() = IS_NULL; + } + } +} + +impl SetVal for bool { + fn set_val(self, val: &mut Val) { + unsafe { + *val.type_info() = if self { IS_TRUE } else { IS_FALSE }; + } + } +} + +impl SetVal for i32 { + fn set_val(self, val: &mut Val) { + (self as i64).set_val(val) + } +} + +impl SetVal for u32 { + fn set_val(self, val: &mut Val) { + (self as i64).set_val(val) + } +} + +impl SetVal for i64 { + fn set_val(self, val: &mut Val) { + unsafe { + (*val.as_mut()).value.lval = self; + (*val.as_mut()).u1.type_info = IS_LONG; + } + } +} + +impl SetVal for f64 { + fn set_val(self, val: &mut Val) { + unsafe { + (*val.as_mut()).value.dval = self; + (*val.as_mut()).u1.type_info = IS_DOUBLE; + } + } +} + +impl SetVal for &str { + fn set_val(self, val: &mut Val) { + unsafe { + phper_zval_stringl(val.as_mut(), self.as_ptr().cast(), self.len()); + } + } +} + +impl SetVal for String { + fn set_val(self, val: &mut Val) { + unsafe { + phper_zval_stringl(val.as_mut(), self.as_ptr().cast(), self.len()); + } + } +} + +impl SetVal for Option { + fn set_val(self, val: &mut Val) { + match self { + Some(t) => t.set_val(val), + None => ().set_val(val), + } + } +} + +impl SetVal for Result { + fn set_val(self, val: &mut Val) { + match self { + Ok(t) => t.set_val(val), + Err(_e) => unsafe { + zend_throw_exception(null_mut(), c_str_ptr!(""), 0); + todo!(); + }, + } + } +} + +impl SetVal for Val { + fn set_val(mut self, val: &mut Val) { + unsafe { + phper_zval_zval(val.as_mut(), self.as_mut(), 1, 0); + } + } } From 7443c148f37dfcd4ae55a8784188f3559f960ea0 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sun, 11 Apr 2021 23:50:48 +0800 Subject: [PATCH 09/20] Add argument infos. --- examples/hello/src/lib.rs | 5 ++- phper/src/functions.rs | 83 +++++++++++++++++++++++++++++++++++---- phper/src/modules.rs | 24 ++++++++++- phper/src/values.rs | 5 +++ 4 files changed, 106 insertions(+), 11 deletions(-) diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index a32f44a4..1af8e072 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -1,7 +1,7 @@ use phper::{ c_str_ptr, classes::{Class, MethodEntity, StdClass, This}, - functions::create_zend_arg_info, + functions::{create_zend_arg_info, Argument}, ini::Policy, modules::{read_global_module, write_global_module, Module, ModuleArgs}, php_get_module, @@ -43,7 +43,7 @@ pub extern "C" fn get_module(module: &mut Module) { module.on_request_shutdown(|_| true); // register functions - module.add_function("hello_say_hello", say_hello); + module.add_function("hello_say_hello", say_hello, vec![Argument::by_val("name")]); module.add_function( "hello_get_all_ini", |_: &mut [Val]| -> Result { @@ -55,6 +55,7 @@ pub extern "C" fn get_module(module: &mut Module) { Ok(String::new()) }, + vec![], ); // register classes diff --git a/phper/src/functions.rs b/phper/src/functions.rs index 7c655d3d..3b9a4a6c 100644 --- a/phper/src/functions.rs +++ b/phper/src/functions.rs @@ -1,11 +1,7 @@ use crate::{ classes::Method, ini::create_ini_entry_ex, - sys::{ - _zend_get_parameters_array_ex, phper_z_strval_p, phper_zval_zval, zend_arg_info, - zend_ce_exception, zend_execute_data, zend_function_entry, zend_ini_entry_def, - zend_internal_arg_info, zend_throw_exception, zend_uchar, zif_handler, zval, - }, + sys::*, throws::Throwable, values::{ExecuteData, SetVal, Val}, }; @@ -40,6 +36,55 @@ pub struct FunctionEntry { pub(crate) struct FunctionEntity { pub(crate) name: String, pub(crate) handler: Box, + pub(crate) arguments: Vec, +} + +pub struct Argument { + pub(crate) name: String, + pub(crate) pass_by_ref: bool, + pub(crate) required: bool, +} + +impl Argument { + pub fn by_val(name: impl ToString) -> Self { + let mut name = name.to_string(); + name.push('\0'); + Self { + name, + pass_by_ref: false, + required: true, + } + } + + pub fn by_ref(name: impl ToString) -> Self { + let mut name = name.to_string(); + name.push('\0'); + Self { + name, + pass_by_ref: true, + required: true, + } + } + + pub fn by_val_optional(name: impl ToString) -> Self { + let mut name = name.to_string(); + name.push('\0'); + Self { + name, + pass_by_ref: false, + required: false, + } + } + + pub fn by_ref_optional(name: impl ToString) -> Self { + let mut name = name.to_string(); + name.push('\0'); + Self { + name, + pass_by_ref: true, + required: false, + } + } } pub(crate) unsafe extern "C" fn invoke( @@ -49,14 +94,38 @@ pub(crate) unsafe extern "C" fn invoke( let execute_data = ExecuteData::from_mut(execute_data); let return_value = Val::from_mut(return_value); + // TODO I don't know why this field is zero. let num_args = execute_data.common_num_args(); let arg_info = execute_data.common_arg_info(); + let mut num_args = 0isize; + for i in 0..10isize { + let buf = transmute::<_, [u8; size_of::()]>(*arg_info.offset(i as isize)); + if buf == zeroed::<[u8; size_of::()]>() { + num_args = i; + break; + } + } + if num_args == 0 { + unreachable!(); + } + num_args += 1; + let last_arg_info = arg_info.offset(num_args as isize); let handler = (*last_arg_info).name as *const Box; let handler = handler.as_ref().expect("handler is null"); - // TODO Do num args check + // Check arguments count. + if execute_data.num_args() < execute_data.common_required_num_args() { + let s = format!( + "expects at least {} parameter(s), {} given\0", + execute_data.common_required_num_args(), + execute_data.num_args() + ); + php_error_docref(null(), E_WARNING as i32, s.as_ptr().cast()); + ().set_val(return_value); + return; + } let mut arguments = execute_data.get_parameters_array(); @@ -103,7 +172,7 @@ pub const fn create_zend_arg_info( } } - #[cfg(any(phper_php_version = "7.1", phper_php_version = "7.0",))] + #[cfg(any(phper_php_version = "7.1", phper_php_version = "7.0"))] { zend_internal_arg_info { name, diff --git a/phper/src/modules.rs b/phper/src/modules.rs index 3486da25..79e526c7 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -1,7 +1,7 @@ use crate::{ c_str_ptr, classes::{Class, ClassEntity, ClassEntry}, - functions::{invoke, Function, FunctionEntity}, + functions::{create_zend_arg_info, invoke, Argument, Function, FunctionEntity}, ini::{IniEntity, IniValue, Policy, StrPtrBox}, sys::*, }; @@ -209,13 +209,19 @@ impl Module { }) } - pub fn add_function(&mut self, name: impl ToString, handler: impl Function + 'static) { + pub fn add_function( + &mut self, + name: impl ToString, + handler: impl Function + 'static, + arguments: Vec, + ) { let mut name = name.to_string(); name.push('\0'); self.function_entities.push(FunctionEntity { name, handler: Box::new(handler), + arguments, }); } @@ -270,6 +276,20 @@ impl Module { for f in &self.function_entities { let mut infos = Vec::new(); + + let require_arg_count = f.arguments.iter().filter(|arg| arg.required).count(); + infos.push(create_zend_arg_info( + require_arg_count as *const c_char, + false, + )); + + for arg in &f.arguments { + infos.push(create_zend_arg_info( + arg.name.as_ptr().cast(), + arg.pass_by_ref, + )); + } + infos.push(unsafe { zeroed::() }); let mut last_arg_info = unsafe { zeroed::() }; diff --git a/phper/src/values.rs b/phper/src/values.rs index a8b43794..e18338b6 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -50,6 +50,11 @@ impl ExecuteData { (*self.inner.func).common.num_args } + #[inline] + pub unsafe fn common_required_num_args(&self) -> u32 { + (*self.inner.func).common.required_num_args + } + #[inline] pub unsafe fn common_arg_info(&self) -> *mut zend_arg_info { (*self.inner.func).common.arg_info From 5ea8cca2f2ec8dd73a63f762fe3a84a2125556fc Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Thu, 15 Apr 2021 18:26:28 +0800 Subject: [PATCH 10/20] Add Zend String and Array. --- .github/workflows/ci.yml | 10 +-- examples/hello/Makefile.toml | 30 --------- examples/hello/src/lib.rs | 41 ++++++++---- phper-macros/src/inner.rs | 2 +- phper-sys/php_wrapper.c | 24 ++++--- phper-sys/php_wrapper.h | 6 +- phper/src/arrays.rs | 62 ++++++++++++++++++ phper/src/classes.rs | 119 ++++++++++++++-------------------- phper/src/errors.rs | 41 +++++++++++- phper/src/functions.rs | 120 +++++++++++++++++++++++------------ phper/src/lib.rs | 7 +- phper/src/modules.rs | 63 ++++++------------ phper/src/strings.rs | 34 ++++++++++ phper/src/throws.rs | 9 --- phper/src/values.rs | 49 +++++++++----- 15 files changed, 370 insertions(+), 247 deletions(-) delete mode 100644 examples/hello/Makefile.toml create mode 100644 phper/src/arrays.rs create mode 100644 phper/src/strings.rs delete mode 100644 phper/src/throws.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4712348f..6d5852ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,9 +7,9 @@ on: branches: [ "**" ] env: - CARGO_TERM_COLOR: always - # RUST_BACKTRACE: 1 # RUST_LOG: debug + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 RUSTFLAGS: "-D warnings" jobs: @@ -49,12 +49,6 @@ jobs: override: true components: rustfmt, clippy - # - name: Install Cargo make - # uses: actions-rs/cargo@v1 - # with: - # command: install - # args: cargo-make - - name: Cargo fmt uses: actions-rs/cargo@v1 with: diff --git a/examples/hello/Makefile.toml b/examples/hello/Makefile.toml deleted file mode 100644 index 5f4e9061..00000000 --- a/examples/hello/Makefile.toml +++ /dev/null @@ -1,30 +0,0 @@ -[env] -PHP_CONFIG = "php-config" -TARGET_DIR = "${CARGO_MAKE_WORKING_DIRECTORY}/../../target" - -[env.development] -TARGET_BUILD_DIR = "${TARGET_DIR}/debug" -BUILD_ARGS = "--" -TEST_ARGS = "--" - -[env.production] -TARGET_BUILD_DIR = "${TARGET_DIR}/release" -BUILD_ARGS = "--release" -TEST_ARGS = "--release" - -[tasks.build] -command = "cargo" -args = ["build", "${BUILD_ARGS}"] - -[tasks.test] -command = "cargo" -args = ["test", "${TEST_ARGS}", "--", "--nocapture"] - -[tasks.install] -dependencies = ["build"] -script = [ - """ - cp ${TARGET_BUILD_DIR}/lib${CARGO_MAKE_CRATE_NAME}.so `${PHP_CONFIG} --extension-dir`/${CARGO_MAKE_CRATE_NAME}.so && \ - echo Installing shared extensions: `${PHP_CONFIG} --extension-dir` - """ -] diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 1af8e072..6b0c559d 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -1,6 +1,7 @@ +use std::{fs::OpenOptions, io::Write}; use phper::{ c_str_ptr, - classes::{Class, MethodEntity, StdClass, This}, + classes::{Class, StdClass, This}, functions::{create_zend_arg_info, Argument}, ini::Policy, modules::{read_global_module, write_global_module, Module, ModuleArgs}, @@ -9,10 +10,11 @@ use phper::{ php_info_print_table_end, php_info_print_table_row, php_info_print_table_start, zend_function_entry, OnUpdateBool, PHP_INI_SYSTEM, }, - throws::{Exception, Throwable}, values::{ExecuteData, Val}, + Throwable, }; -use std::{fs::OpenOptions, io::Write}; +use phper::arrays::Array; +use phper::values::SetVal; fn module_init(_args: ModuleArgs) -> bool { true @@ -20,7 +22,11 @@ fn module_init(_args: ModuleArgs) -> bool { fn say_hello(arguments: &mut [Val]) -> String { let name = arguments[0].as_string(); - format!("Hello, {}\n", name) + format!("Hello, {}!\n", name) +} + +fn throw_exception(_: &mut [Val]) -> phper::Result<()> { + Err(phper::Error::other("I am sorry")) } #[php_get_module] @@ -44,16 +50,21 @@ pub extern "C" fn get_module(module: &mut Module) { // register functions module.add_function("hello_say_hello", say_hello, vec![Argument::by_val("name")]); + module.add_function("hello_throw_exception", throw_exception, vec![]); module.add_function( "hello_get_all_ini", - |_: &mut [Val]| -> Result { - let hello_enable = Module::get_bool_ini("hello.enable"); - dbg!(hello_enable); + |_: &mut [Val]| -> Array { + let mut arr = Array::new(); - let hello_description = Module::get_str_ini("hello.description"); - dbg!(hello_description); + let mut hello_enable = Val::null(); + Module::get_bool_ini("hello.enable").set_val(&mut hello_enable); + arr.insert("hello.enable", &mut hello_enable); - Ok(String::new()) + let mut hello_description = Val::null(); + Module::get_str_ini("hello.description").set_val(&mut hello_description); + arr.insert("hello.description", &mut hello_description); + + arr }, vec![], ); @@ -61,8 +72,12 @@ pub extern "C" fn get_module(module: &mut Module) { // register classes let mut std_class = StdClass::new(); std_class.add_property("foo", 100); - std_class.add_method("test1", |_: &mut This| { - println!("hello test1"); - }); + std_class.add_method( + "test1", + |_: &mut This, _: &mut [Val]| { + println!("hello test1"); + }, + vec![], + ); module.add_class("Test1", std_class); } diff --git a/phper-macros/src/inner.rs b/phper-macros/src/inner.rs index f30b1c4b..53c921a6 100644 --- a/phper-macros/src/inner.rs +++ b/phper-macros/src/inner.rs @@ -31,7 +31,7 @@ pub(crate) fn php_get_module(_attr: TokenStream, input: TokenStream) -> TokenStr ::phper::modules::write_global_module(internal); unsafe { - ::phper::modules::read_global_module(|module| { + ::phper::modules::write_global_module(|module| { module.module_entry() }) } diff --git a/phper-sys/php_wrapper.c b/phper-sys/php_wrapper.c index de9829d0..108a4796 100644 --- a/phper-sys/php_wrapper.c +++ b/phper-sys/php_wrapper.c @@ -1,9 +1,5 @@ #include "php_wrapper.h" -zend_string *phper_zend_string_init(const char *str, size_t len, int persistent) { - return zend_string_init(str, len, persistent); -} - zend_string *phper_zend_new_interned_string(zend_string *str) { return zend_new_interned_string(str); } @@ -22,6 +18,10 @@ zend_uchar phper_zval_get_type(const zval* pz) { return zval_get_type(pz); } +void phper_zval_arr(zval *return_value, zend_array *arr) { + ZVAL_ARR(return_value, arr); +} + void phper_zval_stringl(zval *return_value, const char *s, size_t len) { ZVAL_STRINGL(return_value, s, len); } @@ -42,10 +42,18 @@ zend_string *phper_zval_get_string(zval *op) { return zval_get_string(op); } -void phper_zend_string_release(zend_string *s) { - return zend_string_release(s); -} - zend_long phper_zval_get_long(zval *op) { return zval_get_long(op); } + +zend_string *phper_zend_string_init(const char *str, size_t len, int persistent) { + return zend_string_init(str, len, persistent); +} + +zend_string *phper_zend_string_alloc(size_t len, int persistent) { + return zend_string_alloc(len, persistent); +} + +void phper_zend_string_release(zend_string *s) { + return zend_string_release(s); +} \ No newline at end of file diff --git a/phper-sys/php_wrapper.h b/phper-sys/php_wrapper.h index b231f401..27f779a0 100644 --- a/phper-sys/php_wrapper.h +++ b/phper-sys/php_wrapper.h @@ -8,11 +8,11 @@ typedef ZEND_INI_MH(phper_zend_ini_mh); -zend_string *zend_string_init_(const char *str, size_t len, int persistent); zend_string *zend_new_interned_string_(zend_string *str); zend_class_entry phper_init_class_entry_ex(const char *class_name, size_t class_name_len, const zend_function_entry *functions); void phper_zval_string(zval *return_value, const char *s); zend_uchar phper_zval_get_type(const zval* pz); +void phper_zval_arr(zval *return_value, zend_array *arr); void phper_zval_stringl(zval *return_value, const char *s, size_t len); char *phper_z_strval_p(const zval *v); zval *phper_get_this(zend_execute_data *execute_data); @@ -21,4 +21,8 @@ zend_string *phper_zval_get_string(zval *op); void phper_zend_string_release(zend_string *s); zend_long phper_zval_get_long(zval *op); +zend_string *phper_zend_string_init(const char *str, size_t len, int persistent); +zend_string *phper_zend_string_alloc(size_t len, int persistent); +void phper_zend_string_release(zend_string *s); + #endif //PHPER_PHP_WRAPPER_H diff --git a/phper/src/arrays.rs b/phper/src/arrays.rs new file mode 100644 index 00000000..cd1f86a6 --- /dev/null +++ b/phper/src/arrays.rs @@ -0,0 +1,62 @@ +use crate::sys::*; +use std::ptr::null_mut; +use std::mem::zeroed; +use crate::values::Val; +use std::ops::{Deref, DerefMut}; + +pub struct Array { + inner: Box, +} + +impl Array { + pub fn new() -> Self { + let mut inner = Box::new(unsafe { zeroed::() }); + unsafe { + _zend_hash_init(&mut *inner, 0, None, 1); + } + Self { + inner, + } + } + + pub fn insert(&mut self, key: impl AsRef, value: &mut Val) { + let key = key.as_ref(); + unsafe { + zend_hash_str_update(&mut *self.inner, key.as_ptr().cast(), key.len(), value.as_mut()); + } + } + + pub fn get(&mut self, key: impl AsRef) -> &mut Val { + let key = key.as_ref(); + unsafe { + let value = zend_hash_str_find(&mut *self.inner, key.as_ptr().cast(), key.len()); + Val::from_mut(value) + } + } + + pub fn len(&mut self) -> usize { + unsafe { + zend_array_count(&mut *self.inner) as usize + } + } +} + +impl AsRef for Array { + fn as_ref(&self) -> &zend_array { + self.inner.deref() + } +} + +impl AsMut for Array { + fn as_mut(&mut self) -> &mut zend_array { + self.inner.deref_mut() + } +} + +impl Drop for Array { + fn drop(&mut self) { + unsafe { + zend_hash_destroy(&mut *self.inner); + } + } +} \ No newline at end of file diff --git a/phper/src/classes.rs b/phper/src/classes.rs index a3f124a4..a40a9dc6 100644 --- a/phper/src/classes.rs +++ b/phper/src/classes.rs @@ -1,3 +1,9 @@ +use crate::{ + functions::{invoke, Argument, Callable, FunctionEntity, FunctionEntry, Method}, + sys::*, + values::Val, +}; +use once_cell::sync::OnceCell; use std::{ mem::zeroed, os::raw::c_int, @@ -8,73 +14,16 @@ use std::{ }, }; -use once_cell::sync::OnceCell; - -use crate::{ - functions::{method_invoke, FunctionEntry}, - sys::{ - phper_init_class_entry_ex, zend_class_entry, zend_declare_property_long, - zend_function_entry, zend_internal_arg_info, zend_register_internal_class, zval, - ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, - }, - values::Val, -}; - -pub trait Method: Send + Sync { - fn call(&self, this: &mut This, arguments: &mut [Val], return_value: &mut Val); -} - -impl Method for F -where - F: Fn(&mut This) + Send + Sync, -{ - fn call(&self, this: &mut This, _arguments: &mut [Val], _return_value: &mut Val) { - self(this) - } -} - -pub struct MethodEntity { - pub(crate) name: String, - pub(crate) handler: Box, -} - -impl MethodEntity { - pub fn new(name: impl ToString, handler: impl Method + 'static) -> Self { - let mut name = name.to_string(); - name.push('\0'); - - Self { - name, - handler: Box::new(handler), - } - } - - unsafe fn function_entry(&self) -> zend_function_entry { - let mut infos = Vec::new(); - infos.push(zeroed::()); - - let mut last_arg_info = zeroed::(); - last_arg_info.name = &self.handler as *const _ as *mut _; - infos.push(last_arg_info); - - zend_function_entry { - fname: self.name.as_ptr().cast(), - handler: Some(method_invoke), - arg_info: Box::into_raw(infos.into_boxed_slice()).cast(), - num_args: 0, - flags: 0, - } - } -} - pub trait Class: Send + Sync { - fn methods(&self) -> &[MethodEntity]; + fn methods(&self) -> &[FunctionEntity]; fn properties(&self) -> &[PropertyEntity]; + fn parent(&self) -> Option<&str>; } pub struct StdClass { - pub(crate) method_entities: Vec, + pub(crate) method_entities: Vec, pub(crate) property_entities: Vec, + pub(crate) parent: Option, } impl StdClass { @@ -82,27 +31,46 @@ impl StdClass { Self { method_entities: Vec::new(), property_entities: Vec::new(), + parent: None, } } - pub fn add_method(&mut self, name: impl ToString, handler: impl Method + 'static) { - self.method_entities.push(MethodEntity::new(name, handler)); + pub fn add_method( + &mut self, + name: impl ToString, + handler: impl Method + 'static, + arguments: Vec, + ) { + self.method_entities.push(FunctionEntity::new( + name, + Callable::Method(Box::new(handler)), + arguments, + )); } pub fn add_property(&mut self, name: impl ToString, value: i32) { self.property_entities .push(PropertyEntity::new(name, value)); } + + pub fn extends(&mut self, name: impl ToString) { + let mut name = name.to_string(); + self.parent = Some(name); + } } impl Class for StdClass { - fn methods(&self) -> &[MethodEntity] { + fn methods(&self) -> &[FunctionEntity] { &self.method_entities } fn properties(&self) -> &[PropertyEntity] { &self.property_entities } + + fn parent(&self) -> Option<&str> { + self.parent.as_deref() + } } #[repr(transparent)] @@ -110,7 +78,11 @@ pub struct ClassEntry { inner: zend_class_entry, } -impl ClassEntry {} +impl ClassEntry { + pub fn as_mut(&mut self) -> *mut zend_class_entry { + &mut self.inner + } +} pub struct ClassEntity { pub(crate) name: String, @@ -138,10 +110,17 @@ impl ClassEntity { self.name.len(), self.function_entries().load(Ordering::SeqCst).cast(), ); - self.entry.store( - zend_register_internal_class(&mut class_ce).cast(), - Ordering::SeqCst, - ); + + let parent = self.class.parent().map(|s| match s { + "Exception" | "\\Exception" => zend_ce_exception, + _ => todo!(), + }); + + let ptr = match parent { + Some(parent) => zend_register_internal_class_ex(&mut class_ce, parent).cast(), + None => zend_register_internal_class(&mut class_ce).cast(), + }; + self.entry.store(ptr, Ordering::SeqCst); }); } @@ -164,7 +143,7 @@ impl ClassEntity { .class .methods() .iter() - .map(|method| method.function_entry()) + .map(|method| method.entry()) .collect::>(); methods.push(zeroed::()); let entry = Box::into_raw(methods.into_boxed_slice()).cast(); diff --git a/phper/src/errors.rs b/phper/src/errors.rs index 3d5534fa..5163322e 100644 --- a/phper/src/errors.rs +++ b/phper/src/errors.rs @@ -1,4 +1,14 @@ -use std::{ffi::FromBytesWithNulError, io, str::Utf8Error}; +use crate::{ + classes::{ClassEntity, ClassEntry}, + modules::{read_global_module, write_global_module}, + Error::Other, +}; +use anyhow::anyhow; +use once_cell::sync::Lazy; +use std::{ + cell::RefCell, error, ffi::FromBytesWithNulError, io, ptr::null_mut, str::Utf8Error, + sync::atomic::AtomicPtr, +}; pub type Result = std::result::Result; @@ -16,3 +26,32 @@ pub enum Error { #[error(transparent)] Other(#[from] anyhow::Error), } + +impl Error { + pub fn other(message: impl ToString) -> Self { + let message = message.to_string(); + Other(anyhow!(message)) + } +} + +pub trait Throwable: error::Error { + fn class_entity(&self) -> *const ClassEntity; + fn code(&self) -> u64; +} + +pub(crate) const EXCEPTION_CLASS_NAME: &'static str = "\\Phper\\Exception\\ErrorException"; + +impl Throwable for Error { + fn class_entity(&self) -> *const ClassEntity { + read_global_module(|module| { + module + .class_entities + .get(EXCEPTION_CLASS_NAME) + .expect("Must be called after module init") as *const _ + }) + } + + fn code(&self) -> u64 { + 500 + } +} diff --git a/phper/src/functions.rs b/phper/src/functions.rs index 3b9a4a6c..28be2266 100644 --- a/phper/src/functions.rs +++ b/phper/src/functions.rs @@ -1,8 +1,8 @@ use crate::{ - classes::Method, + classes::This, + errors::Throwable, ini::create_ini_entry_ex, sys::*, - throws::Throwable, values::{ExecuteData, SetVal, Val}, }; use std::{ @@ -28,17 +28,81 @@ where } } +pub trait Method: Send + Sync { + fn call(&self, this: &mut This, arguments: &mut [Val], return_value: &mut Val); +} + +impl Method for F +where + F: Fn(&mut This, &mut [Val]) -> R + Send + Sync, + R: SetVal, +{ + fn call(&self, this: &mut This, arguments: &mut [Val], return_value: &mut Val) { + let r = self(this, arguments); + r.set_val(return_value); + } +} + +pub(crate) enum Callable { + Function(Box), + Method(Box), +} + #[repr(transparent)] pub struct FunctionEntry { inner: zend_function_entry, } -pub(crate) struct FunctionEntity { +pub struct FunctionEntity { pub(crate) name: String, - pub(crate) handler: Box, + pub(crate) handler: Callable, pub(crate) arguments: Vec, } +impl FunctionEntity { + pub(crate) fn new(name: impl ToString, handler: Callable, arguments: Vec) -> Self { + let mut name = name.to_string(); + name.push('\0'); + FunctionEntity { + name, + handler, + arguments, + } + } + + // Leak memory + pub(crate) unsafe fn entry(&self) -> zend_function_entry { + let mut infos = Vec::new(); + + let require_arg_count = self.arguments.iter().filter(|arg| arg.required).count(); + infos.push(create_zend_arg_info( + require_arg_count as *const c_char, + false, + )); + + for arg in &self.arguments { + infos.push(create_zend_arg_info( + arg.name.as_ptr().cast(), + arg.pass_by_ref, + )); + } + + infos.push(unsafe { zeroed::() }); + + let mut last_arg_info = unsafe { zeroed::() }; + last_arg_info.name = ((&self.handler) as *const _ as *mut i8).cast(); + infos.push(last_arg_info); + + zend_function_entry { + fname: self.name.as_ptr().cast(), + handler: Some(invoke), + arg_info: Box::into_raw(infos.into_boxed_slice()).cast(), + num_args: self.arguments.len() as u32, + flags: 0, + } + } +} + pub struct Argument { pub(crate) name: String, pub(crate) pass_by_ref: bool, @@ -94,25 +158,11 @@ pub(crate) unsafe extern "C" fn invoke( let execute_data = ExecuteData::from_mut(execute_data); let return_value = Val::from_mut(return_value); - // TODO I don't know why this field is zero. let num_args = execute_data.common_num_args(); let arg_info = execute_data.common_arg_info(); - let mut num_args = 0isize; - for i in 0..10isize { - let buf = transmute::<_, [u8; size_of::()]>(*arg_info.offset(i as isize)); - if buf == zeroed::<[u8; size_of::()]>() { - num_args = i; - break; - } - } - if num_args == 0 { - unreachable!(); - } - num_args += 1; - - let last_arg_info = arg_info.offset(num_args as isize); - let handler = (*last_arg_info).name as *const Box; + let last_arg_info = arg_info.offset((num_args + 1) as isize); + let handler = (*last_arg_info).name as *const Callable; let handler = handler.as_ref().expect("handler is null"); // Check arguments count. @@ -129,28 +179,14 @@ pub(crate) unsafe extern "C" fn invoke( let mut arguments = execute_data.get_parameters_array(); - handler.call(&mut arguments, return_value); -} - -pub(crate) unsafe extern "C" fn method_invoke( - execute_data: *mut zend_execute_data, - return_value: *mut zval, -) { - let execute_data = ExecuteData::from_mut(execute_data); - let return_value = Val::from_mut(return_value); - - let num_args = execute_data.common_num_args(); - let arg_info = execute_data.common_arg_info(); - - let last_arg_info = arg_info.offset(num_args as isize); - let handler = (*last_arg_info).name as *const Box; - let handler = handler.as_ref().expect("handler is null"); - - // TODO Do num args check - - let mut arguments = execute_data.get_parameters_array(); - - handler.call(execute_data.get_this(), &mut arguments, return_value); + match handler { + Callable::Function(f) => { + f.call(&mut arguments, return_value); + } + Callable::Method(m) => { + m.call(execute_data.get_this(), &mut arguments, return_value); + } + } } pub const fn create_zend_arg_info( diff --git a/phper/src/lib.rs b/phper/src/lib.rs index 716b855a..861096f1 100644 --- a/phper/src/lib.rs +++ b/phper/src/lib.rs @@ -26,15 +26,16 @@ Now the library don't support `ZTS`, the template is using `thread_local!` inste Version `0.1.x` will be a preview version. */ +mod errors; +mod utils; +pub mod arrays; pub mod classes; pub mod cmd; -mod errors; pub mod functions; pub mod ini; pub mod logs; pub mod modules; -pub mod throws; -mod utils; +pub mod strings; pub mod values; pub use crate::errors::*; diff --git a/phper/src/modules.rs b/phper/src/modules.rs index 79e526c7..d1834a56 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -1,9 +1,10 @@ use crate::{ c_str_ptr, - classes::{Class, ClassEntity, ClassEntry}, - functions::{create_zend_arg_info, invoke, Argument, Function, FunctionEntity}, + classes::{Class, ClassEntity, ClassEntry, StdClass}, + functions::{create_zend_arg_info, invoke, Argument, Callable, Function, FunctionEntity}, ini::{IniEntity, IniValue, Policy, StrPtrBox}, sys::*, + EXCEPTION_CLASS_NAME, }; use once_cell::sync::Lazy; use std::{ @@ -35,7 +36,7 @@ unsafe extern "C" fn module_startup(r#type: c_int, module_number: c_int) -> c_in let args = ModuleArgs::new(r#type, module_number); read_global_module(|module| { args.register_ini_entries(module.ini_entries()); - for class_entity in &module.class_entities { + for (_, class_entity) in &module.class_entities { class_entity.init(); class_entity.declare_properties(); } @@ -93,7 +94,7 @@ pub struct Module { request_init: Option bool + Send + Sync>>, request_shutdown: Option bool + Send + Sync>>, function_entities: Vec, - class_entities: Vec, + pub(crate) class_entities: HashMap, } impl Module { @@ -215,25 +216,24 @@ impl Module { handler: impl Function + 'static, arguments: Vec, ) { - let mut name = name.to_string(); - name.push('\0'); - - self.function_entities.push(FunctionEntity { + self.function_entities.push(FunctionEntity::new( name, - handler: Box::new(handler), + Callable::Function(Box::new(handler)), arguments, - }); + )); } pub fn add_class(&mut self, name: impl ToString, class: impl Class + 'static) { self.class_entities - .push(unsafe { ClassEntity::new(name, class) }) + .insert(name.to_string(), unsafe { ClassEntity::new(name, class) }); } - pub unsafe fn module_entry(&self) -> *const zend_module_entry { + pub unsafe fn module_entry(&mut self) -> *const zend_module_entry { assert!(!self.name.is_empty(), "module name must be set"); assert!(!self.version.is_empty(), "module version must be set"); + self.add_error_exception_class(); + let entry: Box = Box::new(zend_module_entry { size: size_of::() as c_ushort, zend_api: ZEND_MODULE_API_NO as c_uint, @@ -273,40 +273,9 @@ impl Module { } let mut entries = Vec::new(); - for f in &self.function_entities { - let mut infos = Vec::new(); - - let require_arg_count = f.arguments.iter().filter(|arg| arg.required).count(); - infos.push(create_zend_arg_info( - require_arg_count as *const c_char, - false, - )); - - for arg in &f.arguments { - infos.push(create_zend_arg_info( - arg.name.as_ptr().cast(), - arg.pass_by_ref, - )); - } - - infos.push(unsafe { zeroed::() }); - - let mut last_arg_info = unsafe { zeroed::() }; - last_arg_info.name = ((&f.handler) as *const _ as *mut i8).cast(); - infos.push(last_arg_info); - - let entry = zend_function_entry { - fname: f.name.as_ptr().cast(), - handler: Some(invoke), - arg_info: Box::into_raw(infos.into_boxed_slice()).cast(), - num_args: 0, - flags: 0, - }; - - entries.push(entry); + entries.push(unsafe { f.entry() }); } - entries.push(unsafe { zeroed::() }); Box::into_raw(entries.into_boxed_slice()).cast() @@ -337,6 +306,12 @@ impl Module { entries.push(entry.ini_entry_def()); } } + + fn add_error_exception_class(&mut self) { + let mut class = StdClass::new(); + class.extends("\\Exception"); + self.add_class(EXCEPTION_CLASS_NAME, class); + } } pub struct ModuleArgs { diff --git a/phper/src/strings.rs b/phper/src/strings.rs new file mode 100644 index 00000000..11cda36f --- /dev/null +++ b/phper/src/strings.rs @@ -0,0 +1,34 @@ +use crate::sys::*; + +pub struct ZString { + inner: *mut zend_string, +} + +impl ZString { + pub fn new() -> Self { + unsafe { + Self { + inner: phper_zend_string_alloc(0, 1), + } + } + } +} + +impl> From for ZString { + fn from(t: T) -> Self { + let s = t.as_ref(); + unsafe { + Self { + inner: phper_zend_string_init(s.as_ptr().cast(), s.len(), 1), + } + } + } +} + +impl Drop for ZString { + fn drop(&mut self) { + unsafe { + phper_zend_string_release(self.inner); + } + } +} \ No newline at end of file diff --git a/phper/src/throws.rs b/phper/src/throws.rs deleted file mode 100644 index 435a23b9..00000000 --- a/phper/src/throws.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub trait Throwable {} - -pub struct MyException; - -impl Throwable for MyException {} - -pub struct Exception {} - -impl Throwable for Exception {} diff --git a/phper/src/values.rs b/phper/src/values.rs index e18338b6..586ce1ce 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -7,24 +7,14 @@ use std::{ ptr::null_mut, slice, str, }; - use crate::{ c_str_ptr, classes::{This, Visibility}, - sys::{ - self, _zend_get_parameters_array_ex, phper_get_this, phper_init_class_entry_ex, - phper_z_strval_p, phper_zend_string_release, phper_zval_get_long, phper_zval_get_string, - phper_zval_get_type, phper_zval_stringl, phper_zval_zval, zend_arg_info, zend_class_entry, - zend_declare_property_bool, zend_declare_property_long, zend_declare_property_null, - zend_declare_property_stringl, zend_execute_data, zend_long, zend_parse_parameters, - zend_read_property, zend_register_internal_class, zend_throw_exception, - zend_update_property_bool, zend_update_property_long, zend_update_property_null, - zend_update_property_stringl, zval, IS_DOUBLE, IS_FALSE, IS_LONG, IS_NULL, IS_TRUE, - ZEND_RESULT_CODE_SUCCESS, - }, - throws::Throwable, + errors::Throwable, + sys::*, }; -use std::slice::from_raw_parts; +use std::{slice::from_raw_parts, sync::atomic::Ordering}; +use crate::arrays::Array; #[repr(transparent)] pub struct ExecuteData { @@ -89,6 +79,14 @@ impl Val { Self { inner } } + pub fn null() -> Self { + let mut val = Self { + inner: unsafe { zeroed::() }, + }; + ().set_val(&mut val); + val + } + #[inline] pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self { assert!(!ptr.is_null(), "ptr should not be null"); @@ -183,6 +181,14 @@ impl SetVal for String { } } +impl SetVal for Array { + fn set_val(mut self, val: &mut Val) { + unsafe { + phper_zval_arr(val.as_mut(), self.as_mut()); + } + } +} + impl SetVal for Option { fn set_val(self, val: &mut Val) { match self { @@ -196,9 +202,18 @@ impl SetVal for Result { fn set_val(self, val: &mut Val) { match self { Ok(t) => t.set_val(val), - Err(_e) => unsafe { - zend_throw_exception(null_mut(), c_str_ptr!(""), 0); - todo!(); + Err(e) => unsafe { + let class = e + .class_entity() + .as_ref() + .expect("class entry is null pointer"); + let mut message = e.to_string(); + message.push('\0'); + zend_throw_exception( + class.entry.load(Ordering::SeqCst).cast(), + message.as_ptr().cast(), + e.code() as i64, + ); }, } } From 6918134bd922b0557fb5cadf69b5c0ad35486b0b Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sat, 17 Apr 2021 00:20:04 +0800 Subject: [PATCH 11/20] Pass the hello examples. --- examples/hello/src/lib.rs | 30 ++++++---- phper-sys/php_wrapper.c | 8 +++ phper-sys/php_wrapper.h | 2 + phper/src/arrays.rs | 28 ++++----- phper/src/classes.rs | 115 ++++++++++++++++++++++--------------- phper/src/functions.rs | 12 ++-- phper/src/lib.rs | 4 +- phper/src/modules.rs | 4 +- phper/src/strings.rs | 2 +- phper/src/values.rs | 116 ++++++++++++++++++++++++-------------- 10 files changed, 202 insertions(+), 119 deletions(-) diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 6b0c559d..870679e6 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -1,5 +1,5 @@ -use std::{fs::OpenOptions, io::Write}; use phper::{ + arrays::Array, c_str_ptr, classes::{Class, StdClass, This}, functions::{create_zend_arg_info, Argument}, @@ -10,11 +10,10 @@ use phper::{ php_info_print_table_end, php_info_print_table_row, php_info_print_table_start, zend_function_entry, OnUpdateBool, PHP_INI_SYSTEM, }, - values::{ExecuteData, Val}, + values::{ExecuteData, SetVal, Val}, Throwable, }; -use phper::arrays::Array; -use phper::values::SetVal; +use std::{fs::OpenOptions, io::Write}; fn module_init(_args: ModuleArgs) -> bool { true @@ -70,14 +69,23 @@ pub extern "C" fn get_module(module: &mut Module) { ); // register classes - let mut std_class = StdClass::new(); - std_class.add_property("foo", 100); - std_class.add_method( - "test1", - |_: &mut This, _: &mut [Val]| { - println!("hello test1"); + let mut foo_class = StdClass::new(); + foo_class.add_property("foo", 100); + foo_class.add_method( + "getFoo", + |this: &mut This, _: &mut [Val]| { + let prop = this.get_property("foo"); + Val::from_val(prop) }, vec![], ); - module.add_class("Test1", std_class); + foo_class.add_method( + "setFoo", + |this: &mut This, arguments: &mut [Val]| { + let prop = this.get_property("foo"); + prop.set(&arguments[0]); + }, + vec![Argument::by_val("foo")], + ); + module.add_class("FooClass", foo_class); } diff --git a/phper-sys/php_wrapper.c b/phper-sys/php_wrapper.c index 108a4796..41335168 100644 --- a/phper-sys/php_wrapper.c +++ b/phper-sys/php_wrapper.c @@ -38,6 +38,14 @@ void phper_zval_zval(zval *return_value, zval *zv, int copy, int dtor) { ZVAL_ZVAL(return_value, zv, copy, dtor); } +void phper_zval_dup(zval *return_value, zval *zv) { + ZVAL_DUP(return_value, zv); +} + +void phper_zval_copy(zval *return_value, zval *zv) { + ZVAL_COPY(return_value, zv); +} + zend_string *phper_zval_get_string(zval *op) { return zval_get_string(op); } diff --git a/phper-sys/php_wrapper.h b/phper-sys/php_wrapper.h index 27f779a0..03ff6c95 100644 --- a/phper-sys/php_wrapper.h +++ b/phper-sys/php_wrapper.h @@ -17,6 +17,8 @@ void phper_zval_stringl(zval *return_value, const char *s, size_t len); char *phper_z_strval_p(const zval *v); zval *phper_get_this(zend_execute_data *execute_data); void phper_zval_zval(zval *return_value, zval *zv, int copy, int dtor); +void phper_zval_dup(zval *return_value, zval *zv); +void phper_zval_copy(zval *return_value, zval *zv); zend_string *phper_zval_get_string(zval *op); void phper_zend_string_release(zend_string *s); zend_long phper_zval_get_long(zval *op); diff --git a/phper/src/arrays.rs b/phper/src/arrays.rs index cd1f86a6..acf802e5 100644 --- a/phper/src/arrays.rs +++ b/phper/src/arrays.rs @@ -1,8 +1,9 @@ -use crate::sys::*; -use std::ptr::null_mut; -use std::mem::zeroed; -use crate::values::Val; -use std::ops::{Deref, DerefMut}; +use crate::{sys::*, values::Val}; +use std::{ + mem::zeroed, + ops::{Deref, DerefMut}, + ptr::null_mut, +}; pub struct Array { inner: Box, @@ -14,15 +15,18 @@ impl Array { unsafe { _zend_hash_init(&mut *inner, 0, None, 1); } - Self { - inner, - } + Self { inner } } pub fn insert(&mut self, key: impl AsRef, value: &mut Val) { let key = key.as_ref(); unsafe { - zend_hash_str_update(&mut *self.inner, key.as_ptr().cast(), key.len(), value.as_mut()); + zend_hash_str_update( + &mut *self.inner, + key.as_ptr().cast(), + key.len(), + value.as_mut(), + ); } } @@ -35,9 +39,7 @@ impl Array { } pub fn len(&mut self) -> usize { - unsafe { - zend_array_count(&mut *self.inner) as usize - } + unsafe { zend_array_count(&mut *self.inner) as usize } } } @@ -59,4 +61,4 @@ impl Drop for Array { zend_hash_destroy(&mut *self.inner); } } -} \ No newline at end of file +} diff --git a/phper/src/classes.rs b/phper/src/classes.rs index a40a9dc6..269e8ac6 100644 --- a/phper/src/classes.rs +++ b/phper/src/classes.rs @@ -1,7 +1,7 @@ use crate::{ functions::{invoke, Argument, Callable, FunctionEntity, FunctionEntry, Method}, sys::*, - values::Val, + values::{SetVal, Val}, }; use once_cell::sync::OnceCell; use std::{ @@ -15,8 +15,8 @@ use std::{ }; pub trait Class: Send + Sync { - fn methods(&self) -> &[FunctionEntity]; - fn properties(&self) -> &[PropertyEntity]; + fn methods(&mut self) -> &mut [FunctionEntity]; + fn properties(&mut self) -> &mut [PropertyEntity]; fn parent(&self) -> Option<&str>; } @@ -43,12 +43,16 @@ impl StdClass { ) { self.method_entities.push(FunctionEntity::new( name, - Callable::Method(Box::new(handler)), + Callable::Method(Box::new(handler), AtomicPtr::new(null_mut())), arguments, )); } - pub fn add_property(&mut self, name: impl ToString, value: i32) { + pub fn add_property( + &mut self, + name: impl ToString, + value: impl SetVal + Send + Sync + 'static, + ) { self.property_entities .push(PropertyEntity::new(name, value)); } @@ -60,12 +64,12 @@ impl StdClass { } impl Class for StdClass { - fn methods(&self) -> &[FunctionEntity] { - &self.method_entities + fn methods(&mut self) -> &mut [FunctionEntity] { + &mut self.method_entities } - fn properties(&self) -> &[PropertyEntity] { - &self.property_entities + fn properties(&mut self) -> &mut [PropertyEntity] { + &mut self.property_entities } fn parent(&self) -> Option<&str> { @@ -88,7 +92,6 @@ pub struct ClassEntity { pub(crate) name: String, pub(crate) entry: AtomicPtr, pub(crate) class: Box, - pub(crate) init_once: Once, pub(crate) function_entries: OnceCell>, } @@ -98,50 +101,59 @@ impl ClassEntity { name: name.to_string(), entry: AtomicPtr::new(null_mut()), class: Box::new(class), - init_once: Once::new(), function_entries: Default::default(), } } - pub(crate) unsafe fn init(&self) { - self.init_once.call_once(|| { - let mut class_ce = phper_init_class_entry_ex( - self.name.as_ptr().cast(), - self.name.len(), - self.function_entries().load(Ordering::SeqCst).cast(), - ); + pub(crate) unsafe fn init(&mut self) { + let mut class_ce = phper_init_class_entry_ex( + self.name.as_ptr().cast(), + self.name.len(), + self.function_entries().load(Ordering::SeqCst).cast(), + ); - let parent = self.class.parent().map(|s| match s { - "Exception" | "\\Exception" => zend_ce_exception, - _ => todo!(), - }); - - let ptr = match parent { - Some(parent) => zend_register_internal_class_ex(&mut class_ce, parent).cast(), - None => zend_register_internal_class(&mut class_ce).cast(), - }; - self.entry.store(ptr, Ordering::SeqCst); + let parent = self.class.parent().map(|s| match s { + "Exception" | "\\Exception" => zend_ce_exception, + _ => todo!(), }); + + let ptr = match parent { + Some(parent) => zend_register_internal_class_ex(&mut class_ce, parent).cast(), + None => zend_register_internal_class(&mut class_ce).cast(), + }; + self.entry.store(ptr, Ordering::SeqCst); + + let methods = self.class.methods(); + for method in methods { + match &method.handler { + Callable::Method(_, class) => { + class.store(ptr, Ordering::SeqCst); + } + _ => unreachable!(), + } + } } - pub(crate) unsafe fn declare_properties(&self) { + pub(crate) unsafe fn declare_properties(&mut self) { let properties = self.class.properties(); for property in properties { - zend_declare_property_long( + let mut val = Val::null(); + val.set(&property.value); + zend_declare_property( self.entry.load(Ordering::SeqCst).cast(), property.name.as_ptr().cast(), property.name.len(), - property.value.into(), + val.as_mut(), Visibility::Public as c_int, ); } } - unsafe fn function_entries(&self) -> &AtomicPtr { + unsafe fn function_entries(&mut self) -> &AtomicPtr { + let methods = &*self.class.methods(); + self.function_entries.get_or_init(|| { - let mut methods = self - .class - .methods() + let mut methods = methods .iter() .map(|method| method.entry()) .collect::>(); @@ -152,29 +164,44 @@ impl ClassEntity { } } -#[repr(transparent)] pub struct This { - inner: zval, + val: *mut Val, + class: *mut ClassEntry, } impl This { - #[inline] - pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self { - assert!(!ptr.is_null(), "ptr should not be null"); - &mut *(ptr as *mut Self) + pub(crate) fn new<'a>(val: *mut Val, class: *mut ClassEntry) -> This { + assert!(!val.is_null()); + assert!(!class.is_null()); + Self { val, class } + } + + pub fn get_property(&self, name: impl AsRef) -> &mut Val { + let name = name.as_ref(); + unsafe { + let prop = zend_read_property( + self.class as *mut _, + self.val as *mut _, + name.as_ptr().cast(), + name.len(), + 0, + null_mut(), + ); + Val::from_mut(prop) + } } } pub struct PropertyEntity { name: String, - value: i32, + value: Box, } impl PropertyEntity { - pub fn new(name: impl ToString, value: i32) -> Self { + pub fn new(name: impl ToString, value: impl SetVal + Send + Sync + 'static) -> Self { Self { name: name.to_string(), - value, + value: Box::new(value), } } } diff --git a/phper/src/functions.rs b/phper/src/functions.rs index 28be2266..1fcfaed2 100644 --- a/phper/src/functions.rs +++ b/phper/src/functions.rs @@ -1,5 +1,5 @@ use crate::{ - classes::This, + classes::{ClassEntry, This}, errors::Throwable, ini::create_ini_entry_ex, sys::*, @@ -11,6 +11,7 @@ use std::{ mem::{size_of, transmute, zeroed}, os::raw::{c_char, c_int}, ptr::{null, null_mut}, + sync::atomic::{AtomicPtr, Ordering}, }; pub trait Function: Send + Sync { @@ -45,7 +46,7 @@ where pub(crate) enum Callable { Function(Box), - Method(Box), + Method(Box, AtomicPtr), } #[repr(transparent)] @@ -173,7 +174,7 @@ pub(crate) unsafe extern "C" fn invoke( execute_data.num_args() ); php_error_docref(null(), E_WARNING as i32, s.as_ptr().cast()); - ().set_val(return_value); + return_value.set(()); return; } @@ -183,8 +184,9 @@ pub(crate) unsafe extern "C" fn invoke( Callable::Function(f) => { f.call(&mut arguments, return_value); } - Callable::Method(m) => { - m.call(execute_data.get_this(), &mut arguments, return_value); + Callable::Method(m, class) => { + let mut this = This::new(execute_data.get_this(), class.load(Ordering::SeqCst)); + m.call(&mut this, &mut arguments, return_value); } } } diff --git a/phper/src/lib.rs b/phper/src/lib.rs index 861096f1..25252c1f 100644 --- a/phper/src/lib.rs +++ b/phper/src/lib.rs @@ -26,16 +26,16 @@ Now the library don't support `ZTS`, the template is using `thread_local!` inste Version `0.1.x` will be a preview version. */ -mod errors; -mod utils; pub mod arrays; pub mod classes; pub mod cmd; +mod errors; pub mod functions; pub mod ini; pub mod logs; pub mod modules; pub mod strings; +mod utils; pub mod values; pub use crate::errors::*; diff --git a/phper/src/modules.rs b/phper/src/modules.rs index d1834a56..5c0f8501 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -34,9 +34,9 @@ pub fn write_global_module(f: impl FnOnce(&mut Module) -> R) -> R { unsafe extern "C" fn module_startup(r#type: c_int, module_number: c_int) -> c_int { let args = ModuleArgs::new(r#type, module_number); - read_global_module(|module| { + write_global_module(|module| { args.register_ini_entries(module.ini_entries()); - for (_, class_entity) in &module.class_entities { + for (_, class_entity) in &mut module.class_entities { class_entity.init(); class_entity.declare_properties(); } diff --git a/phper/src/strings.rs b/phper/src/strings.rs index 11cda36f..bce9648f 100644 --- a/phper/src/strings.rs +++ b/phper/src/strings.rs @@ -31,4 +31,4 @@ impl Drop for ZString { phper_zend_string_release(self.inner); } } -} \ No newline at end of file +} diff --git a/phper/src/values.rs b/phper/src/values.rs index 586ce1ce..bffb3ff9 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -1,20 +1,23 @@ +use crate::{ + arrays::Array, + c_str_ptr, + classes::{This, Visibility}, + errors::Throwable, + sys::*, +}; use std::{ borrow::Cow, cell::Cell, ffi::{c_void, CStr}, - mem::zeroed, + io::empty, + mem::{size_of, zeroed}, os::raw::{c_char, c_int}, ptr::null_mut, - slice, str, + slice, + slice::from_raw_parts, + str, + sync::atomic::Ordering, }; -use crate::{ - c_str_ptr, - classes::{This, Visibility}, - errors::Throwable, - sys::*, -}; -use std::{slice::from_raw_parts, sync::atomic::Ordering}; -use crate::arrays::Array; #[repr(transparent)] pub struct ExecuteData { @@ -56,8 +59,8 @@ impl ExecuteData { } #[inline] - pub unsafe fn get_this(&mut self) -> &mut This { - This::from_mut(phper_get_this(&mut self.inner)) + pub unsafe fn get_this(&mut self) -> *mut Val { + phper_get_this(&mut self.inner).cast() } pub unsafe fn get_parameters_array(&mut self) -> Vec { @@ -79,24 +82,38 @@ impl Val { Self { inner } } - pub fn null() -> Self { - let mut val = Self { + pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self { + assert!(!ptr.is_null(), "ptr should not be null"); + &mut *(ptr as *mut Self) + } + + #[inline] + fn empty() -> Self { + Self { inner: unsafe { zeroed::() }, - }; - ().set_val(&mut val); + } + } + + pub fn null() -> Self { + let mut val = Self::empty(); + val.set(&()); val } - #[inline] - pub unsafe fn from_mut<'a>(ptr: *mut zval) -> &'a mut Self { - assert!(!ptr.is_null(), "ptr should not be null"); - &mut *(ptr as *mut Self) + pub fn from_val(other: &Val) -> Self { + let mut val = Self::empty(); + val.set(other); + val } pub fn as_mut(&mut self) -> *mut zval { &mut self.inner } + pub fn set(&mut self, v: impl SetVal) { + v.set_val(self); + } + unsafe fn type_info(&mut self) -> &mut u32 { &mut self.inner.u1.type_info } @@ -116,11 +133,11 @@ impl Val { } pub trait SetVal { - fn set_val(self, val: &mut Val); + fn set_val(&self, val: &mut Val); } impl SetVal for () { - fn set_val(self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { unsafe { *val.type_info() = IS_NULL; } @@ -128,45 +145,45 @@ impl SetVal for () { } impl SetVal for bool { - fn set_val(self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { unsafe { - *val.type_info() = if self { IS_TRUE } else { IS_FALSE }; + *val.type_info() = if *self { IS_TRUE } else { IS_FALSE }; } } } impl SetVal for i32 { - fn set_val(self, val: &mut Val) { - (self as i64).set_val(val) + fn set_val(&self, val: &mut Val) { + (*self as i64).set_val(val) } } impl SetVal for u32 { - fn set_val(self, val: &mut Val) { - (self as i64).set_val(val) + fn set_val(&self, val: &mut Val) { + (*self as i64).set_val(val) } } impl SetVal for i64 { - fn set_val(self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { unsafe { - (*val.as_mut()).value.lval = self; + (*val.as_mut()).value.lval = *self; (*val.as_mut()).u1.type_info = IS_LONG; } } } impl SetVal for f64 { - fn set_val(self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { unsafe { - (*val.as_mut()).value.dval = self; + (*val.as_mut()).value.dval = *self; (*val.as_mut()).u1.type_info = IS_DOUBLE; } } } -impl SetVal for &str { - fn set_val(self, val: &mut Val) { +impl SetVal for str { + fn set_val(&self, val: &mut Val) { unsafe { phper_zval_stringl(val.as_mut(), self.as_ptr().cast(), self.len()); } @@ -174,7 +191,7 @@ impl SetVal for &str { } impl SetVal for String { - fn set_val(self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { unsafe { phper_zval_stringl(val.as_mut(), self.as_ptr().cast(), self.len()); } @@ -182,15 +199,15 @@ impl SetVal for String { } impl SetVal for Array { - fn set_val(mut self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { unsafe { - phper_zval_arr(val.as_mut(), self.as_mut()); + phper_zval_arr(val.as_mut(), &self as *const _ as *mut _); } } } impl SetVal for Option { - fn set_val(self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { match self { Some(t) => t.set_val(val), None => ().set_val(val), @@ -199,7 +216,7 @@ impl SetVal for Option { } impl SetVal for Result { - fn set_val(self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { match self { Ok(t) => t.set_val(val), Err(e) => unsafe { @@ -220,9 +237,26 @@ impl SetVal for Result { } impl SetVal for Val { - fn set_val(mut self, val: &mut Val) { + fn set_val(&self, val: &mut Val) { unsafe { - phper_zval_zval(val.as_mut(), self.as_mut(), 1, 0); + phper_zval_copy(val.as_mut(), &self.inner as *const _ as *mut _); } } } + +impl SetVal for Box { + fn set_val(&self, val: &mut Val) { + T::set_val(&self, val) + } +} + +impl SetVal for &T { + fn set_val(&self, val: &mut Val) { + T::set_val(self, val) + } +} +impl SetVal for &mut T { + fn set_val(&self, val: &mut Val) { + T::set_val(self, val) + } +} From a74a744c1996994051b9d127196986c407241772 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sat, 17 Apr 2021 19:43:10 +0800 Subject: [PATCH 12/20] Modify Val from api. --- examples/hello/src/lib.rs | 10 ++++------ examples/hello/tests/integration.rs | 24 +++++++++++++----------- phper/src/values.rs | 17 +++++++++++++++-- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 870679e6..45b89d17 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -19,7 +19,7 @@ fn module_init(_args: ModuleArgs) -> bool { true } -fn say_hello(arguments: &mut [Val]) -> String { +fn say_hello(arguments: &mut [Val]) -> impl SetVal { let name = arguments[0].as_string(); format!("Hello, {}!\n", name) } @@ -52,15 +52,13 @@ pub extern "C" fn get_module(module: &mut Module) { module.add_function("hello_throw_exception", throw_exception, vec![]); module.add_function( "hello_get_all_ini", - |_: &mut [Val]| -> Array { + |_: &mut [Val]| { let mut arr = Array::new(); - let mut hello_enable = Val::null(); - Module::get_bool_ini("hello.enable").set_val(&mut hello_enable); + let mut hello_enable = Val::new(Module::get_bool_ini("hello.enable")); arr.insert("hello.enable", &mut hello_enable); - let mut hello_description = Val::null(); - Module::get_str_ini("hello.description").set_val(&mut hello_description); + let mut hello_description = Val::new(Module::get_str_ini("hello.description")); arr.insert("hello.description", &mut hello_description); arr diff --git a/examples/hello/tests/integration.rs b/examples/hello/tests/integration.rs index 573b6660..d0193e5f 100644 --- a/examples/hello/tests/integration.rs +++ b/examples/hello/tests/integration.rs @@ -3,15 +3,17 @@ use std::{env, path::Path}; #[test] fn test_php() { - test_php_scripts( - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("target"), - env!("CARGO_PKG_NAME"), - &[Path::new(env!("CARGO_MANIFEST_DIR")) - .join("tests") - .join("php") - .join("test.php")], - ); + dbg!(env!("CARGO_BIN_EXE_hello")); + dbg!(env::current_exe()); + // test_php_scripts( + // Path::new(env!("CARGO_MANIFEST_DIR")) + // .join("..") + // .join("..") + // .join("target"), + // env!("CARGO_PKG_NAME"), + // &[Path::new(env!("CARGO_MANIFEST_DIR")) + // .join("tests") + // .join("php") + // .join("test.php")], + // ); } diff --git a/phper/src/values.rs b/phper/src/values.rs index bffb3ff9..3459a229 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -67,7 +67,7 @@ impl ExecuteData { let num_args = self.num_args(); let mut arguments = vec![zeroed::(); num_args as usize]; _zend_get_parameters_array_ex(num_args as c_int, arguments.as_mut_ptr()); - arguments.into_iter().map(Val::new).collect() + arguments.into_iter().map(Val::from_inner).collect() } } @@ -77,8 +77,14 @@ pub struct Val { } impl Val { + pub fn new(t: T) -> Self { + let mut val = Self::empty(); + val.set(t); + val + } + #[inline] - pub const fn new(inner: zval) -> Self { + pub const fn from_inner(inner: zval) -> Self { Self { inner } } @@ -100,6 +106,12 @@ impl Val { val } + pub fn from_bool(b: bool) -> Self { + let mut val = Self::empty(); + val.set(b); + val + } + pub fn from_val(other: &Val) -> Self { let mut val = Self::empty(); val.set(other); @@ -255,6 +267,7 @@ impl SetVal for &T { T::set_val(self, val) } } + impl SetVal for &mut T { fn set_val(&self, val: &mut Val) { T::set_val(self, val) From 30eab80fea9299b0c6b986df2cbb17d122cefa93 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sat, 17 Apr 2021 22:12:57 +0800 Subject: [PATCH 13/20] Pass exmaples hello integation test. --- examples/hello/tests/integration.rs | 20 +++++++------------- examples/hello/tests/php/test.php | 22 ++++++++++++++++++++-- phper-test/src/lib.rs | 28 ++++++++++++++++++---------- phper/src/arrays.rs | 6 +++++- phper/src/errors.rs | 2 +- phper/src/values.rs | 4 ++-- 6 files changed, 53 insertions(+), 29 deletions(-) diff --git a/examples/hello/tests/integration.rs b/examples/hello/tests/integration.rs index d0193e5f..aa0c5c13 100644 --- a/examples/hello/tests/integration.rs +++ b/examples/hello/tests/integration.rs @@ -3,17 +3,11 @@ use std::{env, path::Path}; #[test] fn test_php() { - dbg!(env!("CARGO_BIN_EXE_hello")); - dbg!(env::current_exe()); - // test_php_scripts( - // Path::new(env!("CARGO_MANIFEST_DIR")) - // .join("..") - // .join("..") - // .join("target"), - // env!("CARGO_PKG_NAME"), - // &[Path::new(env!("CARGO_MANIFEST_DIR")) - // .join("tests") - // .join("php") - // .join("test.php")], - // ); + test_php_scripts( + env!("CARGO_BIN_EXE_hello"), + &[Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests") + .join("php") + .join("test.php")], + ); } diff --git a/examples/hello/tests/php/test.php b/examples/hello/tests/php/test.php index f13d5875..eda8a2ad 100644 --- a/examples/hello/tests/php/test.php +++ b/examples/hello/tests/php/test.php @@ -4,8 +4,26 @@ ini_set("display_startup_errors", "On"); error_reporting(E_ALL); -assert_eq(get_extension_funcs('hello'), ["say_hello"]); -assert_eq(say_hello("world"), "Hello, world!"); +assert_eq(hello_say_hello("world"), "Hello, world!\n"); + +assert_eq(class_exists("PHPerException"), true); + +try { + hello_throw_exception(); +} catch (PHPerException $e) { + assert_eq($e->getMessage(), "I am sorry"); +} + +assert_eq(hello_get_all_ini(), [ + "hello.enable" => true, + "hello.description" => "hello world.", +]); + +$foo = new FooClass(); +assert_eq($foo->getFoo(), 100); + +$foo->setFoo("Hello"); +assert_eq($foo->getFoo(), "Hello"); function assert_eq($left, $right) { if ($left !== $right) { diff --git a/phper-test/src/lib.rs b/phper-test/src/lib.rs index cfcf2f5b..6a8b5a71 100644 --- a/phper-test/src/lib.rs +++ b/phper-test/src/lib.rs @@ -3,22 +3,17 @@ use std::{ env, ffi::OsStr, fmt::Debug, fs::read_to_string, io::Write, path::Path, process::Command, }; use tempfile::NamedTempFile; +use std::ffi::OsString; +use std::path::PathBuf; +use std::error::Error; pub fn test_php_scripts( - target_dir: impl AsRef, - lib_name: &str, + exe_path: impl AsRef, scripts: &[impl AsRef], ) { let context = php_context(); - let lib_path = target_dir - .as_ref() - .join(if cfg!(debug_assertions) { - "debug" - } else { - "release" - }) - .join(format!("lib{}.so", lib_name)); + let lib_path = get_lib_path(exe_path); let mut out_ini_temp_file = NamedTempFile::new().unwrap(); let out_ini_file = out_ini_temp_file.as_file_mut(); @@ -117,3 +112,16 @@ fn execute_command + Debug>(argv: &[S]) -> String { .stdout; String::from_utf8(output).unwrap().trim().to_owned() } + +fn get_lib_path(exe_path: impl AsRef) -> PathBuf { + let exe_path = exe_path.as_ref(); + let exe_stem = exe_path.file_stem().expect("failed to get current exe stem"); + let target_dir = exe_path.parent().expect("failed to get current exe directory"); + + let mut ext_name = OsString::new(); + ext_name.push("lib"); + ext_name.push(exe_stem); + ext_name.push(".so"); + + target_dir.join(ext_name) +} diff --git a/phper/src/arrays.rs b/phper/src/arrays.rs index acf802e5..b25161be 100644 --- a/phper/src/arrays.rs +++ b/phper/src/arrays.rs @@ -18,11 +18,15 @@ impl Array { Self { inner } } + pub fn as_ptr(&self) -> *const zend_array { + self.inner.deref() + } + pub fn insert(&mut self, key: impl AsRef, value: &mut Val) { let key = key.as_ref(); unsafe { zend_hash_str_update( - &mut *self.inner, + self.inner.deref_mut(), key.as_ptr().cast(), key.len(), value.as_mut(), diff --git a/phper/src/errors.rs b/phper/src/errors.rs index 5163322e..e68e0776 100644 --- a/phper/src/errors.rs +++ b/phper/src/errors.rs @@ -39,7 +39,7 @@ pub trait Throwable: error::Error { fn code(&self) -> u64; } -pub(crate) const EXCEPTION_CLASS_NAME: &'static str = "\\Phper\\Exception\\ErrorException"; +pub(crate) const EXCEPTION_CLASS_NAME: &'static str = "PHPerException"; impl Throwable for Error { fn class_entity(&self) -> *const ClassEntity { diff --git a/phper/src/values.rs b/phper/src/values.rs index 3459a229..404e2028 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -34,7 +34,7 @@ impl ExecuteData { &mut *(ptr as *mut Self) } - pub fn as_mut(&mut self) -> *mut zend_execute_data { + pub fn as_mut_ptr(&mut self) -> *mut zend_execute_data { &mut self.inner } @@ -213,7 +213,7 @@ impl SetVal for String { impl SetVal for Array { fn set_val(&self, val: &mut Val) { unsafe { - phper_zval_arr(val.as_mut(), &self as *const _ as *mut _); + phper_zval_arr(val.as_mut(), self.as_ptr() as *mut _); } } } From edac5e8b6f0f24224491e54e19f5e3f1a786a4de Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sat, 17 Apr 2021 22:13:42 +0800 Subject: [PATCH 14/20] Temporarily delete the mini-curl example. --- examples/mini-curl/Cargo.toml | 22 ---- examples/mini-curl/Makefile.toml | 30 ----- examples/mini-curl/build.rs | 3 - examples/mini-curl/src/lib.rs | 162 ------------------------ examples/mini-curl/tests/integration.rs | 17 --- examples/mini-curl/tests/php/test.php | 15 --- 6 files changed, 249 deletions(-) delete mode 100644 examples/mini-curl/Cargo.toml delete mode 100644 examples/mini-curl/Makefile.toml delete mode 100644 examples/mini-curl/build.rs delete mode 100644 examples/mini-curl/src/lib.rs delete mode 100644 examples/mini-curl/tests/integration.rs delete mode 100644 examples/mini-curl/tests/php/test.php diff --git a/examples/mini-curl/Cargo.toml b/examples/mini-curl/Cargo.toml deleted file mode 100644 index bcd92df6..00000000 --- a/examples/mini-curl/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "mini-curl" -version = "0.0.0" -authors = ["jmjoy <918734043@qq.com>"] -edition = "2018" -publish = false -license = "Unlicense" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["cdylib"] - -[dependencies] -curl = "0.4.34" -phper = { version = "0.2", path = "../../phper" } - -[dev-dependencies] -phper-test = { version = "0.2", path = "../../phper-test" } - -[build-dependencies] -phper-build = { version = "0.2", path = "../../phper-build" } diff --git a/examples/mini-curl/Makefile.toml b/examples/mini-curl/Makefile.toml deleted file mode 100644 index 5f4e9061..00000000 --- a/examples/mini-curl/Makefile.toml +++ /dev/null @@ -1,30 +0,0 @@ -[env] -PHP_CONFIG = "php-config" -TARGET_DIR = "${CARGO_MAKE_WORKING_DIRECTORY}/../../target" - -[env.development] -TARGET_BUILD_DIR = "${TARGET_DIR}/debug" -BUILD_ARGS = "--" -TEST_ARGS = "--" - -[env.production] -TARGET_BUILD_DIR = "${TARGET_DIR}/release" -BUILD_ARGS = "--release" -TEST_ARGS = "--release" - -[tasks.build] -command = "cargo" -args = ["build", "${BUILD_ARGS}"] - -[tasks.test] -command = "cargo" -args = ["test", "${TEST_ARGS}", "--", "--nocapture"] - -[tasks.install] -dependencies = ["build"] -script = [ - """ - cp ${TARGET_BUILD_DIR}/lib${CARGO_MAKE_CRATE_NAME}.so `${PHP_CONFIG} --extension-dir`/${CARGO_MAKE_CRATE_NAME}.so && \ - echo Installing shared extensions: `${PHP_CONFIG} --extension-dir` - """ -] diff --git a/examples/mini-curl/build.rs b/examples/mini-curl/build.rs deleted file mode 100644 index 8be39f4f..00000000 --- a/examples/mini-curl/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - phper_build::register_configures(); -} diff --git a/examples/mini-curl/src/lib.rs b/examples/mini-curl/src/lib.rs deleted file mode 100644 index 959f0180..00000000 --- a/examples/mini-curl/src/lib.rs +++ /dev/null @@ -1,162 +0,0 @@ -#[php_minit_function] -fn module_init(args: ModuleArgs) -> bool { - args.register_ini_entries(&INI_ENTRIES); - MINI_CURL_CE.init("MiniCurl", &MINI_CURL_METHODS); - MINI_CURL_CE.declare_property("_rust_easy_ptr", 0, Visibility::Private); - true -} - -#[php_mshutdown_function] -fn module_shutdown(args: ModuleArgs) -> bool { - args.unregister_ini_entries(); - true -} - -#[php_rinit_function] -fn request_init(_args: ModuleArgs) -> bool { - true -} - -#[php_rshutdown_function] -fn request_shutdown(_args: ModuleArgs) -> bool { - true -} - -#[php_minfo_function] -fn module_info(__module: &ModuleEntry) { - unsafe { - php_info_print_table_start(); - php_info_print_table_end(); - } -} - -static ARG_INFO_VOID: MultiInternalArgInfo<0> = MultiInternalArgInfo::new(0, false, []); - -static ARG_INFO_MINI_CURL_CONSTRUCT: MultiInternalArgInfo<1> = - MultiInternalArgInfo::new(0, false, [create_zend_arg_info(c_str_ptr!("url"), false)]); - -static MINI_CURL_METHODS: FunctionEntries<3> = FunctionEntries::new([ - FunctionEntryBuilder::new( - c_str_ptr!("__construct"), - Some(php_fn!(mini_curl_construct)), - ) - .arg_info(&ARG_INFO_MINI_CURL_CONSTRUCT) - .build(), - FunctionEntryBuilder::new(c_str_ptr!("__destruct"), Some(php_fn!(mini_curl_destruct))) - .arg_info(&ARG_INFO_VOID) - .build(), - FunctionEntryBuilder::new(c_str_ptr!("exec"), Some(php_fn!(mini_curl_exec))) - .arg_info(&ARG_INFO_VOID) - .build(), -]); - -#[php_function] -pub fn mini_curl_construct(execute_data: &mut ExecuteData) -> impl SetVal { - let url = match execute_data.parse_parameters_optional::<&str, _>("") { - Some(url) => url, - None => return ReturnValue::Bool(false), - }; - - let this = execute_data.get_this(); - - let mut easy = Box::new(Easy::new()); - - if !url.is_empty() { - if let Err(e) = easy.url(url) { - error_doc_ref(Level::Warning, format!("curl set failed: {}\0", e)); - return ReturnValue::Bool(false); - } - } - - MINI_CURL_CE.update_property(this, "_rust_easy_ptr", Box::into_raw(easy) as i64); - - ReturnValue::Null -} - -#[php_function] -pub fn mini_curl_exec(execute_data: &mut ExecuteData) -> impl SetVal { - if execute_data.parse_parameters::<()>().is_none() { - return ReturnValue::Bool(false); - } - - let mut data = Vec::new(); - - let this = execute_data.get_this(); - let ptr = MINI_CURL_CE.read_property(this, "_rust_easy_ptr"); - let value = ptr.try_into_value().unwrap(); - let ptr = value.into_long().unwrap(); - - let mut handle = unsafe { Box::from_raw(ptr as *mut Easy) }; - let mut transfer = handle.transfer(); - transfer - .write_function(|new_data| { - data.extend_from_slice(new_data); - Ok(new_data.len()) - }) - .unwrap(); - transfer.perform().unwrap(); - drop(transfer); - - Box::into_raw(handle); - - ReturnValue::String(String::from_utf8(data).unwrap()) -} - -#[php_function] -pub fn mini_curl_destruct(execute_data: &mut ExecuteData) -> impl SetVal { - if execute_data.parse_parameters::<()>().is_none() { - return ReturnValue::Bool(false); - } - - let this = execute_data.get_this(); - let ptr = MINI_CURL_CE.read_property(this, "_rust_easy_ptr"); - let ptr = ptr.try_into_value().unwrap(); - if let Value::Long(ptr) = ptr { - unsafe { - drop(Box::from_raw(ptr as *mut Easy)); - } - } - - ReturnValue::Null -} - -static MODULE_ENTRY: ModuleEntry = ModuleEntryBuilder::new( - c_str_ptr!(env!("CARGO_PKG_NAME")), - c_str_ptr!(env!("CARGO_PKG_VERSION")), -) -.module_startup_func(php_minit!(module_init)) -.module_shutdown_func(php_mshutdown!(module_shutdown)) -.request_startup_func(php_rinit!(request_init)) -.request_shutdown_func(php_rshutdown!(request_shutdown)) -.info_func(php_minfo!(module_info)) -.build(); - - -fn curl_init() { - -} - - -#[get_module] -pub fn get_module() -> Module { - let module = Module::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); - - module.on_module_startup(|module_args: ModuleArgs| -> bool { - true - }); - module.on_module_shutdown(|module_args: ModuleArgs| -> bool { - true - }); - module.on_request_startup(|module_args: ModuleArgs| -> bool { - true - }); - module.on_request_shutdown(|module_args: ModuleArgs| -> bool { - true - }); - - module.add_ini("curl.cainfo", "???", INI::System); - - module.add_function("curl_init", curl_init); - - module -} diff --git a/examples/mini-curl/tests/integration.rs b/examples/mini-curl/tests/integration.rs deleted file mode 100644 index 573b6660..00000000 --- a/examples/mini-curl/tests/integration.rs +++ /dev/null @@ -1,17 +0,0 @@ -use phper_test::test_php_scripts; -use std::{env, path::Path}; - -#[test] -fn test_php() { - test_php_scripts( - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("target"), - env!("CARGO_PKG_NAME"), - &[Path::new(env!("CARGO_MANIFEST_DIR")) - .join("tests") - .join("php") - .join("test.php")], - ); -} diff --git a/examples/mini-curl/tests/php/test.php b/examples/mini-curl/tests/php/test.php deleted file mode 100644 index 6f73cdb8..00000000 --- a/examples/mini-curl/tests/php/test.php +++ /dev/null @@ -1,15 +0,0 @@ -exec(); -var_dump($response); - -function assert_eq($left, $right) { - if ($left !== $right) { - throw new Exception("left != right,\n left: {$left},\n right: {$right};"); - } -} \ No newline at end of file From 0726bffc35a131fa80296ffab22f64f3a7797993 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sat, 17 Apr 2021 22:25:21 +0800 Subject: [PATCH 15/20] Arrange ci. --- .github/workflows/ci.yml | 13 ++++++++++--- examples/hello/src/lib.rs | 15 ++++----------- phper-test/src/lib.rs | 26 +++++++++++++++----------- phper/src/arrays.rs | 1 - phper/src/classes.rs | 11 ++++------- phper/src/errors.rs | 12 ++---------- phper/src/functions.rs | 15 ++++++--------- phper/src/ini.rs | 9 +++------ phper/src/modules.rs | 18 ++++++++---------- phper/src/values.rs | 22 ++-------------------- 10 files changed, 54 insertions(+), 88 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d5852ad..dc0bce3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,18 +42,25 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust + - name: Install Rust Nightly uses: actions-rs/toolchain@v1 with: toolchain: nightly override: true - components: rustfmt, clippy + components: rustfmt + + - name: Install Rust Stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt - name: Cargo fmt uses: actions-rs/cargo@v1 with: command: fmt - args: --all -- --check + args: +nightly --all -- --check - name: Cargo build uses: actions-rs/cargo@v1 diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 45b89d17..755cb22c 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -1,19 +1,12 @@ use phper::{ arrays::Array, - c_str_ptr, - classes::{Class, StdClass, This}, - functions::{create_zend_arg_info, Argument}, + classes::{StdClass, This}, + functions::Argument, ini::Policy, - modules::{read_global_module, write_global_module, Module, ModuleArgs}, + modules::{Module, ModuleArgs}, php_get_module, - sys::{ - php_info_print_table_end, php_info_print_table_row, php_info_print_table_start, - zend_function_entry, OnUpdateBool, PHP_INI_SYSTEM, - }, - values::{ExecuteData, SetVal, Val}, - Throwable, + values::{SetVal, Val}, }; -use std::{fs::OpenOptions, io::Write}; fn module_init(_args: ModuleArgs) -> bool { true diff --git a/phper-test/src/lib.rs b/phper-test/src/lib.rs index 6a8b5a71..0d154abc 100644 --- a/phper-test/src/lib.rs +++ b/phper-test/src/lib.rs @@ -1,16 +1,16 @@ use once_cell::sync::OnceCell; use std::{ - env, ffi::OsStr, fmt::Debug, fs::read_to_string, io::Write, path::Path, process::Command, + env, + ffi::{OsStr, OsString}, + fmt::Debug, + fs::read_to_string, + io::Write, + path::{Path, PathBuf}, + process::Command, }; use tempfile::NamedTempFile; -use std::ffi::OsString; -use std::path::PathBuf; -use std::error::Error; - -pub fn test_php_scripts( - exe_path: impl AsRef, - scripts: &[impl AsRef], -) { + +pub fn test_php_scripts(exe_path: impl AsRef, scripts: &[impl AsRef]) { let context = php_context(); let lib_path = get_lib_path(exe_path); @@ -115,8 +115,12 @@ fn execute_command + Debug>(argv: &[S]) -> String { fn get_lib_path(exe_path: impl AsRef) -> PathBuf { let exe_path = exe_path.as_ref(); - let exe_stem = exe_path.file_stem().expect("failed to get current exe stem"); - let target_dir = exe_path.parent().expect("failed to get current exe directory"); + let exe_stem = exe_path + .file_stem() + .expect("failed to get current exe stem"); + let target_dir = exe_path + .parent() + .expect("failed to get current exe directory"); let mut ext_name = OsString::new(); ext_name.push("lib"); diff --git a/phper/src/arrays.rs b/phper/src/arrays.rs index b25161be..8b703383 100644 --- a/phper/src/arrays.rs +++ b/phper/src/arrays.rs @@ -2,7 +2,6 @@ use crate::{sys::*, values::Val}; use std::{ mem::zeroed, ops::{Deref, DerefMut}, - ptr::null_mut, }; pub struct Array { diff --git a/phper/src/classes.rs b/phper/src/classes.rs index 269e8ac6..99f8828c 100644 --- a/phper/src/classes.rs +++ b/phper/src/classes.rs @@ -1,5 +1,5 @@ use crate::{ - functions::{invoke, Argument, Callable, FunctionEntity, FunctionEntry, Method}, + functions::{Argument, Callable, FunctionEntity, FunctionEntry, Method}, sys::*, values::{SetVal, Val}, }; @@ -7,11 +7,8 @@ use once_cell::sync::OnceCell; use std::{ mem::zeroed, os::raw::c_int, - ptr::{null, null_mut}, - sync::{ - atomic::{AtomicPtr, Ordering}, - Arc, Once, RwLock, - }, + ptr::null_mut, + sync::atomic::{AtomicPtr, Ordering}, }; pub trait Class: Send + Sync { @@ -58,7 +55,7 @@ impl StdClass { } pub fn extends(&mut self, name: impl ToString) { - let mut name = name.to_string(); + let name = name.to_string(); self.parent = Some(name); } } diff --git a/phper/src/errors.rs b/phper/src/errors.rs index e68e0776..f7bb322b 100644 --- a/phper/src/errors.rs +++ b/phper/src/errors.rs @@ -1,14 +1,6 @@ -use crate::{ - classes::{ClassEntity, ClassEntry}, - modules::{read_global_module, write_global_module}, - Error::Other, -}; +use crate::{classes::ClassEntity, modules::read_global_module, Error::Other}; use anyhow::anyhow; -use once_cell::sync::Lazy; -use std::{ - cell::RefCell, error, ffi::FromBytesWithNulError, io, ptr::null_mut, str::Utf8Error, - sync::atomic::AtomicPtr, -}; +use std::{error, ffi::FromBytesWithNulError, io, str::Utf8Error}; pub type Result = std::result::Result; diff --git a/phper/src/functions.rs b/phper/src/functions.rs index 1fcfaed2..11dccc51 100644 --- a/phper/src/functions.rs +++ b/phper/src/functions.rs @@ -1,16 +1,12 @@ use crate::{ classes::{ClassEntry, This}, - errors::Throwable, - ini::create_ini_entry_ex, sys::*, values::{ExecuteData, SetVal, Val}, }; use std::{ - cell::Cell, - ffi::CStr, - mem::{size_of, transmute, zeroed}, - os::raw::{c_char, c_int}, - ptr::{null, null_mut}, + mem::zeroed, + os::raw::c_char, + ptr::null, sync::atomic::{AtomicPtr, Ordering}, }; @@ -51,6 +47,7 @@ pub(crate) enum Callable { #[repr(transparent)] pub struct FunctionEntry { + #[allow(dead_code)] inner: zend_function_entry, } @@ -88,9 +85,9 @@ impl FunctionEntity { )); } - infos.push(unsafe { zeroed::() }); + infos.push(zeroed::()); - let mut last_arg_info = unsafe { zeroed::() }; + let mut last_arg_info = zeroed::(); last_arg_info.name = ((&self.handler) as *const _ as *mut i8).cast(); infos.push(last_arg_info); diff --git a/phper/src/ini.rs b/phper/src/ini.rs index dcde0c44..9caa8643 100644 --- a/phper/src/ini.rs +++ b/phper/src/ini.rs @@ -1,15 +1,12 @@ use crate::sys::{ - phper_zend_ini_mh, zend_ini_entry, zend_ini_entry_def, zend_string, OnUpdateBool, OnUpdateLong, - OnUpdateReal, OnUpdateString, PHP_INI_ALL, PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, + phper_zend_ini_mh, zend_ini_entry_def, OnUpdateBool, OnUpdateLong, OnUpdateReal, + OnUpdateString, PHP_INI_ALL, PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, }; use std::{ - cell::Cell, ffi::CStr, - mem::{size_of, transmute}, - os::raw::{c_char, c_int, c_void}, + os::raw::{c_char, c_void}, ptr::null_mut, str, - sync::atomic::AtomicPtr, }; type OnModify = phper_zend_ini_mh; diff --git a/phper/src/modules.rs b/phper/src/modules.rs index 5c0f8501..ac95db05 100644 --- a/phper/src/modules.rs +++ b/phper/src/modules.rs @@ -1,7 +1,7 @@ use crate::{ c_str_ptr, - classes::{Class, ClassEntity, ClassEntry, StdClass}, - functions::{create_zend_arg_info, invoke, Argument, Callable, Function, FunctionEntity}, + classes::{Class, ClassEntity, StdClass}, + functions::{Argument, Callable, Function, FunctionEntity}, ini::{IniEntity, IniValue, Policy, StrPtrBox}, sys::*, EXCEPTION_CLASS_NAME, @@ -9,15 +9,12 @@ use crate::{ use once_cell::sync::Lazy; use std::{ borrow::BorrowMut, - cell::{Cell, RefCell, RefMut}, + cell::RefCell, collections::HashMap, - ffi::CStr, - mem::{forget, size_of, transmute, zeroed}, - ops::DerefMut, - os::raw::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void}, + mem::{size_of, zeroed}, + os::raw::{c_int, c_uchar, c_uint, c_ushort}, ptr::{null, null_mut}, - sync::{atomic::AtomicPtr, Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, - thread::LocalKey, + sync::RwLock, }; static GLOBAL_MODULE: Lazy> = Lazy::new(Default::default); @@ -293,7 +290,7 @@ impl Module { Self::STR_INI_ENTITIES .with(|entities| Self::push_ini_entry(&mut entries, &mut *entities.borrow_mut())); - entries.push(unsafe { zeroed::() }); + entries.push(zeroed::()); Box::into_raw(entries.into_boxed_slice()).cast() } @@ -315,6 +312,7 @@ impl Module { } pub struct ModuleArgs { + #[allow(dead_code)] r#type: c_int, module_number: c_int, } diff --git a/phper/src/values.rs b/phper/src/values.rs index 404e2028..97589884 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -1,23 +1,5 @@ -use crate::{ - arrays::Array, - c_str_ptr, - classes::{This, Visibility}, - errors::Throwable, - sys::*, -}; -use std::{ - borrow::Cow, - cell::Cell, - ffi::{c_void, CStr}, - io::empty, - mem::{size_of, zeroed}, - os::raw::{c_char, c_int}, - ptr::null_mut, - slice, - slice::from_raw_parts, - str, - sync::atomic::Ordering, -}; +use crate::{arrays::Array, errors::Throwable, sys::*}; +use std::{mem::zeroed, os::raw::c_int, slice::from_raw_parts, str, sync::atomic::Ordering}; #[repr(transparent)] pub struct ExecuteData { From e261b665b7e8252788496f00b9e65c1e20025b9b Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sat, 17 Apr 2021 22:32:29 +0800 Subject: [PATCH 16/20] Add ci toolchain specification. --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc0bce3f..c03cb061 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,22 +59,26 @@ jobs: - name: Cargo fmt uses: actions-rs/cargo@v1 with: + toolchain: nightly command: fmt - args: +nightly --all -- --check + args: --all -- --check - name: Cargo build uses: actions-rs/cargo@v1 with: + toolchain: stable command: build args: --release - name: Cargo test uses: actions-rs/cargo@v1 with: + toolchain: stable command: test args: --release -- --nocapture - name: Cargo doc uses: actions-rs/cargo@v1 with: + toolchain: stable command: doc From 6ad5417c477b78d5937cffb7d9509fd18ab49bd2 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sun, 18 Apr 2021 00:04:10 +0800 Subject: [PATCH 17/20] Fix some compatibility problems. --- examples/hello/tests/php/test.php | 4 ++-- phper-sys/php_wrapper.c | 6 +++++- phper-sys/php_wrapper.h | 4 ++++ phper-test/src/lib.rs | 14 +++++++++----- phper/src/arrays.rs | 4 ++-- phper/src/classes.rs | 2 +- phper/src/functions.rs | 26 +++++++++++++++++++++----- phper/src/values.rs | 12 ++++++------ 8 files changed, 50 insertions(+), 22 deletions(-) diff --git a/examples/hello/tests/php/test.php b/examples/hello/tests/php/test.php index eda8a2ad..ac54bc52 100644 --- a/examples/hello/tests/php/test.php +++ b/examples/hello/tests/php/test.php @@ -15,7 +15,7 @@ } assert_eq(hello_get_all_ini(), [ - "hello.enable" => true, + "hello.enable" => false, "hello.description" => "hello world.", ]); @@ -27,6 +27,6 @@ function assert_eq($left, $right) { if ($left !== $right) { - throw new Exception("left != right,\n left: {$left},\n right: {$right};"); + throw new Exception(sprintf("left != right,\n left: %s,\n right: %s", var_export($left, true), var_export($right, true))); } } \ No newline at end of file diff --git a/phper-sys/php_wrapper.c b/phper-sys/php_wrapper.c index 41335168..1367b559 100644 --- a/phper-sys/php_wrapper.c +++ b/phper-sys/php_wrapper.c @@ -64,4 +64,8 @@ zend_string *phper_zend_string_alloc(size_t len, int persistent) { void phper_zend_string_release(zend_string *s) { return zend_string_release(s); -} \ No newline at end of file +} + +void phper_zend_hash_str_update(HashTable *ht, const char *key, size_t len, zval *pData) { + zend_hash_str_update(ht, key, len, pData); +} diff --git a/phper-sys/php_wrapper.h b/phper-sys/php_wrapper.h index 03ff6c95..36dc1836 100644 --- a/phper-sys/php_wrapper.h +++ b/phper-sys/php_wrapper.h @@ -27,4 +27,8 @@ zend_string *phper_zend_string_init(const char *str, size_t len, int persistent) zend_string *phper_zend_string_alloc(size_t len, int persistent); void phper_zend_string_release(zend_string *s); +void phper_zend_hash_str_update(HashTable *ht, const char *key, size_t len, zval *pData); + + + #endif //PHPER_PHP_WRAPPER_H diff --git a/phper-test/src/lib.rs b/phper-test/src/lib.rs index 0d154abc..b31fade0 100644 --- a/phper-test/src/lib.rs +++ b/phper-test/src/lib.rs @@ -88,11 +88,15 @@ fn php_context() -> &'static Context { "echo php_ini_scanned_files();", ]); - ini_content.push_str(&read_to_string(ini_file).unwrap()); - for file in ini_files.split(',') { - let file = file.trim(); - if !file.is_empty() { - ini_content.push_str(&read_to_string(file).unwrap()); + if !ini_file.is_empty() { + ini_content.push_str(&read_to_string(ini_file).unwrap()); + } + if !ini_files.is_empty() { + for file in ini_files.split(',') { + let file = file.trim(); + if !file.is_empty() { + ini_content.push_str(&read_to_string(file).unwrap()); + } } } diff --git a/phper/src/arrays.rs b/phper/src/arrays.rs index 8b703383..0defca03 100644 --- a/phper/src/arrays.rs +++ b/phper/src/arrays.rs @@ -12,7 +12,7 @@ impl Array { pub fn new() -> Self { let mut inner = Box::new(unsafe { zeroed::() }); unsafe { - _zend_hash_init(&mut *inner, 0, None, 1); + _zend_hash_init(&mut *inner, 0, None, true.into()); } Self { inner } } @@ -24,7 +24,7 @@ impl Array { pub fn insert(&mut self, key: impl AsRef, value: &mut Val) { let key = key.as_ref(); unsafe { - zend_hash_str_update( + phper_zend_hash_str_update( self.inner.deref_mut(), key.as_ptr().cast(), key.len(), diff --git a/phper/src/classes.rs b/phper/src/classes.rs index 99f8828c..209b83b8 100644 --- a/phper/src/classes.rs +++ b/phper/src/classes.rs @@ -181,7 +181,7 @@ impl This { self.val as *mut _, name.as_ptr().cast(), name.len(), - 0, + false.into(), null_mut(), ); Val::from_mut(prop) diff --git a/phper/src/functions.rs b/phper/src/functions.rs index 11dccc51..1da6df03 100644 --- a/phper/src/functions.rs +++ b/phper/src/functions.rs @@ -170,7 +170,12 @@ pub(crate) unsafe extern "C" fn invoke( execute_data.common_required_num_args(), execute_data.num_args() ); - php_error_docref(null(), E_WARNING as i32, s.as_ptr().cast()); + php_error_docref1( + null(), + "\0".as_ptr().cast(), + E_WARNING as i32, + s.as_ptr().cast(), + ); return_value.set(()); return; } @@ -190,10 +195,21 @@ pub(crate) unsafe extern "C" fn invoke( pub const fn create_zend_arg_info( name: *const c_char, - pass_by_ref: bool, + _pass_by_ref: bool, ) -> zend_internal_arg_info { + #[cfg(phper_php_version = "8.0")] + { + zend_internal_arg_info { + name, + type_: zend_type { + ptr: null_mut(), + type_mask: 0, + }, + default_value: null_mut(), + } + } + #[cfg(any( - phper_php_version = "8.0", phper_php_version = "7.4", phper_php_version = "7.3", phper_php_version = "7.2" @@ -202,7 +218,7 @@ pub const fn create_zend_arg_info( zend_internal_arg_info { name, type_: 0 as crate::sys::zend_type, - pass_by_reference: pass_by_ref as zend_uchar, + pass_by_reference: _pass_by_ref as zend_uchar, is_variadic: 0, } } @@ -214,7 +230,7 @@ pub const fn create_zend_arg_info( class_name: std::ptr::null(), type_hint: 0, allow_null: 0, - pass_by_reference: pass_by_ref as zend_uchar, + pass_by_reference: _pass_by_ref as zend_uchar, is_variadic: 0, } } diff --git a/phper/src/values.rs b/phper/src/values.rs index 97589884..7914582a 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -1,5 +1,5 @@ use crate::{arrays::Array, errors::Throwable, sys::*}; -use std::{mem::zeroed, os::raw::c_int, slice::from_raw_parts, str, sync::atomic::Ordering}; +use std::{mem::zeroed, slice::from_raw_parts, str, sync::atomic::Ordering}; #[repr(transparent)] pub struct ExecuteData { @@ -26,8 +26,8 @@ impl ExecuteData { } #[inline] - pub unsafe fn common_required_num_args(&self) -> u32 { - (*self.inner.func).common.required_num_args + pub unsafe fn common_required_num_args(&self) -> u16 { + (*self.inner.func).common.required_num_args as u16 } #[inline] @@ -36,8 +36,8 @@ impl ExecuteData { } #[inline] - pub unsafe fn num_args(&self) -> u32 { - self.inner.This.u2.num_args + pub unsafe fn num_args(&self) -> u16 { + self.inner.This.u2.num_args as u16 } #[inline] @@ -48,7 +48,7 @@ impl ExecuteData { pub unsafe fn get_parameters_array(&mut self) -> Vec { let num_args = self.num_args(); let mut arguments = vec![zeroed::(); num_args as usize]; - _zend_get_parameters_array_ex(num_args as c_int, arguments.as_mut_ptr()); + _zend_get_parameters_array_ex(num_args.into(), arguments.as_mut_ptr()); arguments.into_iter().map(Val::from_inner).collect() } } From 91721b4cd61fc1d9e0ad677c2b504fce67b34ac6 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sun, 18 Apr 2021 09:55:24 +0800 Subject: [PATCH 18/20] Modify README. --- README.md | 108 ++++++++++++++++++++++++++++++++++-- examples/hello/README.md | 28 ++++++++++ examples/hello/src/lib.rs | 2 +- phper/Cargo.toml | 1 + phper/src/functions.rs | 1 + phper/src/lib.rs | 112 +++++++++++++++++++++++++++++++++++--- 6 files changed, 238 insertions(+), 14 deletions(-) create mode 100644 examples/hello/README.md diff --git a/README.md b/README.md index 5d7d7fec..66070e1a 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,116 @@ # PHPer +[![crates](https://img.shields.io/crates/v/phper?style=flat-square)](https://crates.io/crates/phper) +[![](https://img.shields.io/docsrs/phper?style=flat-square)](https://docs.rs/phper) + A library that allows us to write PHP extensions using pure Rust and using safe Rust whenever possible. ## Requirement -- os: linux -- libclang: version >= 9 -- php: version in (7.1, 7.2, 7.3, 7.4, 8.0), mode: nts +### Necessary + +**libclang** version >= 9 + +**php** version >= 7 + +### Tested Support + +**os** + +- linux + +**php** + +*version* + +- 7.0 +- 7.1 +- 7.2 +- 7.3 +- 7.4 +- 8.0 + +*mode* + +- nts + +*sapi* + +- cli ## Usage -Now see [examples](examples). +1. Make sure `libclang` and `php` is installed. + +```bash +# If you are using debian like linux system: +sudo apt install libclang-10-dev php-cli +``` + +2. Create you cargo project, suppose your application is called myapp. + +```bash +cargo new myapp +``` + +3. Add the dependencies and metadata to you Cargo project. + +```toml +[lib] +crate-type = ["cdylib"] + +[dependencies] +phper = "0.2" +``` + +4. Add these code to `main.rs`. + +```rust +use phper::cmd::make; + +fn main() { + make(); +} +``` + +5. Write you owned extension logic in `lib.rs`. + +```rust +#[php_get_module] +pub fn get_module(module: &mut Module) { + // set module metadata + module.set_name(env!("CARGO_PKG_NAME")); + module.set_version(env!("CARGO_PKG_VERSION")); + module.set_author(env!("CARGO_PKG_AUTHORS")); + + // ... +} +``` + +6. Build and install, if your php isn't installed globally, you should specify the path of `php-config`. + +```bash +# Specify if php isn't installed globally. +export PHP_CONFIG = + +# Build libmyapp.so. +cargo build --release + +# Install to php extension path, if you install php globally, you should use sudo. +cargo run --release -- install +``` + +7. Edit your `php.ini`, add the below line. + +```ini +extension = myapp +``` + +8. Enjoy. + +## examples + +See [examples](examples). ## License diff --git a/examples/hello/README.md b/examples/hello/README.md new file mode 100644 index 00000000..750c2142 --- /dev/null +++ b/examples/hello/README.md @@ -0,0 +1,28 @@ +# hello + +Hello world example. + +## Environment + +```bash +# Specify if php isn't installed globally. +export PHP_CONFIG = +``` + +## Build + +```bash +cargo build --release +``` + +## Test + +```bash +cargo test --release +``` + +## Install + +```bash +cargo run --release -- install +``` diff --git a/examples/hello/src/lib.rs b/examples/hello/src/lib.rs index 755cb22c..0a40f298 100644 --- a/examples/hello/src/lib.rs +++ b/examples/hello/src/lib.rs @@ -22,7 +22,7 @@ fn throw_exception(_: &mut [Val]) -> phper::Result<()> { } #[php_get_module] -pub extern "C" fn get_module(module: &mut Module) { +pub fn get_module(module: &mut Module) { // set module metadata module.set_name(env!("CARGO_PKG_NAME")); module.set_version(env!("CARGO_PKG_VERSION")); diff --git a/phper/Cargo.toml b/phper/Cargo.toml index 31f74e90..39b718a0 100644 --- a/phper/Cargo.toml +++ b/phper/Cargo.toml @@ -5,6 +5,7 @@ authors = ["jmjoy <918734043@qq.com>"] edition = "2018" description = "A library that allows us to write PHP extensions using pure Rust and using safe Rust whenever possible." repository = "https://github.com/jmjoy/phper.git" +documentation = "https://docs.rs/phper" license = "Unlicense" readme = "../README.md" keywords = ["php", "binding", "extension"] diff --git a/phper/src/functions.rs b/phper/src/functions.rs index 1da6df03..a5c0e245 100644 --- a/phper/src/functions.rs +++ b/phper/src/functions.rs @@ -199,6 +199,7 @@ pub const fn create_zend_arg_info( ) -> zend_internal_arg_info { #[cfg(phper_php_version = "8.0")] { + use std::ptr::null_mut; zend_internal_arg_info { name, type_: zend_type { diff --git a/phper/src/lib.rs b/phper/src/lib.rs index 25252c1f..bc4ea1f7 100644 --- a/phper/src/lib.rs +++ b/phper/src/lib.rs @@ -1,29 +1,123 @@ #![warn(rust_2018_idioms, clippy::dbg_macro, clippy::print_stdout)] /*! +# PHPer + +[![crates](https://img.shields.io/crates/v/phper?style=flat-square)](https://crates.io/crates/phper) +[![](https://img.shields.io/docsrs/phper?style=flat-square)](https://docs.rs/phper) + A library that allows us to write PHP extensions using pure Rust and using safe Rust whenever possible. -***Now the peojct is still under development.*** +## Requirement + +### Necessary + +**libclang** version >= 9 + +**php** version >= 7 + +### Tested Support + +**os** + +- linux + +**php** + +*version* -# Usage +- 7.0 +- 7.1 +- 7.2 +- 7.3 +- 7.4 +- 8.0 + +*mode* + +- nts + +*sapi* + +- cli + +## Usage + +1. Make sure `libclang` and `php` is installed. + +```bash +# If you are using debian like linux system: +sudo apt install libclang-10-dev php-cli +``` -First you have to install `cargo-generate`: +2. Create you cargo project, suppose your application is called myapp. ```bash -cargo install cargo-generate +cargo new myapp ``` -Then create a PHP extension project from the [template](https://github.com/jmjoy/phper-ext-skel.git): +3. Add the dependencies and metadata to you Cargo project. + +```toml +[lib] +crate-type = ["cdylib"] + +[dependencies] +phper = "0.2" +``` + +4. Add these code to `main.rs`. + +```rust +use phper::cmd::make; + +fn main() { + make(); +} +``` + +5. Write you owned extension logic in `lib.rs`. + +```rust +#[php_get_module] +pub fn get_module(module: &mut Module) { + // set module metadata + module.set_name(env!("CARGO_PKG_NAME")); + module.set_version(env!("CARGO_PKG_VERSION")); + module.set_author(env!("CARGO_PKG_AUTHORS")); + + // ... +} +``` + +6. Build and install, if your php isn't installed globally, you should specify the path of `php-config`. ```bash -cargo generate --git https://github.com/jmjoy/phper-ext-skel.git +# Specify if php isn't installed globally. +export PHP_CONFIG = + +# Build libmyapp.so. +cargo build --release + +# Install to php extension path, if you install php globally, you should use sudo. +cargo run --release -- install ``` -# Notice +7. Edit your `php.ini`, add the below line. + +```ini +extension = myapp +``` + +8. Enjoy. + +## examples + +See [examples](https://github.com/jmjoy/phper/tree/master/examples). -Now the library don't support `ZTS`, the template is using `thread_local!` instead. +## License -Version `0.1.x` will be a preview version. +[Unlicense](https://github.com/jmjoy/phper/blob/master/LICENSE). */ pub mod arrays; From b50cbfe244bfedf3df2d4f57d7e8319cce354d8b Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sun, 18 Apr 2021 10:18:06 +0800 Subject: [PATCH 19/20] Fix doc test. --- README.md | 14 ++++++++------ phper/src/lib.rs | 4 +++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 66070e1a..391cf6a1 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ A library that allows us to write PHP extensions using pure Rust and using safe **os** - linux - + **php** *version* @@ -29,7 +29,7 @@ A library that allows us to write PHP extensions using pure Rust and using safe - 7.3 - 7.4 - 8.0 - + *mode* - nts @@ -41,7 +41,7 @@ A library that allows us to write PHP extensions using pure Rust and using safe ## Usage 1. Make sure `libclang` and `php` is installed. - + ```bash # If you are using debian like linux system: sudo apt install libclang-10-dev php-cli @@ -76,13 +76,15 @@ fn main() { 5. Write you owned extension logic in `lib.rs`. ```rust +use phper::{php_get_module, modules::Module}; + #[php_get_module] pub fn get_module(module: &mut Module) { // set module metadata module.set_name(env!("CARGO_PKG_NAME")); module.set_version(env!("CARGO_PKG_VERSION")); module.set_author(env!("CARGO_PKG_AUTHORS")); - + // ... } ``` @@ -110,8 +112,8 @@ extension = myapp ## examples -See [examples](examples). +See [examples](https://github.com/jmjoy/phper/tree/master/examples). ## License -[Unlicense](LICENSE). +[Unlicense](https://github.com/jmjoy/phper/blob/master/LICENSE). diff --git a/phper/src/lib.rs b/phper/src/lib.rs index bc4ea1f7..eef8b071 100644 --- a/phper/src/lib.rs +++ b/phper/src/lib.rs @@ -68,7 +68,7 @@ phper = "0.2" 4. Add these code to `main.rs`. -```rust +```rust,no_run use phper::cmd::make; fn main() { @@ -79,6 +79,8 @@ fn main() { 5. Write you owned extension logic in `lib.rs`. ```rust +use phper::{php_get_module, modules::Module}; + #[php_get_module] pub fn get_module(module: &mut Module) { // set module metadata From fc5c3dfe6aa182d8ee631d178e3e36ec8ed12dc6 Mon Sep 17 00:00:00 2001 From: jmjoy <918734043@qq.com> Date: Sun, 18 Apr 2021 12:41:24 +0800 Subject: [PATCH 20/20] Compat php 8 for zend_read_property. --- phper/src/classes.rs | 39 ++++++++++++++++++++++++++++----------- phper/src/values.rs | 2 +- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/phper/src/classes.rs b/phper/src/classes.rs index 209b83b8..f9541079 100644 --- a/phper/src/classes.rs +++ b/phper/src/classes.rs @@ -175,17 +175,34 @@ impl This { pub fn get_property(&self, name: impl AsRef) -> &mut Val { let name = name.as_ref(); - unsafe { - let prop = zend_read_property( - self.class as *mut _, - self.val as *mut _, - name.as_ptr().cast(), - name.len(), - false.into(), - null_mut(), - ); - Val::from_mut(prop) - } + + let prop = unsafe { + #[cfg(phper_major_version = "8")] + { + zend_read_property( + self.class as *mut _, + (*self.val).inner.value.obj, + name.as_ptr().cast(), + name.len(), + false.into(), + null_mut(), + ) + } + + #[cfg(phper_major_version = "7")] + { + zend_read_property( + self.class as *mut _, + self.val as *mut _, + name.as_ptr().cast(), + name.len(), + false.into(), + null_mut(), + ) + } + }; + + unsafe { Val::from_mut(prop) } } } diff --git a/phper/src/values.rs b/phper/src/values.rs index 7914582a..73843896 100644 --- a/phper/src/values.rs +++ b/phper/src/values.rs @@ -55,7 +55,7 @@ impl ExecuteData { #[repr(transparent)] pub struct Val { - inner: zval, + pub(crate) inner: zval, } impl Val {