use std::ffi::CString;
use crate::{
class::RegisteredClass,
error::{Error, Result},
ffi::zend_throw_exception_ex,
flags::ClassFlags,
zend::{ce, ClassEntry},
};
pub type PhpResult<T = ()> = std::result::Result<T, PhpException>;
#[derive(Debug)]
pub struct PhpException {
message: String,
code: i32,
ex: &'static ClassEntry,
}
impl PhpException {
pub fn new(message: String, code: i32, ex: &'static ClassEntry) -> Self {
Self { message, code, ex }
}
pub fn default(message: String) -> Self {
Self::new(message, 0, ce::exception())
}
pub fn from_class<T: RegisteredClass>(message: String) -> Self {
Self::new(message, 0, T::get_metadata().ce())
}
pub fn throw(self) -> Result<()> {
throw_with_code(self.ex, self.code, &self.message)
}
}
impl From<String> for PhpException {
fn from(str: String) -> Self {
Self::default(str)
}
}
impl From<&str> for PhpException {
fn from(str: &str) -> Self {
Self::default(str.into())
}
}
#[cfg(feature = "anyhow")]
impl From<anyhow::Error> for PhpException {
fn from(err: anyhow::Error) -> Self {
Self::new(format!("{:#}", err), 0, crate::zend::ce::exception())
}
}
pub fn throw(ex: &ClassEntry, message: &str) -> Result<()> {
throw_with_code(ex, 0, message)
}
pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> {
let flags = ex.flags();
if flags.contains(ClassFlags::Interface) || flags.contains(ClassFlags::Abstract) {
return Err(Error::InvalidException(flags));
}
unsafe {
zend_throw_exception_ex(
(ex as *const _) as *mut _,
code as _,
CString::new("%s")?.as_ptr(),
CString::new(message)?.as_ptr(),
)
};
Ok(())
}