use crate::{
arrays::{ZArr, ZArray},
classes::ClassEntry,
errors::ExpectTypeError,
functions::{ZFunc, call_internal},
objects::{StateObject, ZObj, ZObject},
references::ZRef,
resources::ZRes,
strings::{ZStr, ZString},
sys::*,
types::TypeInfo,
};
use phper_alloc::RefClone;
use std::{
ffi::CStr,
fmt,
fmt::Debug,
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit, transmute, zeroed},
str,
};
#[repr(transparent)]
pub struct ExecuteData {
inner: zend_execute_data,
}
impl ExecuteData {
#[inline]
#[allow(dead_code)]
pub unsafe fn from_ptr<'a>(ptr: *const zend_execute_data) -> &'a Self {
unsafe { (ptr as *const Self).as_ref().expect("ptr should't be null") }
}
#[inline]
#[allow(dead_code)]
pub unsafe fn try_from_ptr<'a>(ptr: *const zend_execute_data) -> Option<&'a Self> {
unsafe { (ptr as *const Self).as_ref() }
}
#[inline]
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zend_execute_data) -> &'a mut Self {
unsafe { (ptr as *mut Self).as_mut().expect("ptr should't be null") }
}
#[inline]
#[allow(dead_code)]
pub unsafe fn try_from_mut_ptr<'a>(ptr: *mut zend_execute_data) -> Option<&'a mut Self> {
unsafe { (ptr as *mut Self).as_mut() }
}
pub const fn as_ptr(&self) -> *const zend_execute_data {
&self.inner
}
#[inline]
#[allow(dead_code)]
pub fn as_mut_ptr(&mut self) -> *mut zend_execute_data {
&mut self.inner
}
#[inline]
pub fn common_num_args(&self) -> u32 {
unsafe { (*self.inner.func).common.num_args }
}
#[inline]
pub fn common_required_num_args(&self) -> usize {
unsafe { (*self.inner.func).common.required_num_args as usize }
}
#[inline]
pub fn common_arg_info(&self) -> *mut zend_arg_info {
unsafe { (*self.inner.func).common.arg_info }
}
#[inline]
pub fn num_args(&self) -> usize {
unsafe { phper_zend_num_args(self.as_ptr()).try_into().unwrap() }
}
pub fn func(&self) -> &ZFunc {
unsafe { ZFunc::from_mut_ptr(self.inner.func) }
}
pub fn get_opline_lineno(&self) -> Option<u32> {
unsafe {
match u32::from((*self.inner.func).type_) {
ZEND_USER_FUNCTION | ZEND_EVAL_CODE => {
let opline = self.inner.opline;
if opline.is_null() {
None
} else {
Some((*opline).lineno)
}
}
_ => None,
}
}
}
pub fn get_return_value(&self) -> Option<&ZVal> {
unsafe {
let val = self.inner.return_value;
ZVal::try_from_ptr(val)
}
}
pub fn get_return_value_mut(&mut self) -> Option<&mut ZVal> {
unsafe {
let val = self.inner.return_value;
ZVal::try_from_mut_ptr(val)
}
}
pub fn get_return_value_mut_ptr(&mut self) -> *mut ZVal {
self.inner.return_value as *mut ZVal
}
pub fn get_return_value_ptr(&self) -> *const ZVal {
self.inner.return_value as *const ZVal
}
pub fn get_this(&mut self) -> Option<&ZObj> {
unsafe { ZVal::try_from_ptr(phper_get_this(&mut self.inner))?.as_z_obj() }
}
pub fn get_this_mut(&mut self) -> Option<&mut ZObj> {
unsafe { ZVal::try_from_mut_ptr(phper_get_this(&mut self.inner))?.as_mut_z_obj() }
}
pub fn get_called_scope(&mut self) -> Option<&ClassEntry> {
unsafe {
let ptr = phper_get_called_scope(&mut self.inner);
if ptr.is_null() {
None
} else {
Some(ClassEntry::from_ptr(ptr))
}
}
}
pub(crate) unsafe fn get_parameters_array(&mut self) -> Vec<ManuallyDrop<ZVal>> {
unsafe {
let num_args = self.num_args();
let mut arguments = vec![zeroed::<zval>(); num_args];
if num_args > 0 {
phper_zend_get_parameters_array_ex(
num_args.try_into().unwrap(),
arguments.as_mut_ptr(),
);
}
transmute(arguments)
}
}
pub fn get_parameter(&self, index: usize) -> &ZVal {
unsafe {
let val = phper_zend_call_var_num(self.as_ptr() as *mut _, index.try_into().unwrap());
ZVal::from_ptr(val)
}
}
pub fn get_mut_parameter(&mut self, index: usize) -> &mut ZVal {
unsafe {
let val = phper_zend_call_var_num(self.as_mut_ptr(), index.try_into().unwrap());
ZVal::from_mut_ptr(val)
}
}
}
#[repr(transparent)]
pub struct ZVal {
inner: zval,
_p: PhantomData<*mut ()>,
}
impl ZVal {
#[inline]
pub unsafe fn from_ptr<'a>(ptr: *const zval) -> &'a Self {
unsafe { (ptr as *const Self).as_ref().expect("ptr should't be null") }
}
#[inline]
pub unsafe fn try_from_ptr<'a>(ptr: *const zval) -> Option<&'a Self> {
unsafe { (ptr as *const Self).as_ref() }
}
#[inline]
pub unsafe fn from_mut_ptr<'a>(ptr: *mut zval) -> &'a mut Self {
unsafe { (ptr as *mut Self).as_mut().expect("ptr should't be null") }
}
#[inline]
pub unsafe fn try_from_mut_ptr<'a>(ptr: *mut zval) -> Option<&'a mut Self> {
unsafe { (ptr as *mut Self).as_mut() }
}
pub const fn as_ptr(&self) -> *const zval {
&self.inner
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut zval {
&mut self.inner
}
#[inline]
pub fn into_inner(self) -> zval {
self.inner
}
pub fn get_type_info(&self) -> TypeInfo {
let t = unsafe { phper_z_type_info_p(self.as_ptr()) };
t.into()
}
pub fn as_null(&self) -> Option<()> {
self.expect_null().ok()
}
pub fn expect_null(&self) -> crate::Result<()> {
if self.get_type_info().is_null() {
Ok(())
} else {
Err(ExpectTypeError::new(TypeInfo::NULL, self.get_type_info()).into())
}
}
pub fn as_bool(&self) -> Option<bool> {
self.expect_bool().ok()
}
pub fn expect_bool(&self) -> crate::Result<bool> {
let t = self.get_type_info();
if t.is_true() {
Ok(true)
} else if t.is_false() {
Ok(false)
} else {
Err(ExpectTypeError::new(TypeInfo::BOOL, self.get_type_info()).into())
}
}
pub fn as_long(&self) -> Option<i64> {
self.expect_long().ok()
}
pub fn expect_long(&self) -> crate::Result<i64> {
self.inner_expect_long().cloned()
}
pub fn as_mut_long(&mut self) -> Option<&mut i64> {
self.expect_mut_long().ok()
}
pub fn expect_mut_long(&mut self) -> crate::Result<&mut i64> {
self.inner_expect_long()
}
fn inner_expect_long(&self) -> crate::Result<&mut i64> {
if self.get_type_info().is_long() {
unsafe { Ok(phper_z_lval_p(self.as_ptr() as *mut _).as_mut().unwrap()) }
} else {
Err(ExpectTypeError::new(TypeInfo::LONG, self.get_type_info()).into())
}
}
pub fn as_double(&self) -> Option<f64> {
self.expect_double().ok()
}
pub fn expect_double(&self) -> crate::Result<f64> {
self.inner_expect_double().cloned()
}
pub fn as_mut_double(&mut self) -> Option<&mut f64> {
self.expect_mut_double().ok()
}
pub fn expect_mut_double(&mut self) -> crate::Result<&mut f64> {
self.inner_expect_double()
}
fn inner_expect_double(&self) -> crate::Result<&mut f64> {
if self.get_type_info().is_double() {
unsafe { Ok(phper_z_dval_p(self.as_ptr() as *mut _).as_mut().unwrap()) }
} else {
Err(ExpectTypeError::new(TypeInfo::DOUBLE, self.get_type_info()).into())
}
}
pub fn as_z_str(&self) -> Option<&ZStr> {
self.expect_z_str().ok()
}
pub fn expect_z_str(&self) -> crate::Result<&ZStr> {
self.inner_expect_z_str().map(|x| &*x)
}
pub fn as_mut_z_str(&mut self) -> Option<&mut ZStr> {
self.expect_mut_z_str().ok()
}
pub fn expect_mut_z_str(&mut self) -> crate::Result<&mut ZStr> {
self.inner_expect_z_str()
}
fn inner_expect_z_str(&self) -> crate::Result<&mut ZStr> {
if self.get_type_info().is_string() {
unsafe { Ok(ZStr::from_mut_ptr(phper_z_str_p(self.as_ptr()))) }
} else {
Err(ExpectTypeError::new(TypeInfo::STRING, self.get_type_info()).into())
}
}
pub fn as_z_arr(&self) -> Option<&ZArr> {
self.expect_z_arr().ok()
}
pub fn expect_z_arr(&self) -> crate::Result<&ZArr> {
self.inner_expect_z_arr().map(|x| &*x)
}
pub fn as_mut_z_arr(&mut self) -> Option<&mut ZArr> {
self.expect_mut_z_arr().ok()
}
pub fn expect_mut_z_arr(&mut self) -> crate::Result<&mut ZArr> {
self.inner_expect_z_arr()
}
fn inner_expect_z_arr(&self) -> crate::Result<&mut ZArr> {
if self.get_type_info().is_array() {
unsafe { Ok(ZArr::from_mut_ptr(phper_z_arr_p(self.as_ptr()))) }
} else {
Err(ExpectTypeError::new(TypeInfo::ARRAY, self.get_type_info()).into())
}
}
pub fn as_z_obj(&self) -> Option<&ZObj> {
self.expect_z_obj().ok()
}
pub fn expect_z_obj(&self) -> crate::Result<&ZObj> {
self.inner_expect_z_obj().map(|x| &*x)
}
pub fn as_mut_z_obj(&mut self) -> Option<&mut ZObj> {
self.expect_mut_z_obj().ok()
}
pub fn expect_mut_z_obj(&mut self) -> crate::Result<&mut ZObj> {
self.inner_expect_z_obj()
}
fn inner_expect_z_obj(&self) -> crate::Result<&mut ZObj> {
if self.get_type_info().is_object() {
unsafe {
let ptr = phper_z_obj_p(self.as_ptr());
Ok(ZObj::from_mut_ptr(ptr))
}
} else {
Err(ExpectTypeError::new(TypeInfo::OBJECT, self.get_type_info()).into())
}
}
pub fn as_z_res(&self) -> Option<&ZRes> {
self.expect_z_res().ok()
}
pub fn expect_z_res(&self) -> crate::Result<&ZRes> {
self.inner_expect_z_res().map(|x| &*x)
}
pub fn as_mut_z_res(&mut self) -> Option<&mut ZRes> {
self.expect_mut_z_res().ok()
}
pub fn expect_mut_z_res(&mut self) -> crate::Result<&mut ZRes> {
self.inner_expect_z_res()
}
fn inner_expect_z_res(&self) -> crate::Result<&mut ZRes> {
if self.get_type_info().is_resource() {
unsafe { Ok(ZRes::from_mut_ptr(phper_z_res_p(self.as_ptr()))) }
} else {
Err(ExpectTypeError::new(TypeInfo::RESOURCE, self.get_type_info()).into())
}
}
pub fn as_z_ref(&self) -> Option<&ZRef> {
self.expect_z_ref().ok()
}
pub fn expect_z_ref(&self) -> crate::Result<&ZRef> {
self.inner_expect_z_ref().map(|x| &*x)
}
pub fn as_mut_z_ref(&mut self) -> Option<&mut ZRef> {
self.expect_mut_z_ref().ok()
}
pub fn expect_mut_z_ref(&mut self) -> crate::Result<&mut ZRef> {
self.inner_expect_z_ref()
}
fn inner_expect_z_ref(&self) -> crate::Result<&mut ZRef> {
if self.get_type_info().is_reference() {
unsafe { Ok(ZRef::from_mut_ptr(phper_z_ref_p(self.as_ptr()))) }
} else {
Err(ExpectTypeError::new(TypeInfo::REFERENCE, self.get_type_info()).into())
}
}
pub fn convert_to_long(&mut self) {
unsafe {
phper_convert_to_long(self.as_mut_ptr());
}
}
pub fn convert_to_string(&mut self) {
unsafe {
phper_convert_to_string(self.as_mut_ptr());
}
}
#[inline]
pub fn call(&mut self, arguments: impl AsMut<[ZVal]>) -> crate::Result<ZVal> {
call_internal(self, None, arguments)
}
}
impl Debug for ZVal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[derive(Debug)]
#[allow(non_camel_case_types)]
struct null;
#[derive(Debug)]
#[allow(non_camel_case_types)]
struct unknown;
let mut d = f.debug_tuple("ZVal");
let t = self.get_type_info();
if t.is_null() {
d.field(&null);
} else if t.is_bool() {
if let Some(v) = self.as_bool() {
d.field(&v);
}
} else if t.is_long() {
if let Some(v) = self.as_long() {
d.field(&v);
}
} else if t.is_double() {
if let Some(v) = self.as_double() {
d.field(&v);
}
} else if t.is_string() {
if let Some(v) = self.as_z_str() {
d.field(&v);
}
} else if t.is_array() {
if let Some(v) = self.as_z_arr() {
d.field(&v);
}
} else if t.is_object() {
if let Some(v) = self.as_z_obj() {
d.field(&v);
}
} else if t.is_resource() {
if let Some(v) = self.as_z_res() {
d.field(&v);
}
} else if t.is_reference() {
if let Some(v) = self.as_z_ref() {
d.field(&v);
}
} else {
d.field(&unknown);
}
d.finish()
}
}
impl Default for ZVal {
#[inline]
fn default() -> Self {
ZVal::from(())
}
}
impl Clone for ZVal {
fn clone(&self) -> Self {
let mut val = ZVal::default();
unsafe {
phper_zval_copy(val.as_mut_ptr(), self.as_ptr());
if val.get_type_info().is_string() {
phper_separate_string(val.as_mut_ptr());
} else if val.get_type_info().is_array() {
phper_separate_array(val.as_mut_ptr());
}
}
val
}
}
impl RefClone for ZVal {
fn ref_clone(&mut self) -> Self {
let mut val = ZVal::default();
unsafe {
phper_zval_copy(val.as_mut_ptr(), self.as_ptr());
}
val
}
}
impl Drop for ZVal {
fn drop(&mut self) {
unsafe {
phper_zval_ptr_dtor(self.as_mut_ptr());
}
}
}
impl From<()> for ZVal {
fn from(_: ()) -> Self {
unsafe {
let mut val = MaybeUninit::<ZVal>::uninit();
phper_zval_null(val.as_mut_ptr().cast());
val.assume_init()
}
}
}
impl From<bool> for ZVal {
fn from(b: bool) -> Self {
unsafe {
let mut val = MaybeUninit::<ZVal>::uninit();
if b {
phper_zval_true(val.as_mut_ptr().cast());
} else {
phper_zval_false(val.as_mut_ptr().cast());
}
val.assume_init()
}
}
}
impl From<i64> for ZVal {
#[allow(clippy::useless_conversion)]
fn from(i: i64) -> Self {
unsafe {
let mut val = MaybeUninit::<ZVal>::uninit();
phper_zval_long(val.as_mut_ptr().cast(), i.try_into().unwrap());
val.assume_init()
}
}
}
impl From<f64> for ZVal {
#[allow(clippy::useless_conversion)]
fn from(f: f64) -> Self {
unsafe {
let mut val = MaybeUninit::<ZVal>::uninit();
phper_zval_double(val.as_mut_ptr().cast(), f.try_into().unwrap());
val.assume_init()
}
}
}
impl From<&[u8]> for ZVal {
#[allow(clippy::useless_conversion)]
fn from(b: &[u8]) -> Self {
unsafe {
let mut val = MaybeUninit::<ZVal>::uninit();
phper_zval_stringl(
val.as_mut_ptr().cast(),
b.as_ptr().cast(),
b.len().try_into().unwrap(),
);
val.assume_init()
}
}
}
impl From<Vec<u8>> for ZVal {
fn from(b: Vec<u8>) -> Self {
ZVal::from(&b[..])
}
}
impl From<&str> for ZVal {
fn from(s: &str) -> Self {
ZVal::from(s.as_bytes())
}
}
impl From<&CStr> for ZVal {
fn from(s: &CStr) -> Self {
ZVal::from(s.to_bytes())
}
}
impl From<String> for ZVal {
fn from(s: String) -> Self {
ZVal::from(s.as_bytes())
}
}
impl From<ZString> for ZVal {
fn from(s: ZString) -> Self {
unsafe {
let mut val = MaybeUninit::<ZVal>::uninit();
phper_zval_str(val.as_mut_ptr().cast(), ZString::into_raw_cast(s));
val.assume_init()
}
}
}
impl From<ZArray> for ZVal {
fn from(arr: ZArray) -> Self {
unsafe {
let mut val = MaybeUninit::<ZVal>::uninit();
phper_zval_arr(val.as_mut_ptr().cast(), ZArray::into_raw_cast(arr));
val.assume_init()
}
}
}
impl From<ZObject> for ZVal {
fn from(obj: ZObject) -> Self {
unsafe {
let mut val = MaybeUninit::<ZVal>::uninit();
phper_zval_obj(val.as_mut_ptr().cast(), ZObject::into_raw(obj).cast());
val.assume_init()
}
}
}
impl<T> From<StateObject<T>> for ZVal {
fn from(obj: StateObject<T>) -> Self {
ZVal::from(obj.into_z_object())
}
}
impl<T: Into<ZVal>> From<Option<T>> for ZVal {
fn from(o: Option<T>) -> Self {
match o {
Some(t) => t.into(),
None => ().into(),
}
}
}