ext_php_rs/builders/module.rs
1use crate::{
2 error::Result,
3 ffi::{ext_php_rs_php_build_id, ZEND_MODULE_API_NO},
4 zend::{FunctionEntry, ModuleEntry},
5 PHP_DEBUG, PHP_ZTS,
6};
7
8use std::{ffi::CString, mem, ptr};
9
10/// Builds a Zend module extension to be registered with PHP. Must be called
11/// from within an external function called `get_module`, returning a mutable
12/// pointer to a `ModuleEntry`.
13///
14/// ```
15/// use ext_php_rs::{
16/// builders::ModuleBuilder,
17/// zend::ModuleEntry,
18/// info_table_start, info_table_end, info_table_row
19/// };
20///
21/// #[no_mangle]
22/// pub extern "C" fn php_module_info(_module: *mut ModuleEntry) {
23/// info_table_start!();
24/// info_table_row!("column 1", "column 2");
25/// info_table_end!();
26/// }
27///
28/// #[no_mangle]
29/// pub extern "C" fn get_module() -> *mut ModuleEntry {
30/// ModuleBuilder::new("ext-name", "ext-version")
31/// .info_function(php_module_info)
32/// .build()
33/// .unwrap()
34/// .into_raw()
35/// }
36/// ```
37#[derive(Debug, Clone)]
38pub struct ModuleBuilder {
39 name: String,
40 version: String,
41 module: ModuleEntry,
42 functions: Vec<FunctionEntry>,
43}
44
45impl ModuleBuilder {
46 /// Creates a new module builder with a given name and version.
47 ///
48 /// # Arguments
49 ///
50 /// * `name` - The name of the extension.
51 /// * `version` - The current version of the extension.
52 pub fn new<T: Into<String>, U: Into<String>>(name: T, version: U) -> Self {
53 Self {
54 name: name.into(),
55 version: version.into(),
56 module: ModuleEntry {
57 size: mem::size_of::<ModuleEntry>() as u16,
58 zend_api: ZEND_MODULE_API_NO,
59 zend_debug: u8::from(PHP_DEBUG),
60 zts: u8::from(PHP_ZTS),
61 ini_entry: ptr::null(),
62 deps: ptr::null(),
63 name: ptr::null(),
64 functions: ptr::null(),
65 module_startup_func: None,
66 module_shutdown_func: None,
67 request_startup_func: None,
68 request_shutdown_func: None,
69 info_func: None,
70 version: ptr::null(),
71 globals_size: 0,
72 #[cfg(not(php_zts))]
73 globals_ptr: ptr::null_mut(),
74 #[cfg(php_zts)]
75 globals_id_ptr: ptr::null_mut(),
76 globals_ctor: None,
77 globals_dtor: None,
78 post_deactivate_func: None,
79 module_started: 0,
80 type_: 0,
81 handle: ptr::null_mut(),
82 module_number: 0,
83 build_id: unsafe { ext_php_rs_php_build_id() },
84 },
85 functions: vec![],
86 }
87 }
88
89 /// Sets the startup function for the extension.
90 ///
91 /// # Arguments
92 ///
93 /// * `func` - The function to be called on startup.
94 pub fn startup_function(mut self, func: StartupShutdownFunc) -> Self {
95 self.module.module_startup_func = Some(func);
96 self
97 }
98
99 /// Sets the shutdown function for the extension.
100 ///
101 /// # Arguments
102 ///
103 /// * `func` - The function to be called on shutdown.
104 pub fn shutdown_function(mut self, func: StartupShutdownFunc) -> Self {
105 self.module.module_shutdown_func = Some(func);
106 self
107 }
108
109 /// Sets the request startup function for the extension.
110 ///
111 /// # Arguments
112 ///
113 /// * `func` - The function to be called when startup is requested.
114 pub fn request_startup_function(mut self, func: StartupShutdownFunc) -> Self {
115 self.module.request_startup_func = Some(func);
116 self
117 }
118
119 /// Sets the request shutdown function for the extension.
120 ///
121 /// # Arguments
122 ///
123 /// * `func` - The function to be called when shutdown is requested.
124 pub fn request_shutdown_function(mut self, func: StartupShutdownFunc) -> Self {
125 self.module.request_shutdown_func = Some(func);
126 self
127 }
128
129 /// Sets the post request shutdown function for the extension.
130 ///
131 /// This function can be useful if you need to do any final cleanup at the
132 /// very end of a request, after all other resources have been released. For
133 /// example, if your extension creates any persistent resources that last
134 /// beyond a single request, you could use this function to clean those up.
135 /// # Arguments
136 ///
137 /// * `func` - The function to be called when shutdown is requested.
138 pub fn post_deactivate_function(mut self, func: extern "C" fn() -> i32) -> Self {
139 self.module.post_deactivate_func = Some(func);
140 self
141 }
142
143 /// Sets the extension information function for the extension.
144 ///
145 /// # Arguments
146 ///
147 /// * `func` - The function to be called to retrieve the information about
148 /// the extension.
149 pub fn info_function(mut self, func: InfoFunc) -> Self {
150 self.module.info_func = Some(func);
151 self
152 }
153
154 /// Adds a function to the extension.
155 ///
156 /// # Arguments
157 ///
158 /// * `func` - The function to be added to the extension.
159 pub fn function(mut self, func: FunctionEntry) -> Self {
160 self.functions.push(func);
161 self
162 }
163
164 /// Builds the extension and returns a `ModuleEntry`.
165 ///
166 /// Returns a result containing the module entry if successful.
167 pub fn build(mut self) -> Result<ModuleEntry> {
168 self.functions.push(FunctionEntry::end());
169 self.module.functions =
170 Box::into_raw(self.functions.into_boxed_slice()) as *const FunctionEntry;
171 self.module.name = CString::new(self.name)?.into_raw();
172 self.module.version = CString::new(self.version)?.into_raw();
173
174 Ok(self.module)
175 }
176}
177
178/// A function to be called when the extension is starting up or shutting down.
179pub type StartupShutdownFunc = extern "C" fn(_type: i32, _module_number: i32) -> i32;
180
181/// A function to be called when `phpinfo();` is called.
182pub type InfoFunc = extern "C" fn(zend_module: *mut ModuleEntry);