ext_php_rs/
error.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Error and result types returned from the library functions.

use std::{
    error::Error as ErrorTrait,
    ffi::{CString, NulError},
    fmt::Display,
};

use crate::{
    boxed::ZBox,
    exception::PhpException,
    ffi::php_error_docref,
    flags::{ClassFlags, DataType, ErrorType, ZvalTypeFlags},
    types::ZendObject,
};

/// The main result type which is passed by the library.
pub type Result<T, E = Error> = std::result::Result<T, E>;

/// The main error type which is passed by the library inside the custom
/// [`Result`] type.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
    /// An incorrect number of arguments was given to a PHP function.
    ///
    /// The enum carries two integers - the first representing the minimum
    /// number of arguments expected, and the second representing the number of
    /// arguments that were received.
    IncorrectArguments(usize, usize),
    /// There was an error converting a Zval into a primitive type.
    ///
    /// The enum carries the data type of the Zval.
    ZvalConversion(DataType),
    /// The type of the Zval is unknown.
    ///
    /// The enum carries the integer representation of the type of Zval.
    UnknownDatatype(u32),
    /// Attempted to convert a [`ZvalTypeFlags`] struct to a [`DataType`].
    /// The flags did not contain a datatype.
    ///
    /// The enum carries the flags that were attempted to be converted to a
    /// [`DataType`].
    InvalidTypeToDatatype(ZvalTypeFlags),
    /// The function called was called in an invalid scope (calling
    /// class-related functions inside of a non-class bound function).
    InvalidScope,
    /// The pointer inside a given type was invalid, either null or pointing to
    /// garbage.
    InvalidPointer,
    /// The given property name does not exist.
    InvalidProperty,
    /// The string could not be converted into a C-string due to the presence of
    /// a NUL character.
    InvalidCString,
    /// The string could not be converted into a valid Utf8 string
    InvalidUtf8,
    /// Could not call the given function.
    Callable,
    /// An object was expected.
    Object,
    /// An invalid exception type was thrown.
    InvalidException(ClassFlags),
    /// Converting integer arguments resulted in an overflow.
    IntegerOverflow,
    /// An exception was thrown in a function.
    Exception(ZBox<ZendObject>),
    /// A failure occurred while registering the stream wrapper
    StreamWrapperRegistrationFailure,
    /// A failure occurred while unregistering the stream wrapper
    StreamWrapperUnregistrationFailure,
}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::IncorrectArguments(n, expected) => write!(
                f,
                "Expected at least {expected} arguments, got {n} arguments."
            ),
            Error::ZvalConversion(ty) => write!(
                f,
                "Could not convert Zval from type {ty} into primitive type."
            ),
            Error::UnknownDatatype(dt) => write!(f, "Unknown datatype {dt}."),
            Error::InvalidTypeToDatatype(dt) => {
                write!(f, "Type flags did not contain a datatype: {dt:?}")
            }
            Error::InvalidScope => write!(f, "Invalid scope."),
            Error::InvalidPointer => write!(f, "Invalid pointer."),
            Error::InvalidProperty => write!(f, "Property does not exist on object."),
            Error::InvalidCString => write!(
                f,
                "String given contains NUL-bytes which cannot be present in a C string."
            ),
            Error::InvalidUtf8 => write!(f, "Invalid Utf8 byte sequence."),
            Error::Callable => write!(f, "Could not call given function."),
            Error::Object => write!(f, "An object was expected."),
            Error::InvalidException(flags) => {
                write!(f, "Invalid exception type was thrown: {flags:?}")
            }
            Error::IntegerOverflow => {
                write!(f, "Converting integer arguments resulted in an overflow.")
            }
            Error::Exception(e) => write!(f, "Exception was thrown: {e:?}"),
            Error::StreamWrapperRegistrationFailure => {
                write!(f, "A failure occurred while registering the stream wrapper")
            }
            Error::StreamWrapperUnregistrationFailure => {
                write!(
                    f,
                    "A failure occurred while unregistering the stream wrapper"
                )
            }
        }
    }
}

impl ErrorTrait for Error {}

impl From<NulError> for Error {
    fn from(_: NulError) -> Self {
        Self::InvalidCString
    }
}

impl From<Error> for PhpException {
    fn from(err: Error) -> Self {
        Self::default(err.to_string())
    }
}

/// Trigger an error that is reported in PHP the same way `trigger_error()` is.
///
/// See specific error type descriptions at <https://www.php.net/manual/en/errorfunc.constants.php>.
pub fn php_error(type_: ErrorType, message: &str) {
    let c_string = match CString::new(message) {
        Ok(string) => string,
        Err(_) => {
            return;
        }
    };

    unsafe { php_error_docref(std::ptr::null(), type_.bits() as _, c_string.as_ptr()) }
}