Skip to content

Add wrapper for zend_exec_stringl with exception handling #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 7 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
//! 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.
pub type Result<T> = std::result::Result<T, Error>;

/// 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)]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, what's the benefit of having ordering on an error enum?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparison.

#[non_exhaustive]
pub enum Error {
/// An incorrect number of arguments was given to a PHP function.
Expand Down Expand Up @@ -50,6 +51,8 @@ pub enum Error {
InvalidException(ClassFlags),
/// Converting integer arguments resulted in an overflow.
IntegerOverflow,
CompileFailed,
UncaughtException(Rc<Box<ZendObject>>),
}

impl Display for Error {
Expand Down Expand Up @@ -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),
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions src/php/eval.rs
Original file line number Diff line number Diff line change
@@ -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<Zval> {
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)
}
}
23 changes: 22 additions & 1 deletion src/php/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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")
Comment on lines +23 to +24
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept this private because I don't know in general if this is thread safe. As I understand it, the take_exception function should be safe.

}

/// Attempts to retrieve the global class hash table.
pub fn class_table(&self) -> Option<ZendHashTable> {
if self.class_table.is_null() {
Expand All @@ -24,4 +32,17 @@ impl ExecutorGlobals {

unsafe { ZendHashTable::from_ptr(self.class_table, false) }.ok()
}

pub fn take_exception() -> Option<Box<ZendObject>> {
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) })
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand the php source code, we're responsible for freeing it if we take it.

} else {
None
}
}
}
1 change: 1 addition & 0 deletions src/php/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ pub mod globals;
pub mod module;
pub mod pack;
pub mod types;
pub mod eval;
7 changes: 7 additions & 0 deletions src/php/types/zval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@ impl<'a> Zval {
pub(crate) fn release(mut self) {
self.u1.type_info = ZvalTypeFlags::Null.bits();
}

pub fn extract<T>(self) -> Result<T>
where
T: TryFrom<Self, Error = Error>,
{
T::try_from(self)
}
}

impl Debug for Zval {
Expand Down