Skip to content
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

feat(embed): add embed features, add test example which run php inside it #270

Merged
merged 6 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
15 changes: 15 additions & 0 deletions .github/actions/embed/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM php:8.2-bullseye

WORKDIR /tmp

RUN apt update -y && apt upgrade -y
RUN apt install lsb-release wget gnupg software-properties-common -y
RUN bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"

ENV RUSTUP_HOME=/rust
ENV CARGO_HOME=/cargo
ENV PATH=/cargo/bin:/rust/bin:$PATH

RUN (curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly --no-modify-path) && rustup default nightly
joelwurtz marked this conversation as resolved.
Show resolved Hide resolved

ENTRYPOINT [ "/cargo/bin/cargo", "test", "--lib", "--release", "--all-features" ]
5 changes: 5 additions & 0 deletions .github/actions/embed/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: 'PHP Embed and Rust'
description: 'Builds the crate after installing the latest PHP with php embed and stable Rust.'
runs:
using: 'docker'
image: 'Dockerfile'
12 changes: 10 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ jobs:
- name: Build
env:
EXT_PHP_RS_TEST: ""
run: cargo build --release --all-features --all
run: cargo build --release --features closure,anyhow --all
# Test & lint
- name: Test inline examples
run: cargo test --release --all --all-features
run: cargo test --release --all --features closure,anyhow
- name: Run rustfmt
if: matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' && matrix.php == '8.2'
run: cargo fmt --all -- --check
Expand All @@ -110,3 +110,11 @@ jobs:
uses: actions/checkout@v3
- name: Build
uses: ./.github/actions/zts
test-embed:
name: Test with embed
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Test
uses: ./.github/actions/embed
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ zip = "0.6"

[features]
closure = []
embed = []
joelwurtz marked this conversation as resolved.
Show resolved Hide resolved

[workspace]
members = [
Expand Down
5 changes: 4 additions & 1 deletion allowed_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,8 @@ bind! {
tsrm_get_ls_cache,
executor_globals_offset,
zend_atomic_bool_store,
zend_interrupt_function
zend_interrupt_function,
php_embed_init,
php_embed_shutdown,
zend_eval_stringl
}
18 changes: 18 additions & 0 deletions src/types/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,21 @@ impl<'a> FromZval<'a> for &'a str {
zval.str()
}
}

#[cfg(test)]
#[cfg(feature = "embed")]
mod tests {
use crate::zend::embed::Embed;

#[test]
fn test_string() {
let result = Embed::run("'foo';");

assert!(result.is_ok());

let zval = result.as_ref().unwrap();

assert!(zval.is_string());
assert_eq!(zval.string().unwrap(), "foo");
}
}
1 change: 1 addition & 0 deletions src/wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "zend_inheritance.h"
#include "zend_interfaces.h"
#include "zend_ini.h"
#include "sapi/embed/php_embed.h"

zend_string *ext_php_rs_zend_string_init(const char *str, size_t len, bool persistent);
void ext_php_rs_zend_string_release(zend_string *zs);
Expand Down
119 changes: 119 additions & 0 deletions src/zend/embed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use crate::boxed::ZBox;
use crate::ffi::{php_embed_init, php_embed_shutdown, zend_eval_stringl, ZEND_RESULT_CODE_SUCCESS};
use crate::types::{ZendObject, Zval};
use crate::zend::ExecutorGlobals;
use parking_lot::{const_rwlock, RwLock, RwLockWriteGuard};
use std::ffi::{c_char, CString};
use std::ops::Deref;
use std::ptr::null_mut;

pub struct Embed;

#[derive(Debug)]
pub enum EmbedError {
InitError,
ExecuteError(Option<ZBox<ZendObject>>),
}

static EMBED_LOCK: RwLock<bool> = const_rwlock(false);

pub struct EmbedWriteGuard<'a, T> {
guard: RwLockWriteGuard<'a, bool>,
data: T,
}

impl<'a, T> Deref for EmbedWriteGuard<'a, T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.data
}
}

impl Embed {
fn init<'a>() -> EmbedWriteGuard<'a, Result<(), EmbedError>> {
let mut write_guard = EMBED_LOCK.write();

if !*write_guard {
let result = unsafe { php_embed_init(0, null_mut()) };

if result != ZEND_RESULT_CODE_SUCCESS {
return EmbedWriteGuard {
guard: write_guard,
data: Err(EmbedError::InitError),
};
}

*write_guard = true
}

return EmbedWriteGuard {
guard: write_guard,
data: Ok(()),
};
}

// We use a write guard, this should only be called once at a time in non thread safe
// In the future we may not need this when it's linked to a thread safe php
pub fn run<'a>(code: &str) -> EmbedWriteGuard<'a, Result<Zval, EmbedError>> {
joelwurtz marked this conversation as resolved.
Show resolved Hide resolved
let guard = Self::init();

if guard.data.is_err() {
return EmbedWriteGuard {
guard: guard.guard,
data: Err(EmbedError::InitError),
};
}

let cstr = CString::new(code).unwrap();
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
let exec_result = unsafe {
zend_eval_stringl(
cstr.as_ptr() as *const c_char,
code.len() as _,
&mut result,
b"run\0".as_ptr() as *const _,
)
};

let exception = ExecutorGlobals::take_exception();

EmbedWriteGuard {
guard: guard.guard,
data: if exec_result != ZEND_RESULT_CODE_SUCCESS {
Err(EmbedError::ExecuteError(exception))
} else {
Ok(result)
},
}
}
}

impl Drop for Embed {
fn drop(&mut self) {
unsafe {
php_embed_shutdown();
}
}
}

#[cfg(test)]
mod tests {
use super::Embed;

#[test]
fn test_run() {
let result = Embed::run("'foo';");

assert!(result.is_ok());
}

#[test]
fn test_run_error() {
let result = Embed::run("stupid code");

assert!(!result.is_ok());
}
}
2 changes: 2 additions & 0 deletions src/zend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
mod _type;
pub mod ce;
mod class;
#[cfg(feature = "embed")]
pub mod embed;
mod ex;
mod function;
mod globals;
Expand Down
7 changes: 7 additions & 0 deletions unix_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,11 @@ impl<'a> PHPProvider<'a> for Provider {
fn get_defines(&self) -> Result<Vec<(&'static str, &'static str)>> {
Ok(vec![])
}

fn print_extra_link_args(&self) -> Result<()> {
#[cfg(feature = "embed")]
println!("cargo:rustc-link-lib=php");

Ok(())
}
}