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