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