Merge pull request #225 from davidcole1340/ini-definitions

Support registering ini definitions for modules
This commit is contained in:
Daniil Gentili 2023-10-10 15:32:00 +02:00 committed by GitHub
commit eff7b0195f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 195 additions and 6 deletions

View File

@ -101,6 +101,8 @@ bind! {
zend_objects_clone_members, zend_objects_clone_members,
zend_register_bool_constant, zend_register_bool_constant,
zend_register_double_constant, zend_register_double_constant,
zend_register_ini_entries,
zend_ini_entry_def,
zend_register_internal_class_ex, zend_register_internal_class_ex,
zend_register_long_constant, zend_register_long_constant,
zend_register_string_constant, zend_register_string_constant,
@ -159,6 +161,10 @@ bind! {
IS_PTR, IS_PTR,
MAY_BE_ANY, MAY_BE_ANY,
MAY_BE_BOOL, MAY_BE_BOOL,
PHP_INI_USER,
PHP_INI_PERDIR,
PHP_INI_SYSTEM,
PHP_INI_ALL,
USING_ZTS, USING_ZTS,
ZEND_ACC_ABSTRACT, ZEND_ACC_ABSTRACT,
ZEND_ACC_ANON_CLASS, ZEND_ACC_ANON_CLASS,

View File

@ -101,6 +101,10 @@ pub const ZEND_MODULE_API_NO: u32 = 20220829;
pub const USING_ZTS: u32 = 0; pub const USING_ZTS: u32 = 0;
pub const MAY_BE_BOOL: u32 = 12; pub const MAY_BE_BOOL: u32 = 12;
pub const MAY_BE_ANY: u32 = 1022; pub const MAY_BE_ANY: u32 = 1022;
pub const PHP_INI_USER: u32 = 1;
pub const PHP_INI_PERDIR: u32 = 2;
pub const PHP_INI_SYSTEM: u32 = 4;
pub const PHP_INI_ALL: u32 = 7;
pub const CONST_CS: u32 = 0; pub const CONST_CS: u32 = 0;
pub const CONST_PERSISTENT: u32 = 1; pub const CONST_PERSISTENT: u32 = 1;
pub const CONST_NO_FILE_CACHE: u32 = 2; pub const CONST_NO_FILE_CACHE: u32 = 2;
@ -1402,6 +1406,32 @@ extern "C" {
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct _zend_ini_entry_def {
pub name: *const ::std::os::raw::c_char,
pub on_modify: ::std::option::Option<
unsafe extern "C" fn(
entry: *mut zend_ini_entry,
new_value: *mut zend_string,
mh_arg1: *mut ::std::os::raw::c_void,
mh_arg2: *mut ::std::os::raw::c_void,
mh_arg3: *mut ::std::os::raw::c_void,
stage: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub mh_arg1: *mut ::std::os::raw::c_void,
pub mh_arg2: *mut ::std::os::raw::c_void,
pub mh_arg3: *mut ::std::os::raw::c_void,
pub value: *const ::std::os::raw::c_char,
pub displayer: ::std::option::Option<
unsafe extern "C" fn(ini_entry: *mut zend_ini_entry, type_: ::std::os::raw::c_int),
>,
pub value_length: u32,
pub name_length: u16,
pub modifiable: u8,
}
pub type zend_ini_entry_def = _zend_ini_entry_def;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct _zend_ini_entry { pub struct _zend_ini_entry {
pub name: *mut zend_string, pub name: *mut zend_string,
pub on_modify: ::std::option::Option< pub on_modify: ::std::option::Option<
@ -1427,6 +1457,12 @@ pub struct _zend_ini_entry {
pub orig_modifiable: u8, pub orig_modifiable: u8,
pub modified: u8, pub modified: u8,
} }
extern "C" {
pub fn zend_register_ini_entries(
ini_entry: *const zend_ini_entry_def,
module_number: ::std::os::raw::c_int,
) -> zend_result;
}
extern "C" { extern "C" {
pub fn zend_register_bool_constant( pub fn zend_register_bool_constant(
name: *const ::std::os::raw::c_char, name: *const ::std::os::raw::c_char,

View File

@ -33,3 +33,4 @@
- [Constants](./macros/constant.md) - [Constants](./macros/constant.md)
- [`ZvalConvert`](./macros/zval_convert.md) - [`ZvalConvert`](./macros/zval_convert.md)
- [Exceptions](./exceptions.md) - [Exceptions](./exceptions.md)
- [INI Settings](./ini-settings.md)

48
guide/src/ini-settings.md Normal file
View File

@ -0,0 +1,48 @@
# INI Settings
Your PHP Extension may want to provide it's own PHP INI settings to configure behaviour. This can be done in the `#[php_startup]` annotated startup function.
## Registering INI Settings
All PHP INI definitions must be registered with PHP to get / set their values via the `php.ini` file or `ini_get() / ini_set()`.
```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
# use ext_php_rs::zend::IniEntryDef;
# use ext_php_rs::flags::IniEntryPermission;
#[php_startup]
pub fn startup_function(ty: i32, module_number: i32) {
let ini_entries: Vec<IniEntryDef> = vec![
IniEntryDef::new(
"my_extension.display_emoji".to_owned(),
"yes".to_owned(),
IniEntryPermission::All,
),
];
IniEntryDef::register(ini_entries, module_number);
}
# fn main() {}
```
## Getting INI Settings
The INI values are stored as part of the `GlobalExecutor`, and can be accessed via the `ini_values()` function. To retrieve the value for a registered INI setting
```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
# use ext_php_rs::zend::ExecutorGlobals;
#[php_startup]
pub fn startup_function(ty: i32, module_number: i32) {
// Get all INI values
let ini_values = ExecutorGlobals::get().ini_values(); // HashMap<String, Option<String>>
let my_ini_value = ini_values.get("my_extension.display_emoji"); // Option<Option<String>>
}
# fn main() {}
```

View File

@ -10,11 +10,12 @@ use crate::ffi::{
E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING, E_RECOVERABLE_ERROR, E_STRICT, E_USER_DEPRECATED, E_USER_ERROR, E_USER_NOTICE, E_USER_WARNING,
E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_MIXED, E_WARNING, IS_ARRAY, IS_CALLABLE, IS_CONSTANT_AST, IS_DOUBLE, IS_FALSE, IS_LONG, IS_MIXED,
IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE, IS_NULL, IS_OBJECT, IS_PTR, IS_REFERENCE, IS_RESOURCE, IS_STRING, IS_TRUE, IS_TYPE_COLLECTABLE,
IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, IS_TYPE_REFCOUNTED, IS_UNDEF, IS_VOID, PHP_INI_ALL, PHP_INI_PERDIR, PHP_INI_SYSTEM,
ZEND_ACC_CALL_VIA_TRAMPOLINE, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, PHP_INI_USER, ZEND_ACC_ABSTRACT, ZEND_ACC_ANON_CLASS, ZEND_ACC_CALL_VIA_TRAMPOLINE,
ZEND_ACC_CTOR, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_CHANGED, ZEND_ACC_CLOSURE, ZEND_ACC_CONSTANTS_UPDATED, ZEND_ACC_CTOR,
ZEND_ACC_FAKE_CLOSURE, ZEND_ACC_FINAL, ZEND_ACC_GENERATOR, ZEND_ACC_HAS_FINALLY_BLOCK, ZEND_ACC_DEPRECATED, ZEND_ACC_DONE_PASS_TWO, ZEND_ACC_EARLY_BINDING, ZEND_ACC_FAKE_CLOSURE,
ZEND_ACC_HAS_RETURN_TYPE, ZEND_ACC_HAS_TYPE_HINTS, ZEND_ACC_HEAP_RT_CACHE, ZEND_ACC_IMMUTABLE, 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_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_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_PROMOTED, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_RESOLVED_INTERFACES,
@ -174,6 +175,16 @@ bitflags! {
} }
} }
bitflags! {
/// Represents permissions for where a configuration setting may be set.
pub struct IniEntryPermission: u32 {
const User = PHP_INI_USER;
const PerDir = PHP_INI_PERDIR;
const System = PHP_INI_SYSTEM;
const All = PHP_INI_ALL;
}
}
bitflags! { bitflags! {
/// Represents error types when used via php_error_docref for example. /// Represents error types when used via php_error_docref for example.
pub struct ErrorType: u32 { pub struct ErrorType: u32 {
@ -194,6 +205,7 @@ bitflags! {
const UserDeprecated = E_USER_DEPRECATED; const UserDeprecated = E_USER_DEPRECATED;
} }
} }
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)] #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
pub enum FunctionType { pub enum FunctionType {
Internal, Internal,

View File

@ -20,6 +20,7 @@
#include "zend_exceptions.h" #include "zend_exceptions.h"
#include "zend_inheritance.h" #include "zend_inheritance.h"
#include "zend_interfaces.h" #include "zend_interfaces.h"
#include "zend_ini.h"
zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent); zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent);
void ext_php_rs_zend_string_release(zend_string *zs); void ext_php_rs_zend_string_release(zend_string *zs);

View File

@ -1,5 +1,6 @@
//! Types related to the PHP executor globals. //! Types related to the PHP executor globals.
use std::collections::HashMap;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard}; use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard};
@ -7,7 +8,7 @@ use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::boxed::ZBox; use crate::boxed::ZBox;
#[cfg(php82)] #[cfg(php82)]
use crate::ffi::zend_atomic_bool_store; use crate::ffi::zend_atomic_bool_store;
use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals}; use crate::ffi::{_zend_executor_globals, ext_php_rs_executor_globals, zend_ini_entry};
use crate::types::{ZendHashTable, ZendObject}; use crate::types::{ZendHashTable, ZendObject};
/// Stores global variables used in the PHP executor. /// Stores global variables used in the PHP executor.
@ -51,6 +52,31 @@ impl ExecutorGlobals {
unsafe { self.class_table.as_ref() } unsafe { self.class_table.as_ref() }
} }
/// Retrieves the ini values for all ini directives in the current executor
/// context..
pub fn ini_values(&self) -> HashMap<String, Option<String>> {
let hash_table = unsafe { &*self.ini_directives };
let mut ini_hash_map: HashMap<String, Option<String>> = HashMap::new();
for (_index, key, value) in hash_table.iter() {
if let Some(key) = key {
ini_hash_map.insert(key, unsafe {
let ini_entry = &*value.ptr::<zend_ini_entry>().expect("Invalid ini entry");
if ini_entry.value.is_null() {
None
} else {
Some(
(*ini_entry.value)
.as_str()
.expect("Ini value is not a string")
.to_owned(),
)
}
});
}
}
ini_hash_map
}
/// Attempts to retrieve the global constants table. /// Attempts to retrieve the global constants table.
pub fn constants(&self) -> Option<&ZendHashTable> { pub fn constants(&self) -> Option<&ZendHashTable> {
unsafe { self.zend_constants.as_ref() } unsafe { self.zend_constants.as_ref() }

57
src/zend/ini_entry_def.rs Normal file
View File

@ -0,0 +1,57 @@
//! Builder for creating inis and methods in PHP.
//! See <https://www.phpinternalsbook.com/php7/extensions_design/ini_settings.html> for details.
use std::{ffi::CString, os::raw::c_char, ptr};
use crate::{ffi::zend_ini_entry_def, ffi::zend_register_ini_entries, flags::IniEntryPermission};
/// A Zend ini entry definition.
///
/// To register ini definitions for extensions, the IniEntryDef builder should be used. Ini
/// entries should be registered in your module's startup_function via `IniEntryDef::register(Vec<IniEntryDef>)`.
pub type IniEntryDef = zend_ini_entry_def;
impl IniEntryDef {
/// Returns an empty ini entry, signifying the end of a ini list.
pub fn new(name: String, default_value: String, permission: IniEntryPermission) -> Self {
let mut template = Self::end();
let name = CString::new(name).expect("Unable to create CString from name");
let value = CString::new(default_value).expect("Unable to create CString from value");
template.name_length = name.as_bytes().len() as _;
template.name = name.into_raw();
template.value_length = value.as_bytes().len() as _;
template.value = value.into_raw();
template.modifiable = IniEntryPermission::PerDir.bits() as _;
template.modifiable = permission.bits() as _;
template
}
/// Returns an empty ini entry def, signifying the end of a ini list.
pub fn end() -> Self {
Self {
name: ptr::null() as *const c_char,
on_modify: None,
mh_arg1: std::ptr::null_mut(),
mh_arg2: std::ptr::null_mut(),
mh_arg3: std::ptr::null_mut(),
value: std::ptr::null_mut(),
displayer: None,
modifiable: 0,
value_length: 0,
name_length: 0,
}
}
/// Converts the ini entry into a raw and pointer, releasing it to the
/// C world.
pub fn into_raw(self) -> *mut Self {
Box::into_raw(Box::new(self))
}
pub fn register(mut entries: Vec<Self>, module_number: i32) {
entries.push(Self::end());
let entries = Box::into_raw(entries.into_boxed_slice()) as *const Self;
unsafe { zend_register_ini_entries(entries, module_number) };
}
}

View File

@ -7,6 +7,7 @@ mod ex;
mod function; mod function;
mod globals; mod globals;
mod handlers; mod handlers;
mod ini_entry_def;
mod module; mod module;
use crate::{error::Result, ffi::php_printf}; use crate::{error::Result, ffi::php_printf};
@ -19,6 +20,7 @@ pub use function::Function;
pub use function::FunctionEntry; pub use function::FunctionEntry;
pub use globals::ExecutorGlobals; pub use globals::ExecutorGlobals;
pub use handlers::ZendObjectHandlers; pub use handlers::ZendObjectHandlers;
pub use ini_entry_def::IniEntryDef;
pub use module::ModuleEntry; pub use module::ModuleEntry;
// Used as the format string for `php_printf`. // Used as the format string for `php_printf`.