Skip to main content

ext_php_rs/zend/
mod.rs

1//! Types used to interact with the Zend engine.
2
3mod _type;
4mod bailout_guard;
5pub mod ce;
6mod class;
7#[cfg(feature = "observer")]
8pub(crate) mod error_observer;
9mod ex;
10#[cfg(feature = "observer")]
11pub(crate) mod exception_observer;
12mod function;
13mod globals;
14mod handlers;
15mod ini_entry_def;
16mod linked_list;
17mod module;
18pub(crate) mod module_globals;
19#[cfg(feature = "observer")]
20pub(crate) mod observer;
21mod streams;
22mod try_catch;
23#[cfg(feature = "observer")]
24pub(crate) mod zend_extension;
25
26use crate::{
27    error::Result,
28    ffi::{php_output_write, php_printf, sapi_module},
29};
30use std::ffi::CString;
31use std::os::raw::c_char;
32
33pub use _type::ZendType;
34pub use bailout_guard::BailoutGuard;
35pub use bailout_guard::run_bailout_cleanups;
36pub use class::ClassEntry;
37#[cfg(feature = "observer")]
38pub use error_observer::{BacktraceFrame, ErrorInfo, ErrorObserver, ErrorType};
39pub use ex::ExecuteData;
40#[cfg(feature = "observer")]
41pub use exception_observer::{ExceptionInfo, ExceptionObserver};
42pub use function::Function;
43pub use function::FunctionEntry;
44pub use globals::ExecutorGlobals;
45pub use globals::FileGlobals;
46pub use globals::ProcessGlobals;
47pub use globals::SapiGlobals;
48pub use globals::SapiHeader;
49pub use globals::SapiHeaders;
50pub use globals::SapiModule;
51pub use handlers::ZendObjectHandlers;
52pub use ini_entry_def::IniEntryDef;
53pub use linked_list::ZendLinkedList;
54pub use module::{ModuleEntry, StaticModuleEntry, cleanup_module_allocations};
55pub use module_globals::{ModuleGlobal, ModuleGlobals};
56#[cfg(feature = "observer")]
57pub use observer::{FcallInfo, FcallObserver};
58pub use streams::*;
59#[cfg(feature = "embed")]
60pub(crate) use try_catch::panic_wrapper;
61pub use try_catch::{CatchError, bailout, try_catch, try_catch_first};
62#[cfg(feature = "observer")]
63pub use zend_extension::{ZendExtensionBuilder, ZendExtensionHandler};
64
65/// Register a `zend_extension` with the PHP engine.
66///
67/// # Safety
68///
69/// * Must be called during `MINIT`.
70/// * The struct's string fields (`name`, `version`, ...) must point at memory
71///   that lives for the process lifetime. PHP copies the struct into its
72///   internal list but keeps those `*const c_char` pointers.
73#[cfg(feature = "observer")]
74pub(crate) unsafe fn register_extension(ext: *mut crate::ffi::zend_extension) {
75    unsafe {
76        crate::ffi::zend_register_extension(ext, std::ptr::null_mut());
77    }
78}
79
80// Used as the format string for `php_printf`.
81const FORMAT_STR: &[u8] = b"%s\0";
82
83/// Prints to stdout using the `php_printf` function.
84///
85/// Also see the [`php_print`] and [`php_println`] macros.
86///
87/// # Arguments
88///
89/// * message - The message to print to stdout.
90///
91/// # Errors
92///
93/// * If the message could not be converted to a [`CString`].
94pub fn printf(message: &str) -> Result<()> {
95    let message = CString::new(message)?;
96    unsafe {
97        php_printf(FORMAT_STR.as_ptr().cast(), message.as_ptr());
98    };
99    Ok(())
100}
101
102/// Writes binary data to PHP's output stream (stdout).
103///
104/// Unlike [`printf`], this function is binary-safe and can handle data
105/// containing NUL bytes. It uses the SAPI module's `ub_write` function
106/// which accepts a pointer and length, allowing arbitrary binary data.
107///
108/// Also see the [`php_write!`] macro.
109///
110/// # Arguments
111///
112/// * `data` - The binary data to write to stdout.
113///
114/// # Returns
115///
116/// The number of bytes written.
117///
118/// # Errors
119///
120/// Returns [`crate::error::Error::SapiWriteUnavailable`] if the SAPI's
121/// `ub_write` function is not available.
122///
123/// # Example
124///
125/// ```ignore
126/// use ext_php_rs::zend::write;
127///
128/// // Write binary data including NUL bytes
129/// let data = b"Hello\x00World";
130/// write(data).expect("Failed to write data");
131/// ```
132pub fn write(data: &[u8]) -> Result<usize> {
133    unsafe {
134        if let Some(ub_write) = sapi_module.ub_write {
135            Ok(ub_write(data.as_ptr().cast::<c_char>(), data.len()))
136        } else {
137            Err(crate::error::Error::SapiWriteUnavailable)
138        }
139    }
140}
141
142/// Writes binary data to PHP's output stream with output buffering support.
143///
144/// This function is binary-safe (can handle NUL bytes) AND respects PHP's
145/// output buffering (`ob_start()`). Use this when you need both binary-safe
146/// output and output buffering compatibility.
147///
148/// # Arguments
149///
150/// * `data` - The binary data to write.
151///
152/// # Returns
153///
154/// The number of bytes written.
155///
156/// # Comparison
157///
158/// | Function | Binary-safe | Output Buffering |
159/// |----------|-------------|------------------|
160/// | [`printf`] | No | Yes |
161/// | [`write()`] | Yes | No (unbuffered) |
162/// | [`output_write`] | Yes | Yes |
163///
164/// # Example
165///
166/// ```ignore
167/// use ext_php_rs::zend::output_write;
168///
169/// // Binary data that will be captured by ob_start()
170/// let data = b"Hello\x00World";
171/// output_write(data);
172/// ```
173#[inline]
174#[must_use]
175pub fn output_write(data: &[u8]) -> usize {
176    unsafe { php_output_write(data.as_ptr().cast::<c_char>(), data.len()) }
177}
178
179/// Get the name of the SAPI module.
180///
181/// # Panics
182///
183/// * If the module name is not a valid [`CStr`]
184///
185/// [`CStr`]: std::ffi::CStr
186pub fn php_sapi_name() -> String {
187    let c_str = unsafe { std::ffi::CStr::from_ptr(sapi_module.name) };
188    c_str.to_str().expect("Unable to parse CStr").to_string()
189}