phper/
ini.rs

1// Copyright (c) 2022 PHPER Framework Team
2// PHPER is licensed under Mulan PSL v2.
3// You can use this software according to the terms and conditions of the Mulan
4// PSL v2. You may obtain a copy of Mulan PSL v2 at:
5//          http://license.coscl.org.cn/MulanPSL2
6// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
7// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
8// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9// See the Mulan PSL v2 for more details.
10
11//! Apis relate to [zend_ini_entry_def].
12
13use crate::sys::*;
14use std::{
15    ffi::{CStr, c_int},
16    mem::zeroed,
17    os::raw::c_char,
18    ptr::null_mut,
19    str,
20};
21
22/// Get the global registered configuration value.
23///
24/// # Examples
25///
26/// ```no_run
27/// use phper::ini::ini_get;
28/// use std::ffi::CStr;
29///
30/// let _foo = ini_get::<bool>("FOO");
31/// let _bar = ini_get::<Option<&CStr>>("BAR");
32/// ```
33pub fn ini_get<T: FromIniValue>(name: &str) -> T {
34    T::from_ini_value(name)
35}
36
37/// Configuration changeable policy.
38#[repr(u32)]
39#[derive(Copy, Clone)]
40pub enum Policy {
41    /// Entry can be set anywhere.
42    All = PHP_INI_ALL,
43    /// Entry can be set in user scripts (like with `ini_set()`) or in the
44    /// Windows registry. Entry can be set in `.user.ini`.
45    User = PHP_INI_USER,
46    /// Entry can be set in `php.ini`, `.htaccess`, `httpd.conf` or `.user.ini`.
47    Perdir = PHP_INI_PERDIR,
48    /// Entry can be set in `php.ini` or `httpd.conf`.
49    System = PHP_INI_SYSTEM,
50}
51
52/// The Type which can transform to an ini value.
53pub trait IntoIniValue {
54    /// transform to an ini value.
55    fn into_ini_value(self) -> String;
56}
57
58impl IntoIniValue for bool {
59    #[inline]
60    fn into_ini_value(self) -> String {
61        if self { "1".to_owned() } else { "0".to_owned() }
62    }
63}
64
65impl IntoIniValue for i64 {
66    #[inline]
67    fn into_ini_value(self) -> String {
68        self.to_string()
69    }
70}
71
72impl IntoIniValue for f64 {
73    #[inline]
74    fn into_ini_value(self) -> String {
75        self.to_string()
76    }
77}
78
79impl IntoIniValue for String {
80    #[inline]
81    fn into_ini_value(self) -> String {
82        self
83    }
84}
85
86/// The Type which can transform from an ini key name.
87///
88/// For php7, the zend_ini_* functions receive ini name as `*mut c_char`, but I
89/// think it's immutable.
90pub trait FromIniValue {
91    /// transform from an ini key name.
92    fn from_ini_value(name: &str) -> Self;
93}
94
95impl FromIniValue for bool {
96    #[allow(clippy::useless_conversion)]
97    fn from_ini_value(name: &str) -> Self {
98        let s = <Option<&CStr>>::from_ini_value(name);
99        [Some(c"1"), Some(c"true"), Some(c"on"), Some(c"On")].contains(&s)
100    }
101}
102
103impl FromIniValue for i64 {
104    #[allow(clippy::useless_conversion)]
105    fn from_ini_value(name: &str) -> Self {
106        unsafe {
107            let name_ptr = name.as_ptr() as *mut u8 as *mut c_char;
108            zend_ini_long(name_ptr, name.len().try_into().unwrap(), 0)
109        }
110    }
111}
112
113impl FromIniValue for f64 {
114    #[allow(clippy::useless_conversion)]
115    fn from_ini_value(name: &str) -> Self {
116        unsafe {
117            let name_ptr = name.as_ptr() as *mut u8 as *mut c_char;
118            zend_ini_double(name_ptr, name.len().try_into().unwrap(), 0)
119        }
120    }
121}
122
123impl FromIniValue for Option<&CStr> {
124    #[allow(clippy::useless_conversion)]
125    fn from_ini_value(name: &str) -> Self {
126        unsafe {
127            let name_ptr = name.as_ptr() as *mut u8 as *mut c_char;
128            let ptr = zend_ini_string_ex(name_ptr, name.len().try_into().unwrap(), 0, null_mut());
129            (!ptr.is_null()).then(|| CStr::from_ptr(ptr))
130        }
131    }
132}
133
134pub(crate) struct IniEntity {
135    name: String,
136    default_value: String,
137    policy: Policy,
138}
139
140impl IniEntity {
141    pub(crate) fn new<T: IntoIniValue>(
142        name: impl Into<String>, default_value: T, policy: Policy,
143    ) -> Self {
144        Self {
145            name: name.into(),
146            default_value: default_value.into_ini_value(),
147            policy,
148        }
149    }
150
151    #[inline]
152    pub(crate) fn entry(&self) -> zend_ini_entry_def {
153        create_ini_entry_ex(&self.name, &self.default_value, self.policy as u32)
154    }
155}
156
157fn create_ini_entry_ex(name: &str, default_value: &str, modifiable: u32) -> zend_ini_entry_def {
158    #[cfg(any(
159        phper_major_version = "8",
160        all(
161            phper_major_version = "7",
162            any(phper_minor_version = "4", phper_minor_version = "3")
163        )
164    ))]
165    let (modifiable, name_length) = (modifiable as std::os::raw::c_uchar, name.len() as u16);
166
167    #[cfg(all(
168        phper_major_version = "7",
169        any(
170            phper_minor_version = "2",
171            phper_minor_version = "1",
172            phper_minor_version = "0",
173        )
174    ))]
175    let (modifiable, name_length) = (modifiable as std::os::raw::c_int, name.len() as u32);
176
177    zend_ini_entry_def {
178        name: name.as_ptr().cast(),
179        on_modify: None,
180        mh_arg1: null_mut(),
181        mh_arg2: null_mut(),
182        mh_arg3: null_mut(),
183        value: default_value.as_ptr().cast(),
184        displayer: None,
185        modifiable,
186        name_length,
187        value_length: default_value.len() as u32,
188    }
189}
190
191unsafe fn entries(ini_entries: &[IniEntity]) -> *const zend_ini_entry_def {
192    unsafe {
193        let mut entries = Vec::with_capacity(ini_entries.len() + 1);
194
195        ini_entries.iter().for_each(|entity| {
196            // Ini entity will exist throughout the whole application life cycle.
197            entries.push(entity.entry());
198        });
199
200        entries.push(zeroed::<zend_ini_entry_def>());
201
202        Box::into_raw(entries.into_boxed_slice()).cast()
203    }
204}
205
206pub(crate) fn register(ini_entries: &[IniEntity], module_number: c_int) {
207    unsafe {
208        zend_register_ini_entries(entries(ini_entries), module_number);
209    }
210}
211
212pub(crate) fn unregister(module_number: c_int) {
213    unsafe {
214        zend_unregister_ini_entries(module_number);
215    }
216}