diff --git a/build.rs b/build.rs index e9ae547a7f..09c8a046da 100644 --- a/build.rs +++ b/build.rs @@ -191,6 +191,7 @@ const ALLOWED_BINDINGS: &[&str] = &[ "zend_declare_property", "zend_do_implement_interface", "zend_execute_data", + "zend_eval_stringl", "zend_function_entry", "zend_hash_clean", "zend_hash_index_del", diff --git a/src/errors.rs b/src/errors.rs index fab42ce892..16306ced93 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,11 +1,12 @@ //! Error and result types returned from the library functions. -use std::{error::Error as ErrorTrait, ffi::NulError, fmt::Display}; +use std::{error::Error as ErrorTrait, ffi::NulError, fmt::Display, rc::Rc}; use crate::php::{ enums::DataType, exceptions::PhpException, flags::{ClassFlags, ZvalTypeFlags}, + types::object::ZendObject, }; /// The main result type which is passed by the library. @@ -13,7 +14,7 @@ pub type Result = std::result::Result; /// The main error type which is passed by the library inside the custom /// [`Result`] type. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone)] #[non_exhaustive] pub enum Error { /// An incorrect number of arguments was given to a PHP function. @@ -50,6 +51,8 @@ pub enum Error { InvalidException(ClassFlags), /// Converting integer arguments resulted in an overflow. IntegerOverflow, + CompileFailed, + UncaughtException(Rc>), } impl Display for Error { @@ -83,6 +86,8 @@ impl Display for Error { Error::IntegerOverflow => { write!(f, "Converting integer arguments resulted in an overflow.") } + Error::CompileFailed => write!(f, "Syntax error."), + Error::UncaughtException(exception) => write!(f, "Uncaught Exception: {:?}", exception), } } } diff --git a/src/php/eval.rs b/src/php/eval.rs new file mode 100644 index 0000000000..113907d050 --- /dev/null +++ b/src/php/eval.rs @@ -0,0 +1,28 @@ +use crate::bindings::zend_eval_stringl; +use crate::errors::{Error, Result}; +use crate::php::globals::ExecutorGlobals; +use crate::php::types::zval::Zval; +use std::ffi::CString; +use std::rc::Rc; + +pub fn eval(code: &str) -> Result { + let mut ret = Zval::new(); + let code = CString::new(code)?; + + let result = unsafe { + zend_eval_stringl( + code.as_ptr(), + code.as_bytes().len() as u64, + &mut ret, + CString::new("")?.as_ptr(), + ) + }; + + if result < 0 { + Err(Error::CompileFailed) + } else if let Some(exception) = ExecutorGlobals::take_exception() { + Err(Error::UncaughtException(Rc::new(exception))) + } else { + Ok(ret) + } +} diff --git a/src/php/globals.rs b/src/php/globals.rs index 2c00fd8e05..c22535bf4f 100644 --- a/src/php/globals.rs +++ b/src/php/globals.rs @@ -2,7 +2,7 @@ use crate::bindings::{_zend_executor_globals, ext_php_rs_executor_globals}; -use super::types::array::ZendHashTable; +use super::types::{array::ZendHashTable, object::ZendObject}; /// Stores global variables used in the PHP executor. pub type ExecutorGlobals = _zend_executor_globals; @@ -16,6 +16,14 @@ impl ExecutorGlobals { .expect("Static executor globals were invalid") } + fn get_mut() -> &'static mut Self { + // SAFETY: PHP executor globals are statically declared therefore should never + // return an invalid pointer. + // TODO: Should this be syncronized? + unsafe { ext_php_rs_executor_globals().as_mut() } + .expect("Static executor globals were invalid") + } + /// Attempts to retrieve the global class hash table. pub fn class_table(&self) -> Option { if self.class_table.is_null() { @@ -24,4 +32,17 @@ impl ExecutorGlobals { unsafe { ZendHashTable::from_ptr(self.class_table, false) }.ok() } + + pub fn take_exception() -> Option> { + let globals = Self::get_mut(); + + let mut exception_ptr = std::ptr::null_mut(); + std::mem::swap(&mut exception_ptr, &mut globals.exception); + + if !exception_ptr.is_null() { + Some(unsafe { Box::from_raw(exception_ptr) }) + } else { + None + } + } } diff --git a/src/php/mod.rs b/src/php/mod.rs index da5c82faa9..1f5783983e 100644 --- a/src/php/mod.rs +++ b/src/php/mod.rs @@ -16,3 +16,4 @@ pub mod globals; pub mod module; pub mod pack; pub mod types; +pub mod eval; diff --git a/src/php/types/zval.rs b/src/php/types/zval.rs index 9d5ce443e1..f2c4ecd274 100644 --- a/src/php/types/zval.rs +++ b/src/php/types/zval.rs @@ -371,6 +371,13 @@ impl<'a> Zval { pub(crate) fn release(mut self) { self.u1.type_info = ZvalTypeFlags::Null.bits(); } + + pub fn extract(self) -> Result + where + T: TryFrom, + { + T::try_from(self) + } } impl Debug for Zval {