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