Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions allowed_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ bind! {
zend_string,
zend_string_init_interned,
zend_throw_exception_ex,
zend_throw_exception_object,
zend_type,
zend_value,
zend_wrong_parameters_count_error,
Expand Down
3 changes: 3 additions & 0 deletions docsrs_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,9 @@ extern "C" {
...
) -> *mut zend_object;
}
extern "C" {
pub fn zend_throw_exception_object(exception: *mut zval);
}
extern "C" {
pub fn zend_do_implement_interface(ce: *mut zend_class_entry, iface: *mut zend_class_entry);
}
Expand Down
68 changes: 66 additions & 2 deletions src/exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::{
class::RegisteredClass,
error::{Error, Result},
ffi::zend_throw_exception_ex,
ffi::zend_throw_exception_object,
flags::ClassFlags,
types::Zval,
zend::{ce, ClassEntry},
};

Expand All @@ -25,6 +27,7 @@ pub struct PhpException {
message: String,
code: i32,
ex: &'static ClassEntry,
object: Option<Zval>,
}

impl PhpException {
Expand All @@ -36,7 +39,12 @@ impl PhpException {
/// * `code` - Integer code to go inside the exception.
/// * `ex` - Exception type to throw.
pub fn new(message: String, code: i32, ex: &'static ClassEntry) -> Self {
Self { message, code, ex }
Self {
message,
code,
ex,
object: None,
}
}

/// Creates a new default exception instance, using the default PHP
Expand All @@ -59,10 +67,25 @@ impl PhpException {
Self::new(message, 0, T::get_metadata().ce())
}

/// Set the Zval object for the exception.
///
/// Exceptions can be based of instantiated Zval objects when you are throwing a custom exception with
/// stateful properties.
///
/// # Parameters
///
/// * `object` - The Zval object.
pub fn set_object(&mut self, object: Option<Zval>) {
self.object = object;
}

/// Throws the exception, returning nothing inside a result if successful
/// and an error otherwise.
pub fn throw(self) -> Result<()> {
throw_with_code(self.ex, self.code, &self.message)
match self.object {
Some(object) => throw_object(object),
None => throw_with_code(self.ex, self.code, &self.message),
}
}
}

Expand Down Expand Up @@ -146,3 +169,44 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()>
};
Ok(())
}

/// Throws an exception object.
///
/// Returns a result containing nothing if the exception was successfully
/// thrown.
///
/// # Parameters
///
/// * `object` - The zval of type object
///
/// # Examples
///
/// ```no_run
/// use ext_php_rs::prelude::*;
/// use ext_php_rs::exception::throw_object;
/// use crate::ext_php_rs::convert::IntoZval;
///
/// #[php_class]
/// #[extends(ext_php_rs::zend::ce::exception())]
/// pub struct JsException {
/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)]
/// message: String,
/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)]
/// code: i32,
/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)]
/// file: String,
/// }
///
/// #[php_module]
/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
/// module
/// }
///
/// let error = JsException { message: "A JS error occurred.".to_string(), code: 100, file: "index.js".to_string() };
/// throw_object( error.into_zval(true).unwrap() );
/// ```
pub fn throw_object(zval: Zval) -> Result<()> {
let mut zv = core::mem::ManuallyDrop::new(zval);
unsafe { zend_throw_exception_object(core::ptr::addr_of_mut!(zv).cast()) };
Ok(())
}