Skip to main content

ext_php_rs/
constant.rs

1//! Types and traits for registering constants in PHP.
2
3use cfg_if::cfg_if;
4use std::ffi::CString;
5use std::fmt::Debug;
6
7use super::flags::GlobalConstantFlags;
8use crate::error::Result;
9use crate::ffi::{
10    zend_register_bool_constant, zend_register_double_constant, zend_register_long_constant,
11    zend_register_string_constant,
12};
13
14/// Implemented on types which can be registered as a constant in PHP.
15pub trait IntoConst: Debug {
16    /// Returns the PHP stub representation of this constant value.
17    ///
18    /// This is used when generating PHP stub files for IDE autocompletion.
19    /// The returned string should be a valid PHP literal (e.g., `"hello"`,
20    /// `42`, `true`).
21    fn stub_value(&self) -> String;
22
23    /// Registers a global module constant in PHP, with the value as the content
24    /// of self. This function _must_ be called in the module startup
25    /// function, which is called after the module is initialized. The
26    /// second parameter of the startup function will be the module number.
27    /// By default, the case-insensitive and persistent flags are set when
28    /// registering the constant.
29    ///
30    /// Returns a result containing nothing if the constant was successfully
31    /// registered.
32    ///
33    /// # Parameters
34    ///
35    /// * `name` - The name of the constant.
36    /// * `module_number` - The module number that we are registering the
37    ///   constant under.
38    ///
39    /// # Errors
40    ///
41    /// Returns an error if the constant could not be registered.
42    ///
43    /// # Examples
44    ///
45    /// ```no_run
46    /// use ext_php_rs::constant::IntoConst;
47    ///
48    /// pub extern "C" fn startup_function(_type: i32, module_number: i32) -> i32 {
49    ///     5.register_constant("MY_CONST_NAME", module_number); // MY_CONST_NAME == 5
50    ///     "Hello, world!".register_constant("STRING_CONSTANT", module_number); // STRING_CONSTANT == "Hello, world!"
51    ///     0
52    /// }
53    /// ```
54    fn register_constant(&self, name: &str, module_number: i32) -> Result<()> {
55        self.register_constant_flags(name, module_number, GlobalConstantFlags::Persistent)
56    }
57
58    /// Registers a global module constant in PHP, with the value as the content
59    /// of self. This function _must_ be called in the module startup
60    /// function, which is called after the module is initialized. The
61    /// second parameter of the startup function will be the module number.
62    /// This function allows you to pass any extra flags in if you require.
63    /// Note that the case-sensitive and persistent flags *are not* set when you
64    /// use this function, you must set these yourself.
65    ///
66    /// Returns a result containing nothing if the constant was successfully
67    /// registered.
68    ///
69    /// # Parameters
70    ///
71    /// * `name` - The name of the constant.
72    /// * `module_number` - The module number that we are registering the
73    ///   constant under.
74    /// * `flags` - Flags to register the constant with.
75    ///
76    /// # Errors
77    ///
78    /// Returns an error if the constant flags could not be registered.
79    ///
80    /// # Examples
81    ///
82    /// ```no_run
83    /// use ext_php_rs::{constant::IntoConst, flags::GlobalConstantFlags};
84    ///
85    /// pub extern "C" fn startup_function(_type: i32, module_number: i32) -> i32 {
86    ///     42.register_constant_flags("MY_CONST_NAME", module_number, GlobalConstantFlags::Persistent | GlobalConstantFlags::Deprecated);
87    ///     0
88    /// }
89    /// ```
90    fn register_constant_flags(
91        &self,
92        name: &str,
93        module_number: i32,
94        flags: GlobalConstantFlags,
95    ) -> Result<()>;
96}
97
98impl IntoConst for String {
99    fn stub_value(&self) -> String {
100        self.as_str().stub_value()
101    }
102
103    fn register_constant_flags(
104        &self,
105        name: &str,
106        module_number: i32,
107        flags: GlobalConstantFlags,
108    ) -> Result<()> {
109        self.as_str()
110            .register_constant_flags(name, module_number, flags)
111    }
112}
113
114impl IntoConst for &str {
115    fn stub_value(&self) -> String {
116        // Escape special characters for PHP string literal
117        let escaped = self
118            .replace('\\', "\\\\")
119            .replace('\'', "\\'")
120            .replace('\n', "\\n")
121            .replace('\r', "\\r")
122            .replace('\t', "\\t");
123        format!("'{escaped}'")
124    }
125
126    fn register_constant_flags(
127        &self,
128        name: &str,
129        module_number: i32,
130        flags: GlobalConstantFlags,
131    ) -> Result<()> {
132        unsafe {
133            cfg_if! {
134                if #[cfg(php85)] {
135                    let _ = zend_register_string_constant(
136                        CString::new(name)?.as_ptr(),
137                        name.len() as _,
138                        CString::new(*self)?.as_ptr(),
139                        flags.bits().try_into()?,
140                        module_number,
141                    );
142                } else {
143                    zend_register_string_constant(
144                        CString::new(name)?.as_ptr(),
145                        name.len() as _,
146                        CString::new(*self)?.as_ptr(),
147                        flags.bits().try_into()?,
148                        module_number,
149                    );
150                }
151            }
152        };
153        Ok(())
154    }
155}
156
157impl IntoConst for bool {
158    fn stub_value(&self) -> String {
159        if *self { "true" } else { "false" }.to_string()
160    }
161
162    fn register_constant_flags(
163        &self,
164        name: &str,
165        module_number: i32,
166        flags: GlobalConstantFlags,
167    ) -> Result<()> {
168        unsafe {
169            cfg_if! {
170                if #[cfg(php85)] {
171                    let _ = zend_register_bool_constant(
172                        CString::new(name)?.as_ptr(),
173                        name.len() as _,
174                        *self,
175                        flags.bits().try_into()?,
176                        module_number,
177                    );
178                } else {
179                    zend_register_bool_constant(
180                        CString::new(name)?.as_ptr(),
181                        name.len() as _,
182                        *self,
183                        flags.bits().try_into()?,
184                        module_number,
185                    );
186                }
187            }
188        };
189        Ok(())
190    }
191}
192
193/// Implements the `IntoConst` trait for a given number type using a given
194/// function.
195macro_rules! into_const_num {
196    ($type: ty, $fn: expr) => {
197        impl IntoConst for $type {
198            fn stub_value(&self) -> String {
199                self.to_string()
200            }
201
202            fn register_constant_flags(
203                &self,
204                name: &str,
205                module_number: i32,
206                flags: GlobalConstantFlags,
207            ) -> Result<()> {
208                unsafe {
209                    cfg_if! {
210                        if #[cfg(php85)] {
211                            let _ = $fn(
212                                CString::new(name)?.as_ptr(),
213                                name.len() as _,
214                                (*self).into(),
215                                flags.bits().try_into()?,
216                                module_number,
217                            );
218                        } else {
219                            $fn(
220                                CString::new(name)?.as_ptr(),
221                                name.len() as _,
222                                (*self).into(),
223                                flags.bits().try_into()?,
224                                module_number,
225                            );
226                        }
227                    }
228                };
229                Ok(())
230            }
231        }
232    };
233}
234
235into_const_num!(i8, zend_register_long_constant);
236into_const_num!(i16, zend_register_long_constant);
237into_const_num!(i32, zend_register_long_constant);
238into_const_num!(i64, zend_register_long_constant);
239into_const_num!(f32, zend_register_double_constant);
240into_const_num!(f64, zend_register_double_constant);