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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
use crate::ffi::{zend_execute_data, ZEND_MM_ALIGNMENT, ZEND_MM_ALIGNMENT_MASK};
use crate::{
args::ArgParser,
class::RegisteredClass,
types::{ZendClassObject, ZendObject, Zval},
};
/// Execute data passed when a function is called from PHP.
///
/// This generally contains things related to the call, including but not
/// limited to:
///
/// * Arguments
/// * `$this` object reference
/// * Reference to return value
/// * Previous execute data
pub type ExecuteData = zend_execute_data;
impl ExecuteData {
/// Returns an [`ArgParser`] pre-loaded with the arguments contained inside
/// `self`.
///
/// # Example
///
/// ```no_run
/// use ext_php_rs::{types::Zval, zend::ExecuteData, args::Arg, flags::DataType};
///
/// #[no_mangle]
/// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) {
/// let mut a = Arg::new("a", DataType::Long);
///
/// // The `parse_args!()` macro can be used for this.
/// let parser = ex.parser()
/// .arg(&mut a)
/// .parse();
///
/// if parser.is_err() {
/// return;
/// }
///
/// dbg!(a);
/// }
/// ```
pub fn parser<'a>(&'a mut self) -> ArgParser<'a, '_> {
self.parser_object().0
}
/// Returns an [`ArgParser`] pre-loaded with the arguments contained inside
/// `self`.
///
/// A reference to `$this` is also returned in an [`Option`], which resolves
/// to [`None`] if this function is not called inside a method.
///
/// # Example
///
/// ```no_run
/// use ext_php_rs::{types::Zval, zend::ExecuteData, args::Arg, flags::DataType};
///
/// #[no_mangle]
/// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) {
/// let mut a = Arg::new("a", DataType::Long);
///
/// let (parser, this) = ex.parser_object();
/// let parser = parser
/// .arg(&mut a)
/// .parse();
///
/// if parser.is_err() {
/// return;
/// }
///
/// dbg!(a, this);
/// }
/// ```
pub fn parser_object<'a>(&'a mut self) -> (ArgParser<'a, '_>, Option<&'a mut ZendObject>) {
// SAFETY: All fields of the `u2` union are the same type.
let n_args = unsafe { self.This.u2.num_args };
let mut args = vec![];
for i in 0..n_args {
// SAFETY: Function definition ensures arg lifetime doesn't exceed execution
// data lifetime.
let arg = unsafe { self.zend_call_arg(i as usize) };
args.push(arg);
}
let obj = self.This.object_mut();
(ArgParser::new(args), obj)
}
/// Returns an [`ArgParser`] pre-loaded with the arguments contained inside
/// `self`.
///
/// A reference to `$this` is also returned in an [`Option`], which resolves
/// to [`None`] if this function is not called inside a method.
///
/// This function differs from [`parse_object`] in the fact that it returns
/// a reference to a [`ZendClassObject`], which is an object that
/// contains an arbitrary Rust type at the start of the object. The
/// object will also resolve to [`None`] if the function is called
/// inside a method that does not belong to an object with type `T`.
///
/// # Example
///
/// ```no_run
/// use ext_php_rs::{types::Zval, zend::ExecuteData, args::Arg, flags::DataType, prelude::*};
///
/// #[php_class]
/// #[derive(Debug)]
/// struct Example;
///
/// #[no_mangle]
/// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) {
/// let mut a = Arg::new("a", DataType::Long);
///
/// let (parser, this) = ex.parser_method::<Example>();
/// let parser = parser
/// .arg(&mut a)
/// .parse();
///
/// if parser.is_err() {
/// return;
/// }
///
/// dbg!(a, this);
/// }
///
/// #[php_module]
/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
/// module
/// }
/// ```
///
/// [`parse_object`]: #method.parse_object
pub fn parser_method<'a, T: RegisteredClass>(
&'a mut self,
) -> (ArgParser<'a, '_>, Option<&'a mut ZendClassObject<T>>) {
let (parser, obj) = self.parser_object();
(
parser,
obj.and_then(|obj| ZendClassObject::from_zend_obj_mut(obj)),
)
}
/// Attempts to retrieve a reference to the underlying class object of the
/// Zend object.
///
/// Returns a [`ZendClassObject`] if the execution data contained a valid
/// object of type `T`, otherwise returns [`None`].
///
/// # Example
///
/// ```no_run
/// use ext_php_rs::{types::Zval, zend::ExecuteData, prelude::*};
///
/// #[php_class]
/// #[derive(Debug)]
/// struct Example;
///
/// #[no_mangle]
/// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) {
/// let this = ex.get_object::<Example>();
/// dbg!(this);
/// }
///
/// #[php_module]
/// pub fn module(module: ModuleBuilder) -> ModuleBuilder {
/// module
/// }
/// ```
pub fn get_object<T: RegisteredClass>(&mut self) -> Option<&mut ZendClassObject<T>> {
ZendClassObject::from_zend_obj_mut(self.get_self()?)
}
/// Attempts to retrieve the 'this' object, which can be used in class
/// methods to retrieve the underlying Zend object.
///
/// # Example
///
/// ```no_run
/// use ext_php_rs::{types::Zval, zend::ExecuteData};
///
/// #[no_mangle]
/// pub extern "C" fn example_fn(ex: &mut ExecuteData, retval: &mut Zval) {
/// let this = ex.get_self();
/// dbg!(this);
/// }
/// ```
pub fn get_self(&mut self) -> Option<&mut ZendObject> {
// TODO(david): This should be a `&mut self` function but we need to fix arg
// parser first.
self.This.object_mut()
}
/// Translation of macro `ZEND_CALL_ARG(call, n)`
/// zend_compile.h:578
///
/// The resultant zval reference has a lifetime equal to the lifetime of
/// `self`. This isn't specified because when you attempt to get a
/// reference to args and the `$this` object, Rust doesnt't let you.
/// Since this is a private method it's up to the caller to ensure the
/// lifetime isn't exceeded.
#[doc(hidden)]
unsafe fn zend_call_arg<'a>(&self, n: usize) -> Option<&'a mut Zval> {
let ptr = self.zend_call_var_num(n as isize);
ptr.as_mut()
}
/// Translation of macro `ZEND_CALL_VAR_NUM(call, n)`
/// zend_compile.h: 575
#[doc(hidden)]
unsafe fn zend_call_var_num(&self, n: isize) -> *mut Zval {
let ptr = self as *const Self as *mut Zval;
ptr.offset(Self::zend_call_frame_slot() + n as isize)
}
/// Translation of macro `ZEND_CALL_FRAME_SLOT`
/// zend_compile:573
#[doc(hidden)]
fn zend_call_frame_slot() -> isize {
(Self::zend_mm_aligned_size::<Self>() + Self::zend_mm_aligned_size::<Zval>() - 1)
/ Self::zend_mm_aligned_size::<Zval>()
}
/// Translation of macro `ZEND_MM_ALIGNED_SIZE(size)`
/// zend_alloc.h:41
#[doc(hidden)]
fn zend_mm_aligned_size<T>() -> isize {
let size = std::mem::size_of::<T>();
((size as isize) + ZEND_MM_ALIGNMENT as isize - 1) & ZEND_MM_ALIGNMENT_MASK as isize
}
}
#[cfg(test)]
mod tests {
use super::ExecuteData;
#[test]
fn test_zend_call_frame_slot() {
// PHP 8.0.2 (cli) (built: Feb 21 2021 11:51:33) ( NTS )
// Copyright (c) The PHP Group
// Zend Engine v4.0.2, Copyright (c) Zend Technologies
assert_eq!(ExecuteData::zend_call_frame_slot(), 5);
}
}