Skip to content

Commit

Permalink
feat(embed): add documentation, manage potential errors
Browse files Browse the repository at this point in the history
  • Loading branch information
joelwurtz committed Oct 20, 2023
1 parent 8ed54e1 commit 23864ad
Showing 1 changed file with 94 additions and 7 deletions.
101 changes: 94 additions & 7 deletions src/embed/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! Provides implementations for running php code from rust.
//! It only works on linux for now and you should have `php-embed` installed
//!
//! This crate was only test with PHP 8.2 please report any issue with other version
//! You should only use this crate for test purpose, it's not production ready
mod ffi;

use crate::boxed::ZBox;
Expand All @@ -9,7 +15,7 @@ use crate::ffi::{
use crate::types::{ZendObject, Zval};
use crate::zend::ExecutorGlobals;
use parking_lot::{const_rwlock, RwLock};
use std::ffi::{c_char, c_void, CString};
use std::ffi::{c_char, c_void, CString, NulError};
use std::path::Path;
use std::ptr::null_mut;

Expand All @@ -19,13 +25,44 @@ pub struct Embed;
pub enum EmbedError {
InitError,
ExecuteError(Option<ZBox<ZendObject>>),
ExecuteScriptError,
InvalidEvalString(NulError),
InvalidPath,
}

static RUN_FN_LOCK: RwLock<()> = const_rwlock(());

impl Embed {
pub fn run_script<P: AsRef<Path>>(path: P) -> bool {
let path = CString::new(path.as_ref().to_str().unwrap()).unwrap();
/// Run a php script from a file
///
/// This function will only work correctly when used inside the `Embed::run` function
/// otherwise behavior is unexpected
///
/// # Returns
///
/// * `Ok(())` - The script was executed successfully
/// * `Err(EmbedError)` - An error occured during the execution of the script
///
/// # Example
///
/// ```
/// use ext_php_rs::embed::Embed;
///
/// Embed::run(|| {
/// let result = Embed::run_script("src/embed/test-script.php");
///
/// assert!(result.is_ok());
/// });
/// ```
pub fn run_script<P: AsRef<Path>>(path: P) -> Result<(), EmbedError> {
let path = match path.as_ref().to_str() {
Some(path) => match CString::new(path) {
Ok(path) => path,
Err(err) => return Err(EmbedError::InvalidEvalString(err)),
},
None => return Err(EmbedError::InvalidPath),
};

let mut file_handle = zend_file_handle {
handle: _zend_file_handle__bindgen_ty_1 { fp: null_mut() },
filename: null_mut(),
Expand All @@ -41,9 +78,32 @@ impl Embed {
zend_stream_init_filename(&mut file_handle, path.as_ptr());
}

unsafe { php_execute_script(&mut file_handle) }
if unsafe { php_execute_script(&mut file_handle) } {
Ok(())
} else {
Err(EmbedError::ExecuteScriptError)
}
}

/// Start and run embed sapi engine
///
/// This function will allow to run php code from rust, the same PHP context is keep between calls
/// inside the function passed to this method.
/// Which means subsequent calls to `Embed::eval` or `Embed::run_script` will be able to access
/// variables defined in previous calls
///
/// # Example
///
/// ```
/// use ext_php_rs::embed::Embed;
///
/// Embed::run(|| {
/// let _ = Embed::eval("$foo = 'foo';");
/// let foo = Embed::eval("$foo;");
/// assert!(foo.is_ok());
/// assert_eq!(foo.unwrap().string().unwrap(), "foo");
/// });
/// ```
pub fn run<F: Fn()>(func: F) {
// @TODO handle php thread safe
//
Expand All @@ -65,8 +125,35 @@ impl Embed {
}
}

/// Evaluate a php code
///
/// This function will only work correctly when used inside the `Embed::run` function
///
/// # Returns
///
/// * `Ok(Zval)` - The result of the evaluation
/// * `Err(EmbedError)` - An error occured during the evaluation
///
/// # Example
///
/// ```
/// use ext_php_rs::embed::Embed;
///
/// Embed::run(|| {
/// let foo = Embed::eval("$foo = 'foo';");
/// assert!(foo.is_ok());
/// });
/// ```
///
/// # Panics
///
/// This function will panic if the code cannot be converted to a CString (because it contains null bytes)
pub fn eval(code: &str) -> Result<Zval, EmbedError> {
let cstr = CString::new(code).unwrap();
let cstr = match CString::new(code) {
Ok(cstr) => cstr,
Err(err) => return Err(EmbedError::InvalidEvalString(err)),
};

let mut result = Zval::new();

// this eval is very limited as it only allow simple code, it's the same eval used by php -r
Expand Down Expand Up @@ -115,7 +202,7 @@ mod tests {
Embed::run(|| {
let result = Embed::run_script("src/embed/test-script.php");

assert!(result);
assert!(result.is_ok());

let zval = Embed::eval("$foo;").unwrap();

Expand All @@ -132,7 +219,7 @@ mod tests {
Embed::run(|| {
let result = Embed::run_script("src/embed/test-script-exception.php");

assert!(!result);
assert!(!result.is_ok());
});
}
}

0 comments on commit 23864ad

Please sign in to comment.