diff --git a/Cargo.toml b/Cargo.toml index 14eb22f23a..12210429fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ authors = [ "Xenira ", "David Cole ", ] -edition = "2021" +edition = "2024" categories = ["api-bindings"] exclude = ["/.github", "/.crates"] autotests = false diff --git a/build.rs b/build.rs index 21ee907c56..058a358926 100644 --- a/build.rs +++ b/build.rs @@ -16,7 +16,7 @@ use std::{ str::FromStr, }; -use anyhow::{anyhow, bail, Context, Error, Result}; +use anyhow::{Context, Error, Result, anyhow, bail}; use bindgen::RustTarget; use impl_::Provider; @@ -332,17 +332,25 @@ impl TryFrom for ApiVersion { fn try_from(version: u32) -> Result { match version { - x if ((ApiVersion::Php80 as u32)..(ApiVersion::Php81 as u32)).contains(&x) => Ok(ApiVersion::Php80), - x if ((ApiVersion::Php81 as u32)..(ApiVersion::Php82 as u32)).contains(&x) => Ok(ApiVersion::Php81), - x if ((ApiVersion::Php82 as u32)..(ApiVersion::Php83 as u32)).contains(&x) => Ok(ApiVersion::Php82), - x if ((ApiVersion::Php83 as u32)..(ApiVersion::Php84 as u32)).contains(&x) => Ok(ApiVersion::Php83), + x if ((ApiVersion::Php80 as u32)..(ApiVersion::Php81 as u32)).contains(&x) => { + Ok(ApiVersion::Php80) + } + x if ((ApiVersion::Php81 as u32)..(ApiVersion::Php82 as u32)).contains(&x) => { + Ok(ApiVersion::Php81) + } + x if ((ApiVersion::Php82 as u32)..(ApiVersion::Php83 as u32)).contains(&x) => { + Ok(ApiVersion::Php82) + } + x if ((ApiVersion::Php83 as u32)..(ApiVersion::Php84 as u32)).contains(&x) => { + Ok(ApiVersion::Php83) + } x if (ApiVersion::Php84 as u32) == x => Ok(ApiVersion::Php84), version => Err(anyhow!( - "The current version of PHP is not supported. Current PHP API version: {}, requires a version between {} and {}", - version, - ApiVersion::min() as u32, - ApiVersion::max() as u32 - )) + "The current version of PHP is not supported. Current PHP API version: {}, requires a version between {} and {}", + version, + ApiVersion::min() as u32, + ApiVersion::max() as u32 + )), } } } @@ -367,7 +375,9 @@ fn check_php_version(info: &PHPInfo) -> Result<()> { ); if version == ApiVersion::Php80 { - println!("cargo:warning=PHP 8.0 is EOL and is no longer supported. Please upgrade to a supported version of PHP. See https://www.php.net/supported-versions.php for information on version support timelines."); + println!( + "cargo:warning=PHP 8.0 is EOL and is no longer supported. Please upgrade to a supported version of PHP. See https://www.php.net/supported-versions.php for information on version support timelines." + ); } for supported_version in version.supported_apis() { diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 95acfed720..f230ea08ce 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -8,9 +8,10 @@ keywords = ["php", "ffi", "zend"] version = "0.1.13" authors = [ "Xenira ", - "David Cole " + "David Cole ", + "Pierre Tondereau ", ] -edition = "2018" +edition = "2024" categories = ["api-bindings", "command-line-interface"] [dependencies] diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 9fbceca43e..fe744141f3 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -3,8 +3,8 @@ #[cfg(not(windows))] mod ext; -use anyhow::{bail, Context, Result as AResult}; -use cargo_metadata::{camino::Utf8PathBuf, CrateType, Target}; +use anyhow::{Context, Result as AResult, bail}; +use cargo_metadata::{CrateType, Target, camino::Utf8PathBuf}; use clap::Parser; use dialoguer::{Confirm, Select}; @@ -27,7 +27,7 @@ macro_rules! stub_symbols { (@INTERNAL; $s: ident) => { #[allow(non_upper_case_globals)] #[allow(missing_docs)] - #[no_mangle] + #[unsafe(no_mangle)] pub static mut $s: *mut () = ::std::ptr::null_mut(); }; } @@ -221,9 +221,9 @@ impl Install { ext_dir.push(ext_name); } - std::fs::copy(&ext_path, &ext_dir).with_context(|| { - "Failed to copy extension from target directory to extension directory" - })?; + std::fs::copy(&ext_path, &ext_dir).with_context( + || "Failed to copy extension from target directory to extension directory", + )?; if let Some(php_ini) = php_ini { let mut file = OpenOptions::new() @@ -408,15 +408,17 @@ impl Stubs { let result = ext.describe(); // Ensure extension and CLI `ext-php-rs` versions are compatible. - let cli_version = semver::VersionReq::from_str(ext_php_rs::VERSION).with_context(|| { - "Failed to parse `ext-php-rs` version that `cargo php` was compiled with" - })?; - let ext_version = semver::Version::from_str(result.version).with_context(|| { - "Failed to parse `ext-php-rs` version that your extension was compiled with" - })?; + let cli_version = semver::VersionReq::from_str(ext_php_rs::VERSION).with_context( + || "Failed to parse `ext-php-rs` version that `cargo php` was compiled with", + )?; + let ext_version = semver::Version::from_str(result.version).with_context( + || "Failed to parse `ext-php-rs` version that your extension was compiled with", + )?; if !cli_version.matches(&ext_version) { - bail!("Extension was compiled with an incompatible version of `ext-php-rs` - Extension: {ext_version}, CLI: {cli_version}"); + bail!( + "Extension was compiled with an incompatible version of `ext-php-rs` - Extension: {ext_version}, CLI: {cli_version}" + ); } let stubs = result diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml index 2de0206805..a8f8cb64f4 100644 --- a/crates/macros/Cargo.toml +++ b/crates/macros/Cargo.toml @@ -7,9 +7,10 @@ license = "MIT OR Apache-2.0" version = "0.11.4" authors = [ "Xenira ", - "David Cole " + "David Cole ", + "Pierre Tondereau ", ] -edition = "2021" +edition = "2024" [lib] proc-macro = true diff --git a/crates/macros/src/enum_.rs b/crates/macros/src/enum_.rs index d10ab89c91..910f05cf21 100644 --- a/crates/macros/src/enum_.rs +++ b/crates/macros/src/enum_.rs @@ -1,9 +1,9 @@ use std::convert::TryFrom; -use darling::{util::Flag, FromAttributes}; +use darling::{FromAttributes, util::Flag}; use itertools::Itertools; use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; +use quote::{ToTokens, quote}; use syn::{Fields, Ident, ItemEnum, Lit}; use crate::{ diff --git a/crates/macros/src/extern_.rs b/crates/macros/src/extern_.rs index 4a55d4bdaf..8718b2a71e 100644 --- a/crates/macros/src/extern_.rs +++ b/crates/macros/src/extern_.rs @@ -1,8 +1,8 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{ - punctuated::Punctuated, spanned::Spanned as _, token::Unsafe, ForeignItemFn, ItemForeignMod, - ReturnType, Signature, Token, + ForeignItemFn, ItemForeignMod, ReturnType, Signature, Token, punctuated::Punctuated, + spanned::Spanned as _, token::Unsafe, }; use crate::prelude::*; diff --git a/crates/macros/src/function.rs b/crates/macros/src/function.rs index d24f61fe61..270a84618c 100644 --- a/crates/macros/src/function.rs +++ b/crates/macros/src/function.rs @@ -218,6 +218,19 @@ impl<'a> Function<'a> { let required_arg_names: Vec<_> = required.iter().map(|arg| arg.name).collect(); let not_required_arg_names: Vec<_> = not_required.iter().map(|arg| arg.name).collect(); + let variadic_bindings = self.args.typed.iter().filter_map(|arg| { + if arg.variadic { + let name = arg.name; + let variadic_name = format_ident!("__variadic_{}", name); + let clean_ty = arg.clean_ty(); + Some(quote! { + let #variadic_name = #name.variadic_vals::<#clean_ty>(); + }) + } else { + None + } + }); + let arg_accessors = self.args.typed.iter().map(|arg| { arg.accessor(|e| { quote! { @@ -237,6 +250,7 @@ impl<'a> Function<'a> { if parse.is_err() { return; } + #(#variadic_bindings)* #ident(#({#arg_accessors}),*) }, @@ -277,6 +291,7 @@ impl<'a> Function<'a> { if parse_result.is_err() { return; } + #(#variadic_bindings)* #call } @@ -332,6 +347,18 @@ impl<'a> Function<'a> { .iter() .map(TypedArg::arg_declaration) .collect::>(); + let variadic_bindings = self.args.typed.iter().filter_map(|arg| { + if arg.variadic { + let name = arg.name; + let variadic_name = format_ident!("__variadic_{}", name); + let clean_ty = arg.clean_ty(); + Some(quote! { + let #variadic_name = #name.variadic_vals::<#clean_ty>(); + }) + } else { + None + } + }); let arg_accessors = self.args.typed.iter().map(|arg| { arg.accessor( |e| quote! { return ::ext_php_rs::class::ConstructorResult::Exception(#e); }, @@ -359,6 +386,7 @@ impl<'a> Function<'a> { if parse.is_err() { return ::ext_php_rs::class::ConstructorResult::ArgError; } + #(#variadic_bindings)* #class::#ident(#({#arg_accessors}),*).into() } inner @@ -535,7 +563,7 @@ impl TypedArg<'_> { let mut ty = self.ty.clone(); ty.drop_lifetimes(); - // Variadic arguments are passed as slices, so we need to extract the + // Variadic arguments are passed as &[&Zval], so we need to extract the // inner type. if self.variadic { let Type::Reference(reference) = &ty else { @@ -599,8 +627,9 @@ impl TypedArg<'_> { #name.val().unwrap_or(#default.into()) } } else if self.variadic { + let variadic_name = format_ident!("__variadic_{}", name); quote! { - &#name.variadic_vals() + #variadic_name.as_slice() } } else if self.nullable { // Originally I thought we could just use the below case for `null` options, as diff --git a/crates/macros/src/impl_.rs b/crates/macros/src/impl_.rs index 353448eb52..771eaa6b2a 100644 --- a/crates/macros/src/impl_.rs +++ b/crates/macros/src/impl_.rs @@ -1,5 +1,5 @@ -use darling::util::Flag; use darling::FromAttributes; +use darling::util::Flag; use proc_macro2::TokenStream; use quote::quote; use std::collections::{HashMap, HashSet}; diff --git a/crates/macros/src/module.rs b/crates/macros/src/module.rs index 8e00418d4b..2318424aa9 100644 --- a/crates/macros/src/module.rs +++ b/crates/macros/src/module.rs @@ -25,13 +25,13 @@ pub fn parser(input: ItemFn) -> Result { Ok(quote! { #[doc(hidden)] - #[no_mangle] + #[unsafe(no_mangle)] extern "C" fn get_module() -> *mut ::ext_php_rs::zend::ModuleEntry { static __EXT_PHP_RS_MODULE_STARTUP: ::ext_php_rs::internal::ModuleStartupMutex = ::ext_php_rs::internal::MODULE_STARTUP_INIT; extern "C" fn ext_php_rs_startup(ty: i32, mod_num: i32) -> i32 { - let a = #startup; + let a = unsafe { #startup }; let b = __EXT_PHP_RS_MODULE_STARTUP .lock() .take() @@ -64,7 +64,7 @@ pub fn parser(input: ItemFn) -> Result { } #[cfg(debug_assertions)] - #[no_mangle] + #[unsafe(no_mangle)] pub extern "C" fn ext_php_rs_describe_module() -> ::ext_php_rs::describe::Description { use ::ext_php_rs::describe::*; diff --git a/crates/macros/src/parsing.rs b/crates/macros/src/parsing.rs index d73349e8fd..e249b51c95 100644 --- a/crates/macros/src/parsing.rs +++ b/crates/macros/src/parsing.rs @@ -1,6 +1,6 @@ use convert_case::{Case, Casing}; use darling::FromMeta; -use quote::{quote, ToTokens}; +use quote::{ToTokens, quote}; const MAGIC_METHOD: [&str; 17] = [ "__construct", diff --git a/crates/macros/src/zval.rs b/crates/macros/src/zval.rs index 7f95ef027e..ddfaf78009 100644 --- a/crates/macros/src/zval.rs +++ b/crates/macros/src/zval.rs @@ -2,8 +2,8 @@ use darling::ToTokens; use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{ - punctuated::Punctuated, token::Where, DataEnum, DataStruct, DeriveInput, GenericParam, - Generics, Ident, ImplGenerics, Lifetime, LifetimeParam, TypeGenerics, Variant, WhereClause, + DataEnum, DataStruct, DeriveInput, GenericParam, Generics, Ident, ImplGenerics, Lifetime, + LifetimeParam, TypeGenerics, Variant, WhereClause, punctuated::Punctuated, token::Where, }; use crate::prelude::*; diff --git a/src/alloc.rs b/src/alloc.rs index 4b27b711da..c52f789cbc 100644 --- a/src/alloc.rs +++ b/src/alloc.rs @@ -6,7 +6,7 @@ use cfg_if::cfg_if; use crate::ffi::{_efree, _emalloc, _estrdup}; use std::{ alloc::Layout, - ffi::{c_char, c_void, CString}, + ffi::{CString, c_char, c_void}, }; /// Uses the PHP memory allocator to allocate request-bound memory. @@ -52,16 +52,20 @@ pub unsafe fn efree(ptr: *mut u8) { cfg_if! { if #[cfg(php_debug)] { #[allow(clippy::used_underscore_items)] - _efree( - ptr.cast::(), - std::ptr::null_mut(), - 0, - std::ptr::null_mut(), - 0, - ); + unsafe { + _efree( + ptr.cast::(), + std::ptr::null_mut(), + 0, + std::ptr::null_mut(), + 0, + ); + } } else { #[allow(clippy::used_underscore_items)] - _efree(ptr.cast::()); + unsafe { + _efree(ptr.cast::()); + } } } } diff --git a/src/args.rs b/src/args.rs index b6bebb5ff1..c110b3b797 100644 --- a/src/args.rs +++ b/src/args.rs @@ -4,7 +4,7 @@ use std::{ffi::CString, ptr}; use crate::{ convert::{FromZvalMut, IntoZvalDyn}, - describe::{abi, Parameter}, + describe::{Parameter, abi}, error::{Error, Result}, ffi::{ _zend_expected_type, _zend_expected_type_Z_EXPECTED_ARRAY, @@ -189,11 +189,7 @@ impl From> for _zend_expected_type { _ => unreachable!(), }; - if arg.allow_null { - type_id + 1 - } else { - type_id - } + if arg.allow_null { type_id + 1 } else { type_id } } } diff --git a/src/boxed.rs b/src/boxed.rs index 5b2e1dac11..e710122e25 100644 --- a/src/boxed.rs +++ b/src/boxed.rs @@ -48,7 +48,7 @@ impl ZBox { /// Caller must ensure that `ptr` is non-null, well-aligned and pointing to /// a `T`. pub unsafe fn from_raw(ptr: *mut T) -> Self { - Self(NonNull::new_unchecked(ptr)) + Self(unsafe { NonNull::new_unchecked(ptr) }) } /// Returns the pointer contained by the box, dropping the box in the diff --git a/src/builders/module.rs b/src/builders/module.rs index 1eea007e50..bdb258f79c 100644 --- a/src/builders/module.rs +++ b/src/builders/module.rs @@ -1,17 +1,17 @@ use std::{convert::TryFrom, ffi::CString, mem, ptr}; use super::{ClassBuilder, FunctionBuilder}; -#[cfg(feature = "enum")] -use crate::{builders::enum_builder::EnumBuilder, enum_::RegisteredEnum}; use crate::{ + PHP_DEBUG, PHP_ZTS, class::RegisteredClass, constant::IntoConst, describe::DocComments, error::Result, - ffi::{ext_php_rs_php_build_id, ZEND_MODULE_API_NO}, + ffi::{ZEND_MODULE_API_NO, ext_php_rs_php_build_id}, zend::{FunctionEntry, ModuleEntry}, - PHP_DEBUG, PHP_ZTS, }; +#[cfg(feature = "enum")] +use crate::{builders::enum_builder::EnumBuilder, enum_::RegisteredEnum}; /// Builds a Zend module extension to be registered with PHP. Must be called /// from within an external function called `get_module`, returning a mutable @@ -24,14 +24,14 @@ use crate::{ /// info_table_start, info_table_end, info_table_row /// }; /// -/// #[no_mangle] +/// #[unsafe(no_mangle)] /// pub extern "C" fn php_module_info(_module: *mut ModuleEntry) { /// info_table_start!(); /// info_table_row!("column 1", "column 2"); /// info_table_end!(); /// } /// -/// #[no_mangle] +/// #[unsafe(no_mangle)] /// pub extern "C" fn get_module() -> *mut ModuleEntry { /// let (entry, _) = ModuleBuilder::new("ext-name", "ext-version") /// .info_function(php_module_info) diff --git a/src/builders/sapi.rs b/src/builders/sapi.rs index ae0cdfecae..85f9cc549d 100644 --- a/src/builders/sapi.rs +++ b/src/builders/sapi.rs @@ -7,7 +7,7 @@ use crate::types::Zval; use crate::{embed::SapiModule, error::Result}; use std::{ - ffi::{c_char, c_int, c_void, CString}, + ffi::{CString, c_char, c_int, c_void}, ptr, }; @@ -22,13 +22,13 @@ use std::{ /// ffi::sapi_header_struct /// }; /// -/// #[no_mangle] +/// #[unsafe(no_mangle)] /// pub extern "C" fn ub_write(str: *const i8, str_length: usize) -> usize { /// println!("PHP wrote: {:?}", str); /// str_length /// } /// -/// #[no_mangle] +/// #[unsafe(no_mangle)] /// pub extern "C" fn send_header(header: *mut sapi_header_struct, server_context: *mut c_void) { /// println!("PHP sent a header: {:?}", header); /// } diff --git a/src/describe/stub.rs b/src/describe/stub.rs index abfdba9d0f..ca3404ee45 100644 --- a/src/describe/stub.rs +++ b/src/describe/stub.rs @@ -9,9 +9,9 @@ use std::{ }; use super::{ - abi::{Option, RString}, Class, Constant, DocBlock, Function, Method, MethodType, Module, Parameter, Property, Visibility, + abi::{Option, RString}, }; #[cfg(feature = "enum")] @@ -350,14 +350,14 @@ impl ToStub for Method { .join(", ") )?; - if !matches!(self.ty, MethodType::Constructor) { - if let Option::Some(retval) = &self.retval { - write!(buf, ": ")?; - if retval.nullable { - write!(buf, "?")?; - } - retval.ty.fmt_stub(buf)?; + if !matches!(self.ty, MethodType::Constructor) + && let Option::Some(retval) = &self.retval + { + write!(buf, ": ")?; + if retval.nullable { + write!(buf, "?")?; } + retval.ty.fmt_stub(buf)?; } writeln!(buf, " {{}}") diff --git a/src/embed/ffi.rs b/src/embed/ffi.rs index f0ad114361..7749ea051d 100644 --- a/src/embed/ffi.rs +++ b/src/embed/ffi.rs @@ -9,7 +9,7 @@ use crate::ffi::php_ini_builder; use std::ffi::{c_char, c_int, c_void}; #[link(name = "wrapper")] -extern "C" { +unsafe extern "C" { pub fn ext_php_rs_embed_callback( argc: c_int, argv: *mut *mut c_char, diff --git a/src/embed/mod.rs b/src/embed/mod.rs index 12c7e6be6a..97df7e0aa0 100644 --- a/src/embed/mod.rs +++ b/src/embed/mod.rs @@ -10,14 +10,14 @@ mod sapi; use crate::boxed::ZBox; use crate::ffi::{ - _zend_file_handle__bindgen_ty_1, php_execute_script, zend_destroy_file_handle, - zend_eval_string, zend_file_handle, zend_stream_init_filename, ZEND_RESULT_CODE_SUCCESS, + _zend_file_handle__bindgen_ty_1, ZEND_RESULT_CODE_SUCCESS, php_execute_script, + zend_destroy_file_handle, zend_eval_string, zend_file_handle, zend_stream_init_filename, }; use crate::types::{ZendObject, Zval}; -use crate::zend::{panic_wrapper, try_catch, ExecutorGlobals}; -use parking_lot::{const_rwlock, RwLock}; -use std::ffi::{c_char, c_void, CString, NulError}; -use std::panic::{resume_unwind, RefUnwindSafe}; +use crate::zend::{ExecutorGlobals, panic_wrapper, try_catch}; +use parking_lot::{RwLock, const_rwlock}; +use std::ffi::{CString, NulError, c_char, c_void}; +use std::panic::{RefUnwindSafe, resume_unwind}; use std::path::Path; use std::ptr::null_mut; diff --git a/src/exception.rs b/src/exception.rs index 482c80825d..915647d766 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -9,7 +9,7 @@ use crate::{ ffi::zend_throw_exception_object, flags::ClassFlags, types::Zval, - zend::{ce, ClassEntry}, + zend::{ClassEntry, ce}, }; /// Result type with the error variant as a [`PhpException`]. diff --git a/src/ffi.rs b/src/ffi.rs index 83817cb48e..87103b72c1 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -12,7 +12,7 @@ pub const ZEND_MM_ALIGNMENT_MASK: isize = -8; // the `#[link(name = "php")]` attribute appended. This will cause the wrapper // functions to fail to link. #[link(name = "wrapper")] -extern "C" { +unsafe extern "C" { pub fn ext_php_rs_zend_string_init( str_: *const c_char, len: usize, diff --git a/src/flags.rs b/src/flags.rs index 179bfc2f8d..fb0c3717eb 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -7,25 +7,25 @@ use crate::ffi::ZEND_ACC_ENUM; #[cfg(not(php82))] use crate::ffi::ZEND_ACC_REUSE_GET_ITERATOR; use crate::ffi::{ - CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, E_COMPILE_ERROR, + _IS_BOOL, CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, E_COMPILE_ERROR, E_COMPILE_WARNING, E_CORE_ERROR, E_CORE_WARNING, E_DEPRECATED, E_ERROR, E_NOTICE, E_PARSE, E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING, E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_INDIRECT, IS_ITERABLE, IS_LONG, IS_MIXED, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, - PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, - ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, - ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, - ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, - ZEND_ACC_HAS_RETURN_TYPE, ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE, - ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, ZEND_ACC_NEARLY_LINKED, - ZEND_ACC_NEVER_CACHE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE, - ZEND_ACC_PROMOTED, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES, - ZEND_ACC_RESOLVED_PARENT, ZEND_ACC_RETURN_REFERENCE, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES, - ZEND_ACC_TOP_LEVEL, ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE, - ZEND_ACC_USES_THIS, ZEND_ACC_USE_GUARDS, ZEND_ACC_VARIADIC, ZEND_EVAL_CODE, - ZEND_HAS_STATIC_IN_METHODS, ZEND_INTERNAL_FUNCTION, ZEND_USER_FUNCTION, Z_TYPE_FLAGS_SHIFT, - _IS_BOOL, + PHP_INI_PERDIR, PHP_INI_SYSTEM, PHP_INI_USER, Z_TYPE_FLAGS_SHIFT, ZEND_ACC_ABSTRACT, + ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, + ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, + ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, + ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_HAS_RETURN_TYPE, ZEND_ACC_HAS_TYPE_HINTS, + ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE, ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, + ZEND_ACC_INTERFACE, ZEND_ACC_LINKED, ZEND_ACC_NEARLY_LINKED, ZEND_ACC_NEVER_CACHE, + ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PRELOADED, ZEND_ACC_PRIVATE, ZEND_ACC_PROMOTED, + ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES, ZEND_ACC_RESOLVED_PARENT, + ZEND_ACC_RETURN_REFERENCE, ZEND_ACC_STATIC, ZEND_ACC_STRICT_TYPES, ZEND_ACC_TOP_LEVEL, + ZEND_ACC_TRAIT, ZEND_ACC_TRAIT_CLONE, ZEND_ACC_UNRESOLVED_VARIANCE, ZEND_ACC_USE_GUARDS, + ZEND_ACC_USES_THIS, ZEND_ACC_VARIADIC, ZEND_EVAL_CODE, ZEND_HAS_STATIC_IN_METHODS, + ZEND_INTERNAL_FUNCTION, ZEND_USER_FUNCTION, }; use std::{convert::TryFrom, fmt::Display}; diff --git a/src/internal/mod.rs b/src/internal/mod.rs index bceae5a811..24c25ef7db 100644 --- a/src/internal/mod.rs +++ b/src/internal/mod.rs @@ -1,5 +1,5 @@ //! Internal, public functions that are called from downstream extensions. -use parking_lot::{const_mutex, Mutex}; +use parking_lot::{Mutex, const_mutex}; use crate::builders::ModuleStartup; diff --git a/src/lib.rs b/src/lib.rs index 4b51f64137..0ee026fed6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +#![allow(unsafe_attr_outside_unsafe)] #![warn(clippy::pedantic)] #![cfg_attr(docs, feature(doc_cfg))] #![cfg_attr(windows, feature(abi_vectorcall))] @@ -55,8 +56,8 @@ pub mod prelude { pub use crate::php_println; pub use crate::types::ZendCallable; pub use crate::{ - php_class, php_const, php_extern, php_function, php_impl, php_module, wrap_constant, - wrap_function, zend_fastcall, ZvalConvert, + ZvalConvert, php_class, php_const, php_extern, php_function, php_impl, php_module, + wrap_constant, wrap_function, zend_fastcall, }; } @@ -72,6 +73,6 @@ pub const PHP_ZTS: bool = cfg!(php_zts); #[cfg(feature = "enum")] pub use ext_php_rs_derive::php_enum; pub use ext_php_rs_derive::{ - php_class, php_const, php_extern, php_function, php_impl, php_module, wrap_constant, - wrap_function, zend_fastcall, ZvalConvert, + ZvalConvert, php_class, php_const, php_extern, php_function, php_impl, php_module, + wrap_constant, wrap_function, zend_fastcall, }; diff --git a/src/types/array/iterators.rs b/src/types/array/iterators.rs index 8bdcd11aac..4a53fce720 100644 --- a/src/types/array/iterators.rs +++ b/src/types/array/iterators.rs @@ -9,9 +9,8 @@ use crate::boxed::ZBox; use crate::{ convert::FromZval, ffi::{ - zend_hash_get_current_data_ex, zend_hash_get_current_key_type_ex, + HashPosition, zend_hash_get_current_data_ex, zend_hash_get_current_key_type_ex, zend_hash_get_current_key_zval_ex, zend_hash_move_backwards_ex, zend_hash_move_forward_ex, - HashPosition, }, types::Zval, }; diff --git a/src/types/array/mod.rs b/src/types/array/mod.rs index 88c6142c2e..d19bcdebbd 100644 --- a/src/types/array/mod.rs +++ b/src/types/array/mod.rs @@ -9,10 +9,9 @@ use crate::{ error::Result, ffi::zend_ulong, ffi::{ - _zend_new_array, zend_array_count, zend_array_destroy, zend_array_dup, zend_hash_clean, - zend_hash_index_del, zend_hash_index_find, zend_hash_index_update, + _zend_new_array, HT_MIN_SIZE, zend_array_count, zend_array_destroy, zend_array_dup, + zend_hash_clean, zend_hash_index_del, zend_hash_index_find, zend_hash_index_update, zend_hash_next_index_insert, zend_hash_str_del, zend_hash_str_find, zend_hash_str_update, - HT_MIN_SIZE, }, flags::DataType, types::Zval, @@ -367,11 +366,7 @@ impl ZendHashTable { }, }; - if result < 0 { - None - } else { - Some(()) - } + if result < 0 { None } else { Some(()) } } /// Attempts to remove a value from the hash table with a string key. @@ -404,11 +399,7 @@ impl ZendHashTable { zend_hash_index_del(self, key as zend_ulong) }; - if result < 0 { - None - } else { - Some(()) - } + if result < 0 { None } else { Some(()) } } /// Attempts to insert an item into the hash table, or update if the key diff --git a/src/types/class_object.rs b/src/types/class_object.rs index a99778e36e..cad16c579e 100644 --- a/src/types/class_object.rs +++ b/src/types/class_object.rs @@ -72,7 +72,7 @@ impl ZendClassObject { /// /// Panics if memory was unable to be allocated for the new object. pub unsafe fn new_uninit(ce: Option<&'static ClassEntry>) -> ZBox { - Self::internal_new(None, ce) + unsafe { Self::internal_new(None, ce) } } /// Creates a new [`ZendObject`] of type `T`, storing the given (and @@ -110,21 +110,23 @@ impl ZendClassObject { let size = mem::size_of::>(); let meta = T::get_metadata(); let ce = ptr::from_ref(ce.unwrap_or_else(|| meta.ce())).cast_mut(); - let obj = ext_php_rs_zend_object_alloc(size as _, ce).cast::>(); - let obj = obj - .as_mut() - .expect("Failed to allocate for new Zend object"); + let obj = + unsafe { ext_php_rs_zend_object_alloc(size as _, ce).cast::>() }; + let obj = unsafe { + obj.as_mut() + .expect("Failed to allocate for new Zend object") + }; - zend_object_std_init(&raw mut obj.std, ce); - object_properties_init(&raw mut obj.std, ce); + unsafe { zend_object_std_init(&raw mut obj.std, ce) }; + unsafe { object_properties_init(&raw mut obj.std, ce) }; // SAFETY: `obj` is non-null and well aligned as it is a reference. // As the data in `obj.obj` is uninitialized, we don't want to drop // the data, but directly override it. - ptr::write(&raw mut obj.obj, val); + unsafe { ptr::write(&raw mut obj.obj, val) }; obj.std.handlers = meta.handlers(); - ZBox::from_raw(obj) + unsafe { ZBox::from_raw(obj) } } /// Initializes the class object with the value `val`. diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 557108f504..586b231656 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -1,5 +1,5 @@ use crate::convert::FromZvalMut; -use crate::ffi::{zend_object_iterator, ZEND_RESULT_CODE_SUCCESS}; +use crate::ffi::{ZEND_RESULT_CODE_SUCCESS, zend_object_iterator}; use crate::flags::DataType; use crate::types::Zval; use crate::zend::ExecutorGlobals; diff --git a/src/types/object.rs b/src/types/object.rs index 10b8d04106..d305dfb348 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -9,14 +9,14 @@ use crate::{ convert::{FromZendObject, FromZval, FromZvalMut, IntoZval, IntoZvalDyn}, error::{Error, Result}, ffi::{ + HashTable, ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, ZEND_PROPERTY_ISSET, ext_php_rs_zend_object_release, object_properties_init, zend_call_known_function, - zend_function, zend_hash_str_find_ptr_lc, zend_object, zend_objects_new, HashTable, - ZEND_ISEMPTY, ZEND_PROPERTY_EXISTS, ZEND_PROPERTY_ISSET, + zend_function, zend_hash_str_find_ptr_lc, zend_object, zend_objects_new, }, flags::DataType, rc::PhpRc, types::{ZendClassObject, ZendStr, Zval}, - zend::{ce, ClassEntry, ExecutorGlobals, ZendObjectHandlers}, + zend::{ClassEntry, ExecutorGlobals, ZendObjectHandlers, ce}, }; /// A PHP object. @@ -354,7 +354,7 @@ impl ZendObject { /// Attempts to retrieve a reference to the object handlers. #[inline] unsafe fn handlers(&self) -> Result<&ZendObjectHandlers> { - self.handlers.as_ref().ok_or(Error::InvalidScope) + unsafe { self.handlers.as_ref() }.ok_or(Error::InvalidScope) } /// Returns a mutable pointer to `self`, regardless of the type of diff --git a/src/types/string.rs b/src/types/string.rs index 5b54cb6f5c..9d8bb06579 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -9,7 +9,7 @@ use std::{ ptr, slice, }; -use parking_lot::{const_mutex, Mutex}; +use parking_lot::{Mutex, const_mutex}; use crate::{ boxed::{ZBox, ZBoxable}, diff --git a/src/types/zval.rs b/src/types/zval.rs index ba08f9ffd4..1b3e9edca6 100644 --- a/src/types/zval.rs +++ b/src/types/zval.rs @@ -4,8 +4,8 @@ use std::{convert::TryInto, ffi::c_void, fmt::Debug, ptr}; -use crate::types::iterable::Iterable; use crate::types::ZendIterator; +use crate::types::iterable::Iterable; use crate::{ binary::Pack, binary_slice::PackSlice, @@ -338,7 +338,7 @@ impl Zval { #[must_use] pub unsafe fn ptr(&self) -> Option<*mut T> { if self.is_ptr() { - Some(self.value.ptr.cast::()) + Some(unsafe { self.value.ptr.cast::() }) } else { None } diff --git a/src/util/cstring_scope.rs b/src/util/cstring_scope.rs index 0c175457d9..227460aaf2 100644 --- a/src/util/cstring_scope.rs +++ b/src/util/cstring_scope.rs @@ -1,5 +1,5 @@ use std::{ - ffi::{c_char, CString, NulError}, + ffi::{CString, NulError, c_char}, ops::Deref, }; diff --git a/src/zend/_type.rs b/src/zend/_type.rs index a683385cc0..9dfa5c686b 100644 --- a/src/zend/_type.rs +++ b/src/zend/_type.rs @@ -2,8 +2,8 @@ use std::{ffi::c_void, ptr}; use crate::{ ffi::{ - zend_type, IS_MIXED, MAY_BE_ANY, MAY_BE_BOOL, _IS_BOOL, _ZEND_IS_VARIADIC_BIT, - _ZEND_SEND_MODE_SHIFT, _ZEND_TYPE_NULLABLE_BIT, + _IS_BOOL, _ZEND_IS_VARIADIC_BIT, _ZEND_SEND_MODE_SHIFT, _ZEND_TYPE_NULLABLE_BIT, IS_MIXED, + MAY_BE_ANY, MAY_BE_BOOL, zend_type, }, flags::DataType, }; diff --git a/src/zend/ex.rs b/src/zend/ex.rs index c57f34e668..4c66b5b123 100644 --- a/src/zend/ex.rs +++ b/src/zend/ex.rs @@ -1,4 +1,4 @@ -use crate::ffi::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK}; +use crate::ffi::{ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK, zend_execute_data}; use crate::{ args::ArgParser, @@ -28,7 +28,7 @@ impl ExecuteData { /// ```no_run /// use ext_php_rs::{types::Zval, zend::ExecuteData, args::Arg, flags::DataType}; /// - /// #[no_mangle] + /// #[unsafe(no_mangle)] /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { /// let mut a = Arg::new("a", DataType::Long); /// @@ -59,7 +59,7 @@ impl ExecuteData { /// ```no_run /// use ext_php_rs::{types::Zval, zend::ExecuteData, args::Arg, flags::DataType}; /// - /// #[no_mangle] + /// #[unsafe(no_mangle)] /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { /// let mut a = Arg::new("a", DataType::Long); /// @@ -113,7 +113,7 @@ impl ExecuteData { /// #[derive(Debug)] /// struct Example; /// - /// #[no_mangle] + /// #[unsafe(no_mangle)] /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { /// let mut a = Arg::new("a", DataType::Long); /// @@ -161,7 +161,7 @@ impl ExecuteData { /// #[derive(Debug)] /// struct Example; /// - /// #[no_mangle] + /// #[unsafe(no_mangle)] /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { /// let this = ex.get_object::(); /// dbg!(this); @@ -184,7 +184,7 @@ impl ExecuteData { /// ```no_run /// use ext_php_rs::{types::Zval, zend::ExecuteData}; /// - /// #[no_mangle] + /// #[unsafe(no_mangle)] /// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) { /// let this = ex.get_self(); /// dbg!(this); @@ -219,8 +219,8 @@ impl ExecuteData { #[doc(hidden)] unsafe fn zend_call_arg<'a>(&self, n: usize) -> Option<&'a mut Zval> { let n = isize::try_from(n).expect("n is too large"); - let ptr = self.zend_call_var_num(n); - ptr.as_mut() + let ptr = unsafe { self.zend_call_var_num(n) }; + unsafe { ptr.as_mut() } } /// Translation of macro `ZEND_CALL_VAR_NUM(call, n)` @@ -228,7 +228,7 @@ impl ExecuteData { #[doc(hidden)] unsafe fn zend_call_var_num(&self, n: isize) -> *mut Zval { let ptr = std::ptr::from_ref(self) as *mut Zval; - ptr.offset(Self::zend_call_frame_slot() + n) + unsafe { ptr.offset(Self::zend_call_frame_slot() + n) } } /// Translation of macro `ZEND_CALL_FRAME_SLOT` diff --git a/src/zend/globals.rs b/src/zend/globals.rs index 329d2e613f..b75e160ae6 100644 --- a/src/zend/globals.rs +++ b/src/zend/globals.rs @@ -13,12 +13,12 @@ use crate::exception::PhpResult; #[cfg(php82)] use crate::ffi::zend_atomic_bool_store; use crate::ffi::{ - _sapi_module_struct, _zend_compiler_globals, _zend_executor_globals, + _sapi_module_struct, _zend_compiler_globals, _zend_executor_globals, TRACK_VARS_COOKIE, + TRACK_VARS_ENV, TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, TRACK_VARS_SERVER, ext_php_rs_compiler_globals, ext_php_rs_executor_globals, ext_php_rs_file_globals, ext_php_rs_process_globals, ext_php_rs_sapi_globals, ext_php_rs_sapi_module, php_core_globals, php_file_globals, sapi_globals_struct, sapi_header_struct, sapi_headers_struct, - sapi_request_info, zend_ini_entry, zend_is_auto_global, TRACK_VARS_COOKIE, TRACK_VARS_ENV, - TRACK_VARS_FILES, TRACK_VARS_GET, TRACK_VARS_POST, TRACK_VARS_SERVER, + sapi_request_info, zend_ini_entry, zend_is_auto_global, }; #[cfg(not(php81))] use crate::ffi::{_zend_hash_find_known_hash, _zend_string}; @@ -848,7 +848,7 @@ pub(crate) mod lock { /// this is only effective on the Rust side. #[cfg(php_zts)] pub(crate) mod lock { - use parking_lot::{const_rwlock, RwLock}; + use parking_lot::{RwLock, const_rwlock}; use std::sync::Arc; thread_local! { diff --git a/src/zend/handlers.rs b/src/zend/handlers.rs index 40a488344e..6f4f680cd1 100644 --- a/src/zend/handlers.rs +++ b/src/zend/handlers.rs @@ -46,26 +46,28 @@ impl ZendObjectHandlers { /// /// * If the offset of the `T` type is not a valid `i32` value. pub unsafe fn init(ptr: *mut ZendObjectHandlers) { - ptr::copy_nonoverlapping(&raw const std_object_handlers, ptr, 1); + unsafe { ptr::copy_nonoverlapping(&raw const std_object_handlers, ptr, 1) }; let offset = ZendClassObject::::std_offset(); - (*ptr).offset = offset.try_into().expect("Invalid offset"); - (*ptr).free_obj = Some(Self::free_obj::); - (*ptr).read_property = Some(Self::read_property::); - (*ptr).write_property = Some(Self::write_property::); - (*ptr).get_properties = Some(Self::get_properties::); - (*ptr).has_property = Some(Self::has_property::); + unsafe { (*ptr).offset = offset.try_into().expect("Invalid offset") }; + unsafe { (*ptr).free_obj = Some(Self::free_obj::) }; + unsafe { (*ptr).read_property = Some(Self::read_property::) }; + unsafe { (*ptr).write_property = Some(Self::write_property::) }; + unsafe { (*ptr).get_properties = Some(Self::get_properties::) }; + unsafe { (*ptr).has_property = Some(Self::has_property::) }; } unsafe extern "C" fn free_obj(object: *mut ZendObject) { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .expect("Invalid object pointer given for `free_obj`"); + let obj = unsafe { + object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .expect("Invalid object pointer given for `free_obj`") + }; // Manually drop the object as we don't want to free the underlying memory. - ptr::drop_in_place(&raw mut obj.obj); + unsafe { ptr::drop_in_place(&raw mut obj.obj) }; - zend_object_std_dtor(object); + unsafe { zend_object_std_dtor(object) }; } unsafe extern "C" fn read_property( @@ -85,19 +87,23 @@ impl ZendObjectHandlers { cache_slot: *mut *mut c_void, rv: *mut Zval, ) -> PhpResult<*mut Zval> { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .ok_or("Invalid object pointer given")?; - let prop_name = member - .as_ref() - .ok_or("Invalid property name pointer given")?; + let obj = unsafe { + object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .ok_or("Invalid object pointer given")? + }; + let prop_name = unsafe { + member + .as_ref() + .ok_or("Invalid property name pointer given")? + }; let self_ = &mut *obj; let props = T::get_metadata().get_properties(); let prop = props.get(prop_name.as_str()?); // retval needs to be treated as initialized, so we set the type to null - let rv_mut = rv.as_mut().ok_or("Invalid return zval given")?; + let rv_mut = unsafe { rv.as_mut().ok_or("Invalid return zval given")? }; rv_mut.u1.type_info = ZvalTypeFlags::Null.bits(); Ok(match prop { @@ -105,15 +111,15 @@ impl ZendObjectHandlers { prop_info.prop.get(self_, rv_mut)?; rv } - None => zend_std_read_property(object, member, type_, cache_slot, rv), + None => unsafe { zend_std_read_property(object, member, type_, cache_slot, rv) }, }) } - match internal::(object, member, type_, cache_slot, rv) { + match unsafe { internal::(object, member, type_, cache_slot, rv) } { Ok(rv) => rv, Err(e) => { let _ = e.throw(); - (*rv).set_null(); + unsafe { (*rv).set_null() }; rv } } @@ -134,28 +140,32 @@ impl ZendObjectHandlers { value: *mut Zval, cache_slot: *mut *mut c_void, ) -> PhpResult<*mut Zval> { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .ok_or("Invalid object pointer given")?; - let prop_name = member - .as_ref() - .ok_or("Invalid property name pointer given")?; + let obj = unsafe { + object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .ok_or("Invalid object pointer given")? + }; + let prop_name = unsafe { + member + .as_ref() + .ok_or("Invalid property name pointer given")? + }; let self_ = &mut *obj; let props = T::get_metadata().get_properties(); let prop = props.get(prop_name.as_str()?); - let value_mut = value.as_mut().ok_or("Invalid return zval given")?; + let value_mut = unsafe { value.as_mut().ok_or("Invalid return zval given")? }; Ok(match prop { Some(prop_info) => { prop_info.prop.set(self_, value_mut)?; value } - None => zend_std_write_property(object, member, value, cache_slot), + None => unsafe { zend_std_write_property(object, member, value, cache_slot) }, }) } - match internal::(object, member, value, cache_slot) { + match unsafe { internal::(object, member, value, cache_slot) } { Ok(rv) => rv, Err(e) => { let _ = e.throw(); @@ -174,10 +184,12 @@ impl ZendObjectHandlers { object: *mut ZendObject, props: &mut ZendHashTable, ) -> PhpResult { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .ok_or("Invalid object pointer given")?; + let obj = unsafe { + object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .ok_or("Invalid object pointer given")? + }; let self_ = &mut *obj; let struct_props = T::get_metadata().get_properties(); @@ -194,12 +206,14 @@ impl ZendObjectHandlers { Ok(()) } - let props = zend_std_get_properties(object) - .as_mut() - .or_else(|| Some(ZendHashTable::new().into_raw())) - .expect("Failed to get property hashtable"); + let props = unsafe { + zend_std_get_properties(object) + .as_mut() + .or_else(|| Some(ZendHashTable::new().into_raw())) + .expect("Failed to get property hashtable") + }; - if let Err(e) = internal::(object, props) { + if let Err(e) = unsafe { internal::(object, props) } { let _ = e.throw(); } @@ -221,13 +235,17 @@ impl ZendObjectHandlers { has_set_exists: c_int, cache_slot: *mut *mut c_void, ) -> PhpResult { - let obj = object - .as_mut() - .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) - .ok_or("Invalid object pointer given")?; - let prop_name = member - .as_ref() - .ok_or("Invalid property name pointer given")?; + let obj = unsafe { + object + .as_mut() + .and_then(|obj| ZendClassObject::::from_zend_obj_mut(obj)) + .ok_or("Invalid object pointer given")? + }; + let prop_name = unsafe { + member + .as_ref() + .ok_or("Invalid property name pointer given")? + }; let props = T::get_metadata().get_properties(); let prop = props.get(prop_name.as_str()?); let self_ = &mut *obj; @@ -254,12 +272,12 @@ impl ZendObjectHandlers { cfg_if::cfg_if! { if #[cfg(php84)] { #[allow(clippy::unnecessary_mut_passed)] - if zend_is_true(&raw mut zv) { + if unsafe { zend_is_true(&raw mut zv) } { return Ok(1); } } else { #[allow(clippy::unnecessary_mut_passed)] - if zend_is_true(&raw mut zv) == 1 { + if unsafe { zend_is_true(&raw mut zv) } == 1 { return Ok(1); } } @@ -279,15 +297,10 @@ impl ZendObjectHandlers { ), } - Ok(zend_std_has_property( - object, - member, - has_set_exists, - cache_slot, - )) + Ok(unsafe { zend_std_has_property(object, member, has_set_exists, cache_slot) }) } - match internal::(object, member, has_set_exists, cache_slot) { + match unsafe { internal::(object, member, has_set_exists, cache_slot) } { Ok(rv) => rv, Err(e) => { let _ = e.throw(); diff --git a/src/zend/mod.rs b/src/zend/mod.rs index f43c14ce35..02d44b67ef 100644 --- a/src/zend/mod.rs +++ b/src/zend/mod.rs @@ -38,7 +38,7 @@ pub use module::ModuleEntry; pub use streams::*; #[cfg(feature = "embed")] pub(crate) use try_catch::panic_wrapper; -pub use try_catch::{bailout, try_catch, try_catch_first, CatchError}; +pub use try_catch::{CatchError, bailout, try_catch, try_catch_first}; // Used as the format string for `php_printf`. const FORMAT_STR: &[u8] = b"%s\0"; diff --git a/src/zend/try_catch.rs b/src/zend/try_catch.rs index 3e2bc1afe8..ec2e951399 100644 --- a/src/zend/try_catch.rs +++ b/src/zend/try_catch.rs @@ -2,7 +2,7 @@ use crate::ffi::{ ext_php_rs_zend_bailout, ext_php_rs_zend_first_try_catch, ext_php_rs_zend_try_catch, }; use std::ffi::c_void; -use std::panic::{catch_unwind, resume_unwind, RefUnwindSafe}; +use std::panic::{RefUnwindSafe, catch_unwind, resume_unwind}; use std::ptr::null_mut; /// Error returned when a bailout occurs @@ -14,7 +14,7 @@ pub(crate) unsafe extern "C" fn panic_wrapper R + RefUnwindSafe ) -> *const c_void { // we try to catch panic here so we correctly shutdown php if it happens // mandatory when we do assert on test as other test would not run correctly - let panic = catch_unwind(|| (*(ctx as *mut F))()); + let panic = catch_unwind(|| unsafe { (*(ctx as *mut F))() }); Box::into_raw(Box::new(panic)).cast::() } @@ -106,7 +106,7 @@ fn do_try_catch R + RefUnwindSafe>(func: F, first: bool) -> Res /// When using this function you should ensure that all the memory allocated in /// the current scope is released pub unsafe fn bailout() -> ! { - ext_php_rs_zend_bailout(); + unsafe { ext_php_rs_zend_bailout() }; } #[cfg(feature = "embed")] diff --git a/tests/sapi.rs b/tests/sapi.rs index d74accb894..e6477c4dce 100644 --- a/tests/sapi.rs +++ b/tests/sapi.rs @@ -9,10 +9,10 @@ extern crate ext_php_rs; use ext_php_rs::builders::SapiBuilder; -use ext_php_rs::embed::{ext_php_rs_sapi_startup, Embed}; +use ext_php_rs::embed::{Embed, ext_php_rs_sapi_startup}; use ext_php_rs::ffi::{ - php_module_shutdown, php_module_startup, php_request_shutdown, php_request_startup, - sapi_shutdown, sapi_startup, ZEND_RESULT_CODE_SUCCESS, + ZEND_RESULT_CODE_SUCCESS, php_module_shutdown, php_module_startup, php_request_shutdown, + php_request_startup, sapi_shutdown, sapi_startup, }; use ext_php_rs::prelude::*; use ext_php_rs::zend::try_catch_first; diff --git a/tests/src/integration/variadic_args/mod.rs b/tests/src/integration/variadic_args/mod.rs index 2bc58877ea..a588da24ef 100644 --- a/tests/src/integration/variadic_args/mod.rs +++ b/tests/src/integration/variadic_args/mod.rs @@ -17,10 +17,87 @@ pub fn test_variadic_add_required(number: u32, numbers: &[&Zval]) -> u32 { .sum::() } +#[php_function] +pub fn test_variadic_count(items: &[&Zval]) -> usize { + items.len() +} + +#[php_function] +pub fn test_variadic_types(values: &[&Zval]) -> Vec { + values + .iter() + .map(|v| { + if v.is_long() { + "long".to_string() + } else if v.is_string() { + "string".to_string() + } else if v.is_double() { + "double".to_string() + } else if v.is_true() || v.is_false() { + "bool".to_string() + } else if v.is_array() { + "array".to_string() + } else if v.is_object() { + "object".to_string() + } else if v.is_null() { + "null".to_string() + } else { + "unknown".to_string() + } + }) + .collect() +} + +#[php_function] +pub fn test_variadic_strings(prefix: String, suffixes: &[&Zval]) -> Vec { + suffixes + .iter() + .filter_map(|v| v.str()) + .map(|s| format!("{prefix}{s}")) + .collect() +} + +#[php_function] +pub fn test_variadic_sum_all(nums: &[&Zval]) -> i64 { + nums.iter().filter_map(|v| v.long()).sum() +} + +#[php_function] +pub fn test_variadic_optional(required: String, optional: Option, extras: &[&Zval]) -> String { + let opt_str = optional.map_or_else(|| "none".to_string(), |v| v.to_string()); + format!("{required}-{opt_str}-{}", extras.len()) +} + +#[php_function] +pub fn test_variadic_empty_check(items: &[&Zval]) -> bool { + items.is_empty() +} + +#[php_function] +pub fn test_variadic_first_last(items: &[&Zval]) -> Vec { + let mut result = Vec::new(); + if let Some(first) = items.first() { + result.push(first.shallow_clone()); + } + if let Some(last) = items.last() { + if items.len() > 1 { + result.push(last.shallow_clone()); + } + } + result +} + pub fn build_module(builder: ModuleBuilder) -> ModuleBuilder { builder .function(wrap_function!(test_variadic_args)) .function(wrap_function!(test_variadic_add_required)) + .function(wrap_function!(test_variadic_count)) + .function(wrap_function!(test_variadic_types)) + .function(wrap_function!(test_variadic_strings)) + .function(wrap_function!(test_variadic_sum_all)) + .function(wrap_function!(test_variadic_optional)) + .function(wrap_function!(test_variadic_empty_check)) + .function(wrap_function!(test_variadic_first_last)) } #[cfg(test)] diff --git a/tests/src/integration/variadic_args/variadic_args.php b/tests/src/integration/variadic_args/variadic_args.php index 98a9c812c0..6692ffbae0 100644 --- a/tests/src/integration/variadic_args/variadic_args.php +++ b/tests/src/integration/variadic_args/variadic_args.php @@ -34,3 +34,60 @@ assert(gettype(end($types[2])) === 'double', 'Type of argument 2 and its last element should be a float of 0.01'); assert($types[3], 'Arg 4 should be boolean true'); assert($types[4] instanceof stdClass, 'Last argument is an instance of an StdClass'); + +// Test variadic count +assert(test_variadic_count() === 0, 'Empty variadic should return 0'); +assert(test_variadic_count(1) === 1, 'Single arg should return 1'); +assert(test_variadic_count(1, 2, 3, 4, 5) === 5, 'Five args should return 5'); +assert(test_variadic_count(...range(1, 100)) === 100, 'Spread of 100 items should return 100'); + +// Test variadic types detection +$type_result = test_variadic_types(42, "hello", 3.14, true, [1, 2], new stdClass, null); +assert($type_result === ['long', 'string', 'double', 'bool', 'array', 'object', 'null'], 'Should detect all types correctly'); + +$type_result = test_variadic_types(); +assert($type_result === [], 'Empty variadic should return empty array'); + +// Test variadic with string operations +$prefixed = test_variadic_strings("pre_", "a", "b", "c"); +assert($prefixed === ['pre_a', 'pre_b', 'pre_c'], 'Should prefix all strings'); + +$prefixed = test_variadic_strings("x_"); +assert($prefixed === [], 'No suffixes should return empty'); + +$prefixed = test_variadic_strings("test_", "one", 123, "two"); +assert(count($prefixed) === 2, 'Should only process strings, filtering out non-strings'); + +// Test variadic sum +assert(test_variadic_sum_all() === 0, 'Empty sum should be 0'); +assert(test_variadic_sum_all(1, 2, 3, 4, 5) === 15, 'Sum 1-5 should be 15'); +assert(test_variadic_sum_all(10, 20, 30) === 60, 'Sum should be 60'); +assert(test_variadic_sum_all(-5, 5) === 0, 'Negative and positive should cancel'); +assert(test_variadic_sum_all(100, "not a number", 200) === 300, 'Should skip non-numeric values'); + +// Test variadic with optional parameter +assert(test_variadic_optional("req", null) === "req-none-0", 'Optional null, no extras'); +assert(test_variadic_optional("req", 42) === "req-42-0", 'Optional provided, no extras'); +assert(test_variadic_optional("req", 42, "a", "b") === "req-42-2", 'Optional and extras provided'); +assert(test_variadic_optional("req", null, "x") === "req-none-1", 'Null optional, one extra'); + +// Test variadic empty check +assert(test_variadic_empty_check() === true, 'No args should be empty'); +assert(test_variadic_empty_check(1) === false, 'One arg should not be empty'); +assert(test_variadic_empty_check(1, 2, 3) === false, 'Multiple args should not be empty'); + +// Test variadic first/last +$fl = test_variadic_first_last(); +assert($fl === [], 'Empty should return empty array'); + +$fl = test_variadic_first_last(1); +assert($fl === [1], 'Single item should return just that item'); + +$fl = test_variadic_first_last(1, 2); +assert($fl === [1, 2], 'Two items should return first and last'); + +$fl = test_variadic_first_last(1, 2, 3, 4, 5); +assert($fl === [1, 5], 'Multiple items should return first and last'); + +$fl = test_variadic_first_last("start", true, 3.14, ["middle"], "end"); +assert($fl[0] === "start" && $fl[1] === "end", 'Should preserve types for first and last'); diff --git a/unix_build.rs b/unix_build.rs index b45c183963..731846105e 100644 --- a/unix_build.rs +++ b/unix_build.rs @@ -1,8 +1,8 @@ use std::{path::PathBuf, process::Command}; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; -use crate::{find_executable, path_from_env, PHPInfo, PHPProvider}; +use crate::{PHPInfo, PHPProvider, find_executable, path_from_env}; pub struct Provider<'a> { info: &'a PHPInfo, diff --git a/windows_build.rs b/windows_build.rs index 72a64cd618..5c27e39bf7 100644 --- a/windows_build.rs +++ b/windows_build.rs @@ -1,4 +1,4 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use std::{ convert::TryFrom, fmt::Display, @@ -38,7 +38,9 @@ impl<'a> PHPProvider<'a> for Provider<'a> { let devel = DevelPack::new(version, is_zts, arch)?; if let Ok(linker) = get_rustc_linker() { if looks_like_msvc_linker(&linker) { - println!("cargo:warning=It looks like you are using a MSVC linker. You may encounter issues when attempting to load your compiled extension into PHP if your MSVC linker version is not compatible with the linker used to compile your PHP. It is recommended to use `rust-lld` as your linker."); + println!( + "cargo:warning=It looks like you are using a MSVC linker. You may encounter issues when attempting to load your compiled extension into PHP if your MSVC linker version is not compatible with the linker used to compile your PHP. It is recommended to use `rust-lld` as your linker." + ); } }