• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidcole1340 / ext-php-rs / 14502041373

16 Apr 2025 08:30PM UTC coverage: 14.129% (+0.7%) from 13.479%
14502041373

push

github

web-flow
style(clippy): apply pedantic rules

Refs: #418

41 of 345 new or added lines in 46 files covered. (11.88%)

48 existing lines in 25 files now uncovered.

553 of 3914 relevant lines covered (14.13%)

1.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/exception.rs
1
//! Types and functions used for throwing exceptions from Rust to PHP.
2

3
use std::{ffi::CString, fmt::Debug, ptr};
4

5
use crate::{
6
    class::RegisteredClass,
7
    error::{Error, Result},
8
    ffi::zend_throw_exception_ex,
9
    ffi::zend_throw_exception_object,
10
    flags::ClassFlags,
11
    types::Zval,
12
    zend::{ce, ClassEntry},
13
};
14

15
/// Result type with the error variant as a [`PhpException`].
16
pub type PhpResult<T = ()> = std::result::Result<T, PhpException>;
17

18
/// Represents a PHP exception which can be thrown using the `throw()` function.
19
/// Primarily used to return from a [`Result<T, PhpException>`] which can
20
/// immediately be thrown by the `ext-php-rs` macro API.
21
///
22
/// There are default [`From`] implementations for any type that implements
23
/// [`ToString`], so these can also be returned from these functions. You can
24
/// also implement [`From<T>`] for your custom error type.
25
#[derive(Debug)]
26
pub struct PhpException {
27
    message: String,
28
    code: i32,
29
    ex: &'static ClassEntry,
30
    object: Option<Zval>,
31
}
32

33
impl PhpException {
34
    /// Creates a new exception instance.
35
    ///
36
    /// # Parameters
37
    ///
38
    /// * `message` - Message to contain in the exception.
39
    /// * `code` - Integer code to go inside the exception.
40
    /// * `ex` - Exception type to throw.
41
    #[must_use]
UNCOV
42
    pub fn new(message: String, code: i32, ex: &'static ClassEntry) -> Self {
×
43
        Self {
44
            message,
45
            code,
46
            ex,
47
            object: None,
48
        }
49
    }
50

51
    /// Creates a new default exception instance, using the default PHP
52
    /// `Exception` type as the exception type, with an integer code of
53
    /// zero.
54
    ///
55
    /// # Parameters
56
    ///
57
    /// * `message` - Message to contain in the exception.
58
    #[must_use]
59
    pub fn default(message: String) -> Self {
×
60
        Self::new(message, 0, ce::exception())
×
61
    }
62

63
    /// Creates an instance of an exception from a PHP class type and a message.
64
    ///
65
    /// # Parameters
66
    ///
67
    /// * `message` - Message to contain in the exception.
68
    #[must_use]
69
    pub fn from_class<T: RegisteredClass>(message: String) -> Self {
×
70
        Self::new(message, 0, T::get_metadata().ce())
×
71
    }
72

73
    /// Set the Zval object for the exception.
74
    ///
75
    /// Exceptions can be based of instantiated Zval objects when you are
76
    /// throwing a custom exception with stateful properties.
77
    ///
78
    /// # Parameters
79
    ///
80
    /// * `object` - The Zval object.
81
    pub fn set_object(&mut self, object: Option<Zval>) {
×
82
        self.object = object;
×
83
    }
84

85
    /// Throws the exception, returning nothing inside a result if successful
86
    /// and an error otherwise.
87
    ///
88
    /// # Errors
89
    ///
90
    /// * [`Error::InvalidException`] - If the exception type is an interface or
91
    ///   abstract class.
92
    /// * If the message contains NUL bytes.
93
    pub fn throw(self) -> Result<()> {
×
94
        match self.object {
×
95
            Some(object) => throw_object(object),
×
96
            None => throw_with_code(self.ex, self.code, &self.message),
×
97
        }
98
    }
99
}
100

101
impl From<String> for PhpException {
102
    fn from(str: String) -> Self {
×
103
        Self::default(str)
×
104
    }
105
}
106

107
impl From<&str> for PhpException {
108
    fn from(str: &str) -> Self {
×
109
        Self::default(str.into())
×
110
    }
111
}
112

113
#[cfg(feature = "anyhow")]
114
impl From<anyhow::Error> for PhpException {
115
    fn from(err: anyhow::Error) -> Self {
×
NEW
116
        Self::new(format!("{err:#}"), 0, crate::zend::ce::exception())
×
117
    }
118
}
119

