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);