Skip to content

Commit d5e05f0

Browse files
committed
Add support for throwing Exception objects
Currently we only support throwing exception class entries, i.e. stateless exceptions. Ideally we also want to support throwing a ZVal which has a ClassEntry that extends the Exception PHP class. This can be used to throw stateful exceptions which is not uncommon. This PR is missing one piece: `throw_object` will currently `drop` the Zval when the function is completed, causing reference / null pointer errors. It seems `ZVal::Drop` doesn't actually free the zval currently, it just sets the type to NULL (which also breaks things.) Discussed briefly in https://discord.com/channels/115233111977099271/1025314959179120714 on how best to solve this, but I'm not totally clear still! Ideally I think we want `throw_object` to own the `zval` but not free it once the function returns.
1 parent 199962c commit d5e05f0

File tree

2 files changed

+58
-3
lines changed

2 files changed

+58
-3
lines changed

allowed_bindings.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ bind! {
8181
zend_string,
8282
zend_string_init_interned,
8383
zend_throw_exception_ex,
84+
zend_throw_exception_object,
8485
zend_type,
8586
zend_value,
8687
zend_wrong_parameters_count_error,

src/exception.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ use crate::{
66
class::RegisteredClass,
77
error::{Error, Result},
88
ffi::zend_throw_exception_ex,
9+
ffi::zend_throw_exception_object,
910
flags::ClassFlags,
10-
zend::{ce, ClassEntry},
11+
zend::{ce, ClassEntry}, types::Zval,
1112
};
1213

1314
/// Result type with the error variant as a [`PhpException`].
@@ -25,6 +26,7 @@ pub struct PhpException {
2526
message: String,
2627
code: i32,
2728
ex: &'static ClassEntry,
29+
object: Option<Zval>,
2830
}
2931

3032
impl PhpException {
@@ -36,7 +38,7 @@ impl PhpException {
3638
/// * `code` - Integer code to go inside the exception.
3739
/// * `ex` - Exception type to throw.
3840
pub fn new(message: String, code: i32, ex: &'static ClassEntry) -> Self {
39-
Self { message, code, ex }
41+
Self { message, code, ex, object: None, }
4042
}
4143

4244
/// Creates a new default exception instance, using the default PHP
@@ -59,10 +61,27 @@ impl PhpException {
5961
Self::new(message, 0, T::get_metadata().ce())
6062
}
6163

64+
/// Set the Zval object for the exception.
65+
///
66+
/// Exceptions can be based of instantiated Zval objects when you are throwing a custom exception with
67+
/// stateful properties.
68+
///
69+
/// # Parameters
70+
///
71+
/// * `object` - The Zval object.
72+
pub fn set_object(&mut self, object: Option<Zval>) {
73+
self.object = object;
74+
}
75+
6276
/// Throws the exception, returning nothing inside a result if successful
6377
/// and an error otherwise.
6478
pub fn throw(self) -> Result<()> {
65-
throw_with_code(self.ex, self.code, &self.message)
79+
match self.object {
80+
Some(object) => {
81+
throw_object(object)
82+
},
83+
None => throw_with_code(self.ex, self.code, &self.message),
84+
}
6685
}
6786
}
6887

@@ -146,3 +165,38 @@ pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()>
146165
};
147166
Ok(())
148167
}
168+
169+
170+
/// Throws an exception object.
171+
///
172+
/// Returns a result containing nothing if the exception was successfully
173+
/// thrown.
174+
///
175+
/// # Parameters
176+
///
177+
/// * `object` - The zval of type object
178+
///
179+
/// # Examples
180+
///
181+
/// ```no_run
182+
/// use ext_php_rs::{zend::{ce, ClassEntry}, exception::throw_with_code};
183+
///
184+
/// #[php_class]
185+
/// #[extends(ext_php_rs::zend::ce::exception())]
186+
/// pub struct JsException {
187+
/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)]
188+
/// message: String,
189+
/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)]
190+
/// code: i32,
191+
/// #[prop(flags = ext_php_rs::flags::PropertyFlags::Public)]
192+
/// file: String,
193+
/// }
194+
/// let error = JsException { message: "A JS error occurred.", code: 100, file: "index.js" };
195+
/// throw_object( error.into_val(true) );
196+
/// ```
197+
pub fn throw_object(zval: Zval) -> Result<()> {
198+
unsafe {
199+
zend_throw_exception_object((&zval as *const _) as *mut _);
200+
};
201+
Ok(())
202+
}

0 commit comments

Comments
 (0)