Support registering ini definitions for modules

PHP extensions that want to provide ini settings can do so using `IniEntryDef::register()`; values can then be fetched via `GlobalExecutor::ini_values()`.
This commit is contained in:
Joe Hoyle 2023-02-05 12:23:01 +02:00
parent 8b87e4038e
commit a7780753dc
7 changed files with 142 additions and 5 deletions

View File

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

View File

@ -83,6 +83,10 @@ pub const ZEND_MODULE_API_NO: u32 = 20220829;
pub const USING_ZTS: u32 = 0;
pub const MAY_BE_BOOL: u32 = 12;
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_PERSISTENT: u32 = 1;
pub const CONST_NO_FILE_CACHE: u32 = 2;
@ -1343,6 +1347,32 @@ extern "C" {
}
#[repr(C)]
#[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 name: *mut zend_string,
pub on_modify: ::std::option::Option<
@ -1368,6 +1398,12 @@ pub struct _zend_ini_entry {
pub orig_modifiable: 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" {
pub fn zend_register_bool_constant(
name: *const ::std::os::raw::c_char,

View File

@ -8,10 +8,11 @@ use crate::ffi::{
CONST_CS, CONST_DEPRECATED, CONST_NO_FILE_CACHE, CONST_PERSISTENT, 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_TYPE_REFCOUNTED,
IS_UNDEF, IS_VOID, 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,
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,
@ -171,6 +172,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;
}
}
/// Valid data types for PHP.
#[repr(C, u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]

View File

@ -20,6 +20,7 @@
#include "zend_exceptions.h"
#include "zend_inheritance.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);
void ext_php_rs_zend_string_release(zend_string *zs);

View File

@ -1,11 +1,12 @@
//! Types related to the PHP executor globals.
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use parking_lot::{const_rwlock, RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::boxed::ZBox;
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};
@ -50,6 +51,29 @@ impl ExecutorGlobals {
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.as_ref().unwrap() };
let mut ini_hash_map: HashMap<String, Option<String>> = HashMap::new();
for (_index, key, value) in hash_table.iter() {
match key {
Some(key) => {
ini_hash_map.insert(key, unsafe {
let ini_entry = &*value.ptr::<zend_ini_entry>().unwrap();
if ini_entry.value.is_null() {
None
} else {
Some((&*ini_entry.value).as_str().unwrap().to_owned())
}
});
()
}
None => (),
}
}
ini_hash_map
}
/// Attempts to extract the last PHP exception captured by the interpreter.
/// Returned inside a [`ZBox`].
///

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).unwrap();
let value = CString::new(default_value).unwrap();
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 globals;
mod handlers;
mod ini_entry_def;
mod module;
use crate::{error::Result, ffi::php_printf};
@ -18,6 +19,7 @@ pub use ex::ExecuteData;
pub use function::FunctionEntry;
pub use globals::ExecutorGlobals;
pub use handlers::ZendObjectHandlers;
pub use ini_entry_def::IniEntryDef;
pub use module::ModuleEntry;
// Used as the format string for `php_printf`.