├── .gitignore ├── src ├── lib.rs ├── bin │ └── hello.rs ├── zend │ ├── zend_gen.rs │ └── mod.rs └── hello │ └── mod.rs ├── Cargo.toml ├── hello.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | *~ 5 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] pub mod zend; 2 | mod hello; 3 | pub use hello::get_module; 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 3 | version = "0.1.0" 4 | authors = ["Yakov Zaytsev "] 5 | 6 | [dependencies] 7 | libc = "0.2.43" 8 | 9 | [lib] 10 | name = "hello" 11 | crate-type = ["cdylib"] 12 | -------------------------------------------------------------------------------- /src/bin/hello.rs: -------------------------------------------------------------------------------- 1 | extern crate hello; // FIXME cannot satisfy dependencies so `std` only shows up once 2 | 3 | use hello::zend::_zend_module_entry; 4 | 5 | const PHP_HELLO_VERSION: &str = "0.1.0"; 6 | 7 | fn main() -> std::io::Result<()> { 8 | let hello_module_entry = _zend_module_entry::new( 9 | "hello", 10 | PHP_HELLO_VERSION 11 | ); 12 | 13 | println!("PHP Extension Build {:?}", hello_module_entry.build_id); 14 | 15 | Ok(()) 16 | } 17 | 18 | -------------------------------------------------------------------------------- /hello.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('hello')) { 5 | dl('hello.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $module = 'hello'; 8 | $functions = get_extension_funcs($module); 9 | echo "Functions available in the test extension:$br\n"; 10 | foreach($functions as $func) { 11 | echo $func."$br\n"; 12 | } 13 | echo "$br\n"; 14 | $function = 'confirm_' . $module . '_compiled'; 15 | if (extension_loaded($module)) { 16 | $str = $function($module); 17 | } else { 18 | $str = "Module $module is not compiled into PHP"; 19 | } 20 | echo "$str\n"; 21 | ?> 22 | -------------------------------------------------------------------------------- /src/zend/zend_gen.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void}; 2 | #[repr(C)] 3 | pub struct _zend_module_entry { 4 | pub size: c_ushort, 5 | pub zend_api: c_uint, 6 | pub zend_debug: c_uchar, 7 | pub zts: c_uchar, 8 | pub ini_entry: *mut c_void, 9 | pub deps: *mut c_void, 10 | pub name: *const c_char, 11 | pub functions: *mut c_void, 12 | pub module_startup_func: *mut c_void, 13 | pub module_shutdown_func: *mut c_void, 14 | pub request_startup_func: *mut c_void, 15 | pub request_shutdown_func: *mut c_void, 16 | pub info_func: *mut c_void, 17 | pub version: *const c_char, 18 | pub globals_size: c_int, 19 | pub globals_ptr: *mut c_void, 20 | pub globals_ctor: *mut c_void, 21 | pub globals_dtor: *mut c_void, 22 | pub post_deactivate_func: *mut c_void, 23 | pub module_started: c_int, 24 | pub type_: c_uchar, 25 | pub handle: *mut c_void, 26 | pub module_number: c_int, 27 | pub build_id: *const c_char, 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hello (Rust) 2 | 3 | PHP ext_skel for Rust. 4 | 5 | To build extension only set (see: https://doc.rust-lang.org/reference/linkage.html) 6 | 7 | ``` 8 | crate-type = ["cdylib"] 9 | ``` 10 | 11 | in Cargo.toml and 12 | 13 | - on linux 14 | 15 | ``` 16 | cd php-ext-hello-rs 17 | RUSTFLAGS='-C prefer-dynamic' cargo build --lib 18 | ``` 19 | 20 | - or on OSX 21 | 22 | ``` 23 | RUSTFLAGS="-Cprefer-dynamic -Clink-arg=-undefined -Clink-arg=dynamic_lookup" cargo build --lib 24 | ``` 25 | 26 | test PHP extension: 27 | 28 | ``` 29 | $ php -d extension=./target/debug/libhello.so -f hello.php 30 | Functions available in the test extension: 31 | confirm_hello_compiled 32 | 33 | 34 | ``` 35 | 36 | or set in Cargo.toml (otherwise: can't find crate hello) 37 | 38 | ``` 39 | crate-type = ["dylib"] 40 | ``` 41 | 42 | and also try **build and** run hello (which uses lib crate) 43 | 44 | ``` 45 | RUSTFLAGS='-C prefer-dynamic' cargo build 46 | DYLD_LIBRARY_PATH=$HOME/.rustup/toolchains/a-toolchain/lib/ ./target/debug/hello 47 | ``` 48 | 49 | e.g. 50 | 51 | ``` 52 | DYLD_LIBRARY_PATH=$HOME/.rustup/toolchains/stable-x86_64-apple-darwin/lib/ ./target/debug/hello 53 | ``` 54 | 55 | How to generate zend.rs 56 | 57 | Get yakovzaytsev/rust-gen-struct@ecb03b8 and 58 | 59 | ``` 60 | [DY]LD_LIBRARY_PATH=$HOME/.local/share/llvmenv/7.0.0/lib /path/to/rust-gen-struct /home/src/php-7.2.10/Zend/zend_modules.h _zend_module_entry > src/zend/zend_gen.rs 61 | ``` 62 | -------------------------------------------------------------------------------- /src/hello/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | 3 | // XXX 4 | use zend::*; 5 | use std::mem; 6 | use std::ptr; 7 | use std::os::raw::{c_char, c_void}; 8 | use std::ffi::CString; 9 | 10 | const PHP_HELLO_VERSION: &str = "0.1.0"; 11 | 12 | // ZEND_NAMED_FUNCTION(ZEND_FN(name)) zif_##name 13 | // INTERNAL_FUNCTION_PARAMETERS 14 | // zend_execute_data *execute_data, zval *return_value 15 | fn zif_confirm_hello_compiled(execute_data: *mut c_void, return_value: *mut _zval_struct) { // void 16 | let strg = strpprintf(0, "Congratulations! You have successfully modified ext/hello/config.m4. Module hello is now compiled into PHP."); 17 | RETURN_STR!(return_value, strg) 18 | } 19 | 20 | static mut hello_module_entry: _zend_module_entry = 21 | _zend_module_entry { 22 | // STANDARD_MODULE_HEADER 23 | size: mem::size_of::<_zend_module_entry>() as u16, // STANDARD_MODULE_HEADER_EX 24 | zend_api: ZEND_MODULE_API_NO, // STANDARD_MODULE_HEADER_EX 25 | zend_debug: ZEND_DEBUG, // STANDARD_MODULE_HEADER_EX 26 | zts: USING_ZTS, // STANDARD_MODULE_HEADER_EX 27 | ini_entry: ptr::null_mut(), 28 | deps: ptr::null_mut(), 29 | 30 | name: 0 as *const c_char, // XXX 31 | functions: ptr::null_mut(), 32 | module_startup_func: ptr::null_mut(), // PHP_MINIT(hello) 33 | module_shutdown_func: ptr::null_mut(), // PHP_MSHUTDOWN(hello) 34 | request_startup_func: ptr::null_mut(), // PHP_RINIT(hello) 35 | request_shutdown_func: ptr::null_mut(), // PHP_RSHUTDOWN(hello) 36 | info_func: ptr::null_mut(), // PHP_MINFO(hello) 37 | version: 0 as *const c_char, 38 | 39 | // STANDARD_MODULE_PROPERTIES 40 | globals_size: 0, // NO_MODULE_GLOBALS, 41 | globals_ptr: ptr::null_mut(), // NO_MODULE_GLOBALS 42 | globals_ctor: ptr::null_mut(), // NO_MODULE_GLOBALS 43 | globals_dtor: ptr::null_mut(), // NO_MODULE_GLOBALS 44 | post_deactivate_func: ptr::null_mut(), 45 | module_started: 0, // STANDARD_MODULE_PROPERTIES_EX 46 | type_: 0, // STANDARD_MODULE_PROPERTIES_EX 47 | handle: ptr::null_mut(), // STANDARD_MODULE_PROPERTIES_EX 48 | module_number: 0, // STANDARD_MODULE_PROPERTIES_EX 49 | build_id: 0 as *const c_char, // STANDARD_MODULE_PROPERTIES_EX 50 | }; 51 | 52 | #[no_mangle] 53 | pub extern "C" fn get_module() -> *const _zend_module_entry { 54 | let mut hello_functions: Vec<_zend_function_entry> = vec![ 55 | // PHP_FE 56 | _zend_function_entry { 57 | fname: CString::new("confirm_hello_compiled").unwrap().into_raw(), 58 | handler: zif_confirm_hello_compiled as *mut c_void, 59 | arg_info: ptr::null_mut(), 60 | num_args: 0, 61 | flags: 0, 62 | }, 63 | PHP_FE_END!(), 64 | ]; 65 | // println!("hello_functions[0] {:?}", hello_functions[0]); 66 | unsafe { 67 | hello_module_entry.init( 68 | "hello", 69 | PHP_HELLO_VERSION 70 | ); 71 | // TODO transfers ownership to C 72 | hello_module_entry.functions = hello_functions.as_mut_ptr() as *mut c_void; 73 | mem::forget(hello_functions); 74 | &hello_module_entry 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/zend/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | 3 | mod zend_gen; 4 | 5 | pub use self::zend_gen::_zend_module_entry; 6 | 7 | use std::ffi::CString; 8 | use std::ffi::CStr; 9 | use std::os::raw::{c_uchar, c_char, c_int, c_uint, c_void, c_long, c_double}; 10 | 11 | pub const ZEND_MODULE_API_NO: c_uint = 20170718; // zend_modules.h 12 | pub const ZEND_DEBUG: c_uchar = 0; // php -ini | grep 'Debug =>' 13 | pub const USING_ZTS: c_uchar = 0; // NTS 14 | 15 | use std::mem; 16 | use std::ptr; 17 | 18 | pub const ZEND_MODULE_BUILD_ID: &str = "API20170718,NTS"; // php -ini | grep 'PHP Extension Build' 19 | 20 | impl _zend_module_entry { 21 | pub fn init(&mut self, name: &str, version: &str) { 22 | // STANDARD_MODULE_HEADER 23 | self.size = mem::size_of::<_zend_module_entry>() as u16; // STANDARD_MODULE_HEADER_EX 24 | // println!("size {}", self.size); 25 | self.zend_api = ZEND_MODULE_API_NO; // STANDARD_MODULE_HEADER_EX 26 | self.zend_debug = ZEND_DEBUG; // STANDARD_MODULE_HEADER_EX 27 | self.zts = USING_ZTS; // STANDARD_MODULE_HEADER_EX 28 | self.ini_entry = ptr::null_mut(); 29 | self.deps = ptr::null_mut(); 30 | 31 | self.name = CString::new(name).unwrap().into_raw(); 32 | self.functions = ptr::null_mut(); 33 | self.module_startup_func = ptr::null_mut(); // PHP_MINIT(hello) 34 | self.module_shutdown_func = ptr::null_mut(); // PHP_MSHUTDOWN(hello) 35 | self.request_startup_func = ptr::null_mut(); // PHP_RINIT(hello) 36 | self.request_shutdown_func = ptr::null_mut(); // PHP_RSHUTDOWN(hello) 37 | self.info_func = ptr::null_mut(); // PHP_MINFO(hello) 38 | self.version = CString::new(version).unwrap().into_raw(); 39 | 40 | // STANDARD_MODULE_PROPERTIES 41 | self.globals_size = 0; // NO_MODULE_GLOBALS, 42 | self.globals_ptr = ptr::null_mut(); // NO_MODULE_GLOBALS 43 | self.globals_ctor = ptr::null_mut(); // NO_MODULE_GLOBALS 44 | self.globals_dtor = ptr::null_mut(); // NO_MODULE_GLOBALS 45 | self.post_deactivate_func = ptr::null_mut(); 46 | self.module_started = 0; // STANDARD_MODULE_PROPERTIES_EX 47 | self.type_ = 0; // STANDARD_MODULE_PROPERTIES_EX 48 | self.handle = ptr::null_mut(); // STANDARD_MODULE_PROPERTIES_EX 49 | self.module_number = 0; // STANDARD_MODULE_PROPERTIES_EX 50 | let c_string = CString::new(ZEND_MODULE_BUILD_ID).unwrap(); 51 | let ptr = c_string.into_raw(); 52 | // unsafe { 53 | // println!("{:?}", *ptr as u8); 54 | // println!("{:?}", *ptr.offset(1) as u8); 55 | // println!("{:?}", *ptr.offset(2) as u8); 56 | // println!("{:?}", *ptr.offset(3) as u8); 57 | // } 58 | self.build_id = ptr; // STANDARD_MODULE_PROPERTIES_EX 59 | //self.build_id = 42 as *const c_char; 60 | } 61 | 62 | pub fn new(name: &str, version: &str) -> _zend_module_entry { 63 | unsafe { 64 | let mut entry: _zend_module_entry = mem::uninitialized(); 65 | entry.init(name, version); 66 | entry 67 | } 68 | } 69 | } 70 | 71 | // #[repr(C)] 72 | // pub struct _zend_internal_arg_info { 73 | // pub name: *const c_char, 74 | // // zend_type uintptr_t 75 | // pub type_: *mut c_void, 76 | // // zend_uchar unsigned char 77 | // pub pass_by_reference: c_uchar, 78 | // // zend_bool unsigned char 79 | // pub is_variadic: c_uchar, 80 | // } 81 | 82 | #[repr(C)] 83 | pub struct _zend_function_entry { 84 | pub fname: *const c_char, 85 | pub handler: *mut c_void, // void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); 86 | pub arg_info: *mut c_void, // XXX const struct _zend_internal_arg_info * 87 | pub num_args: c_uint, // uint32_t 88 | pub flags: c_uint, // uint32_t 89 | } 90 | 91 | #[repr(C)] 92 | #[derive(Clone, Copy)] // otherwise unions with non-`Copy` fields are unstable 93 | pub struct v { 94 | // ZEND_ENDIAN_LOHI_3 95 | pub type_: c_uchar, 96 | pub flags: c_uchar, 97 | pub gc_info: libc::uint16_t, 98 | } 99 | 100 | #[repr(C)] 101 | pub union u { 102 | pub v: v, 103 | pub type_info: libc::uint32_t, 104 | } 105 | 106 | #[repr(C)] 107 | pub struct zend_refcounted_h { 108 | pub refcount: libc::uint32_t, 109 | pub u: u, 110 | } 111 | 112 | #[repr(C)] 113 | pub struct _zend_string { 114 | pub gc: zend_refcounted_h, 115 | pub h: libc::int64_t, 116 | pub len: libc::size_t, 117 | pub val: c_char, 118 | } 119 | 120 | #[repr(C)] 121 | pub union _zend_value { 122 | pub lval: c_long, // long value zend_long int64_t 123 | pub dval: c_double, 124 | pub str: *mut _zend_string, 125 | // TODO: 126 | } 127 | 128 | #[repr(C)] 129 | pub union u1 { 130 | // TODO: 131 | pub type_info: libc::uint32_t, 132 | } 133 | 134 | #[repr(C)] 135 | pub union u2 { 136 | pub next: libc::uint32_t, 137 | // TODO: 138 | } 139 | 140 | #[repr(C)] 141 | pub struct _zval_struct { // zval 142 | pub value: _zend_value, 143 | pub u1: u1, 144 | pub u2: u2, 145 | } 146 | 147 | pub const IS_STRING_EX: libc::uint32_t = 5126; 148 | 149 | extern "C" { 150 | fn zend_strpprintf(max_len: libc::size_t, format: *const c_char, ...) -> *mut _zend_string; 151 | } 152 | 153 | // XXX 154 | pub fn strpprintf(max_len: libc::size_t, format: &str) -> *mut _zend_string { 155 | let c_format = CString::new(format).unwrap(); 156 | unsafe { 157 | let strg = zend_strpprintf(max_len, c_format.as_ptr()); 158 | strg // ??? 159 | } 160 | } 161 | 162 | #[macro_export] 163 | macro_rules! RETURN_STR { 164 | ( $return_value:expr, $strg:expr ) => { 165 | { 166 | unsafe { 167 | (*$return_value).value.str = $strg; // RETURN_STR 168 | (*$return_value).u1.type_info = IS_STRING_EX; 169 | } 170 | } 171 | }; 172 | } 173 | 174 | #[macro_export] 175 | macro_rules! PHP_FE_END { 176 | // () that macro takes no args 177 | () => { 178 | _zend_function_entry { 179 | fname: ptr::null_mut(), 180 | handler: ptr::null_mut(), 181 | arg_info: ptr::null_mut(), 182 | num_args: 0, 183 | flags: 0, 184 | } 185 | } 186 | } 187 | --------------------------------------------------------------------------------