120
/// Throws an exception with a given message. See [`ClassEntry`] for some
121
/// built-in exception types.
122
///
123
/// Returns a result containing nothing if the exception was successfully
124
/// thrown.
125
///
126
/// # Parameters
127
///
128
/// * `ex` - The exception type to throw.
129
/// * `message` - The message to display when throwing the exception.
130
///
131
/// # Errors
132
///
133
/// * [`Error::InvalidException`] - If the exception type is an interface or
134
///   abstract class.
135
/// * If the message contains NUL bytes.
136
///
137
/// # Examples
138
///
139
/// ```no_run
140
/// use ext_php_rs::{zend::{ce, ClassEntry}, exception::throw};
141
///
142
/// throw(ce::compile_error(), "This is a CompileError.");
143
/// ```
144
pub fn throw(ex: &ClassEntry, message: &str) -> Result<()> {
×
145
    throw_with_code(ex, 0, message)
×
146
}
147

148
/// Throws an exception with a given message and status code. See [`ClassEntry`]
149
/// for some built-in exception types.
150
///
151
/// Returns a result containing nothing if the exception was successfully
152
/// thrown.
153
///
154
/// # Parameters
155
///
156
/// * `ex` - The exception type to throw.
157
/// * `code` - The status code to use when throwing the exception.
158
/// * `message` - The message to display when throwing the exception.
159
///
160
/// # Errors
161
///
162
/// * [`Error::InvalidException`] - If the exception type is an interface or
163
///   abstract class.
164
/// * If the message contains NUL bytes.
165
///
166
/// # Examples
167
///
168
/// ```no_run
169
/// use ext_php_rs::{zend::{ce, ClassEntry}, exception::throw_with_code};
170
///
171
/// throw_with_code(ce::compile_error(), 123, "This is a CompileError.");
172
/// ```
173
pub fn throw_with_code(ex: &ClassEntry, code: i32, message: &str) -> Result<()> {
×
174
    let flags = ex.flags();
×
175

176
    // Can't throw an interface or abstract class.
177
    if flags.contains(ClassFlags::Interface) || flags.contains(ClassFlags::Abstract) {
×
178
        return Err(Error::InvalidException(flags));
×
179
    }
180

181
    // SAFETY: We are given a reference to a `ClassEntry` therefore when we cast it
182
    // to a pointer it will be valid.
183
    unsafe {
184
        zend_throw_exception_ex(
NEW
185
            ptr::from_ref(ex).cast_mut(),
×
NEW
186
            code.into(),
×
187
            CString::new("%s")?.as_ptr(),
×
188
            CString::new(message)?.as_ptr(),
×
189
        )
190
    };
191
    Ok(())
×
192
}
193

194
/// Throws an exception object.
195
///
196
/// Returns a result containing nothing if the exception was successfully
197
/// thrown.
198
///
199
/// # Parameters
200
///
201
/// * `object` - The zval of type object
202
///
203
/// # Errors
204
///
205
/// *shrug*
206
/// TODO: does this error?
207
///
208
/// # Examples
209
///
210
/// ```no_run
211
/// use ext_php_rs::prelude::*;
212
/// use ext_php_rs::exception::throw_object;
213
/// use crate::ext_php_rs::convert::IntoZval;
214
///
215
/// #[php_class]
216
/// #[php(extends = ext_php_rs::zend::ce::exception)]
217
/// pub struct JsException {
218
///     #[php(prop, flags = ext_php_rs::flags::PropertyFlags::Public)]
219
///     message: String,
220
///     #[php(prop, flags = ext_php_rs::flags::PropertyFlags::Public)]
221
///     code: i32,
222
///     #[php(prop, flags = ext_php_rs::flags::PropertyFlags::Public)]
223
///     file: String,
224
/// }
225
///
226
/// #[php_module]
227
/// pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
228
///     module
229
/// }
230
///
231
/// let error = JsException { message: "A JS error occurred.".to_string(), code: 100, file: "index.js".to_string() };
232
/// throw_object( error.into_zval(true).unwrap() );
233
/// ```
234
pub fn throw_object(zval: Zval) -> Result<()> {
×
235
    let mut zv = core::mem::ManuallyDrop::new(zval);
×
236
    unsafe { zend_throw_exception_object(core::ptr::addr_of_mut!(zv).cast()) };
×
237
    Ok(())
×
238
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc