mirror of
https://github.com/danog/ext-php-rs.git
synced 2024-11-30 04:39:04 +01:00
Added support for functions (#1)
* started work on functions can now add functions, but can't return from them * arg defaults to required
This commit is contained in:
parent
0211148a1a
commit
52f9d40050
@ -1,6 +1,11 @@
|
|||||||
use php_rs::{
|
use php_rs::{
|
||||||
info_table_end, info_table_row, info_table_start,
|
info_table_end, info_table_row, info_table_start,
|
||||||
php::module::{ModuleBuilder, ModuleEntry},
|
php::{
|
||||||
|
args::Arg,
|
||||||
|
enums::DataType,
|
||||||
|
function::{ExecutionData, FunctionBuilder, Zval},
|
||||||
|
module::{ModuleBuilder, ModuleEntry},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -12,8 +17,19 @@ pub extern "C" fn php_module_info(_module: *mut ModuleEntry) {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn get_module() -> *mut php_rs::php::module::ModuleEntry {
|
pub extern "C" fn get_module() -> *mut php_rs::php::module::ModuleEntry {
|
||||||
|
let funct = FunctionBuilder::new("skeleton_version", skeleton_version)
|
||||||
|
.arg(Arg::new("test", DataType::String))
|
||||||
|
.returns(DataType::Long, false, false)
|
||||||
|
.build();
|
||||||
|
|
||||||
ModuleBuilder::new("ext-skel", "0.1.0")
|
ModuleBuilder::new("ext-skel", "0.1.0")
|
||||||
.info_function(php_module_info)
|
.info_function(php_module_info)
|
||||||
|
.function(funct)
|
||||||
.build()
|
.build()
|
||||||
.into_raw()
|
.into_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn skeleton_version(_execute_data: *mut ExecutionData, _retval: *mut Zval) {
|
||||||
|
panic!("it worked?");
|
||||||
|
}
|
||||||
|
@ -28,9 +28,9 @@ use std::ffi::{CStr, CString};
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn c_str<S>(s: S) -> *const i8
|
pub fn c_str<S>(s: S) -> *const i8
|
||||||
where
|
where
|
||||||
S: Into<String>,
|
S: AsRef<str>,
|
||||||
{
|
{
|
||||||
CString::into_raw(CString::new(s.into()).unwrap())
|
CString::into_raw(CString::new(s.as_ref()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches the `build_id` for a Zend extension module.
|
/// Fetches the `build_id` for a Zend extension module.
|
||||||
|
64
src/php/args.rs
Normal file
64
src/php/args.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use super::enums::DataType;
|
||||||
|
|
||||||
|
use crate::bindings::zend_internal_arg_info;
|
||||||
|
|
||||||
|
/// Represents an argument to a function.
|
||||||
|
pub struct Arg {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) _type: DataType,
|
||||||
|
pub(crate) required: bool,
|
||||||
|
pub(crate) as_ref: bool,
|
||||||
|
pub(crate) allow_null: bool,
|
||||||
|
pub(crate) default_value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arg {
|
||||||
|
/// Creates a new argument.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `name` - The name of the parameter.
|
||||||
|
/// * `_type` - The type of the parameter.
|
||||||
|
pub fn new<S>(name: S, _type: DataType) -> Self
|
||||||
|
where
|
||||||
|
S: ToString,
|
||||||
|
{
|
||||||
|
Arg {
|
||||||
|
name: name.to_string(),
|
||||||
|
_type,
|
||||||
|
required: true,
|
||||||
|
as_ref: false,
|
||||||
|
allow_null: false,
|
||||||
|
default_value: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the argument as not required.
|
||||||
|
pub fn not_required(mut self) -> Self {
|
||||||
|
self.required = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the argument as a reference.
|
||||||
|
pub fn as_ref(mut self) -> Self {
|
||||||
|
self.as_ref = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the argument as nullable.
|
||||||
|
pub fn allow_null(mut self) -> Self {
|
||||||
|
self.allow_null = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the default value for the argument.
|
||||||
|
pub fn default<S>(mut self, default: S) -> Self
|
||||||
|
where
|
||||||
|
S: ToString,
|
||||||
|
{
|
||||||
|
self.default_value = Some(default.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ArgInfo = zend_internal_arg_info;
|
18
src/php/enums.rs
Normal file
18
src/php/enums.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/// Valid data types for PHP.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum DataType {
|
||||||
|
Undef = 0,
|
||||||
|
Null = 1,
|
||||||
|
False = 2,
|
||||||
|
True = 3,
|
||||||
|
Long = 4,
|
||||||
|
Double = 5,
|
||||||
|
String = 6,
|
||||||
|
Array = 7,
|
||||||
|
Object = 8,
|
||||||
|
Resource = 9,
|
||||||
|
Reference = 10,
|
||||||
|
ConstantExpression = 11,
|
||||||
|
|
||||||
|
Void = 14,
|
||||||
|
}
|
@ -1 +1,140 @@
|
|||||||
|
use std::{any::Any, borrow::Borrow, os::raw::c_char, ptr};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bindings::{zend_execute_data, zend_function_entry, zval},
|
||||||
|
functions::c_str,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
args::{Arg, ArgInfo},
|
||||||
|
enums::DataType,
|
||||||
|
types::ZendType,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A Zend function entry. Alias.
|
||||||
|
pub type FunctionEntry = zend_function_entry;
|
||||||
|
|
||||||
|
impl FunctionEntry {
|
||||||
|
/// Returns an empty function entry, signifing the end of a function list.
|
||||||
|
pub fn end() -> Self {
|
||||||
|
Self {
|
||||||
|
fname: ptr::null() as *const c_char,
|
||||||
|
handler: None,
|
||||||
|
arg_info: ptr::null(),
|
||||||
|
num_args: 0,
|
||||||
|
flags: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the function entry into a raw pointer, releasing it to the C world.
|
||||||
|
pub fn into_raw(self) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execution data passed when a function is called from Zend.
|
||||||
|
pub type ExecutionData = zend_execute_data;
|
||||||
|
|
||||||
|
/// Zend value.
|
||||||
|
pub type Zval = zval;
|
||||||
|
|
||||||
|
/// Function representation in Rust.
|
||||||
|
pub type FunctionHandler = extern "C" fn(execute_data: *mut ExecutionData, retval: *mut Zval);
|
||||||
|
|
||||||
|
/// Builds a function to be exported as a PHP function.
|
||||||
|
pub struct FunctionBuilder {
|
||||||
|
function: FunctionEntry,
|
||||||
|
args: Vec<Arg>,
|
||||||
|
n_req: u32,
|
||||||
|
retval: Option<DataType>,
|
||||||
|
ret_as_ref: bool,
|
||||||
|
ret_as_null: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionBuilder {
|
||||||
|
/// Creates a new function builder, used to build functions
|
||||||
|
/// to be exported to PHP.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `name` - The name of the function.
|
||||||
|
/// * `handler` - The handler to be called when the function is invoked from PHP.
|
||||||
|
pub fn new<N>(name: N, handler: FunctionHandler) -> Self
|
||||||
|
where
|
||||||
|
N: AsRef<str>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
function: FunctionEntry {
|
||||||
|
fname: c_str(name),
|
||||||
|
handler: Some(handler),
|
||||||
|
arg_info: ptr::null(),
|
||||||
|
num_args: 0,
|
||||||
|
flags: 0, // TBD?
|
||||||
|
},
|
||||||
|
args: vec![],
|
||||||
|
n_req: 0,
|
||||||
|
retval: None,
|
||||||
|
ret_as_ref: false,
|
||||||
|
ret_as_null: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an argument to the function.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `arg` - The argument to add to the function.
|
||||||
|
pub fn arg(mut self, arg: Arg) -> Self {
|
||||||
|
if arg.required {
|
||||||
|
self.n_req += 1;
|
||||||
|
}
|
||||||
|
self.args.push(arg);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the return value of the function.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `type_` - The return type of the function.
|
||||||
|
/// * `as_ref` - Whether the fucntion returns a reference.
|
||||||
|
/// * `allow_null` - Whether the function return value is nullable.
|
||||||
|
pub fn returns(mut self, type_: DataType, as_ref: bool, allow_null: bool) -> Self {
|
||||||
|
self.retval = Some(type_);
|
||||||
|
self.ret_as_ref = as_ref;
|
||||||
|
self.ret_as_null = allow_null;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds the function converting it into a Zend function entry.
|
||||||
|
pub fn build(mut self) -> FunctionEntry {
|
||||||
|
let mut args: Vec<ArgInfo> = vec![];
|
||||||
|
|
||||||
|
// argument header, retval etc
|
||||||
|
args.push(ArgInfo {
|
||||||
|
name: c_str(self.n_req.to_string()),
|
||||||
|
type_: match self.retval {
|
||||||
|
Some(retval) => {
|
||||||
|
ZendType::empty_from_type(retval, self.ret_as_ref, false, self.ret_as_null)
|
||||||
|
}
|
||||||
|
None => ZendType::empty(false, false),
|
||||||
|
},
|
||||||
|
default_value: ptr::null(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// arguments
|
||||||
|
for arg in self.args.iter() {
|
||||||
|
args.push(ArgInfo {
|
||||||
|
name: c_str(arg.name.clone()),
|
||||||
|
type_: ZendType::empty_from_type(arg._type, arg.as_ref, false, arg.allow_null),
|
||||||
|
default_value: match &arg.default_value {
|
||||||
|
Some(val) => c_str(val),
|
||||||
|
None => ptr::null(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.function.arg_info = Box::into_raw(args.into_boxed_slice()) as *const ArgInfo;
|
||||||
|
self.function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
|
pub mod args;
|
||||||
|
pub mod enums;
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
|
pub mod types;
|
||||||
|
@ -1,22 +1,14 @@
|
|||||||
use std::{
|
use std::{ffi::c_void, mem, os::raw::c_int, ptr};
|
||||||
ffi::c_void,
|
|
||||||
mem,
|
|
||||||
os::raw::{c_char, c_int},
|
|
||||||
ptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bindings::{
|
bindings::{zend_module_entry, zend_result, USING_ZTS, ZEND_DEBUG, ZEND_MODULE_API_NO},
|
||||||
zend_function_entry, zend_module_entry, zend_result, USING_ZTS, ZEND_DEBUG,
|
|
||||||
ZEND_MODULE_API_NO,
|
|
||||||
},
|
|
||||||
functions::{build_id, c_str},
|
functions::{build_id, c_str},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::function::FunctionEntry;
|
||||||
|
|
||||||
/// A Zend module entry. Alias.
|
/// A Zend module entry. Alias.
|
||||||
pub type ModuleEntry = zend_module_entry;
|
pub type ModuleEntry = zend_module_entry;
|
||||||
/// A Zend function entry. Alias.
|
|
||||||
pub type FunctionEntry = zend_function_entry;
|
|
||||||
/// A function to be called when the extension is starting up or shutting down.
|
/// A function to be called when the extension is starting up or shutting down.
|
||||||
pub type StartupShutdownFunc = extern "C" fn(type_: c_int, module_number: c_int) -> zend_result;
|
pub type StartupShutdownFunc = extern "C" fn(type_: c_int, module_number: c_int) -> zend_result;
|
||||||
/// A function to be called when `phpinfo();` is called.
|
/// A function to be called when `phpinfo();` is called.
|
||||||
@ -55,8 +47,8 @@ impl ModuleBuilder {
|
|||||||
/// * `version` - The current version of the extension. TBD: Deprecate in favour of the `Cargo.toml` version?
|
/// * `version` - The current version of the extension. TBD: Deprecate in favour of the `Cargo.toml` version?
|
||||||
pub fn new<N, V>(name: N, version: V) -> Self
|
pub fn new<N, V>(name: N, version: V) -> Self
|
||||||
where
|
where
|
||||||
N: Into<String>,
|
N: AsRef<str>,
|
||||||
V: Into<String>,
|
V: AsRef<str>,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
module: ModuleEntry {
|
module: ModuleEntry {
|
||||||
@ -152,13 +144,7 @@ impl ModuleBuilder {
|
|||||||
/// Builds the extension and returns a `ModuleEntry`.
|
/// Builds the extension and returns a `ModuleEntry`.
|
||||||
pub fn build(mut self) -> ModuleEntry {
|
pub fn build(mut self) -> ModuleEntry {
|
||||||
// TODO: move to seperate function
|
// TODO: move to seperate function
|
||||||
self.functions.push(FunctionEntry {
|
self.functions.push(FunctionEntry::end());
|
||||||
fname: ptr::null() as *const c_char,
|
|
||||||
handler: None,
|
|
||||||
arg_info: ptr::null(),
|
|
||||||
num_args: 0,
|
|
||||||
flags: 0,
|
|
||||||
});
|
|
||||||
self.module.functions =
|
self.module.functions =
|
||||||
Box::into_raw(self.functions.into_boxed_slice()) as *const FunctionEntry;
|
Box::into_raw(self.functions.into_boxed_slice()) as *const FunctionEntry;
|
||||||
self.module
|
self.module
|
||||||
|
85
src/php/types.rs
Normal file
85
src/php/types.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use std::{ffi::c_void, ptr};
|
||||||
|
|
||||||
|
use crate::bindings::{
|
||||||
|
zend_type, IS_MIXED, MAY_BE_ANY, MAY_BE_BOOL, _IS_BOOL, _ZEND_IS_VARIADIC_BIT,
|
||||||
|
_ZEND_SEND_MODE_SHIFT, _ZEND_TYPE_NULLABLE_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::enums::DataType;
|
||||||
|
|
||||||
|
pub type ZendType = zend_type;
|
||||||
|
|
||||||
|
impl ZendType {
|
||||||
|
/// Builds an empty Zend type container.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `pass_by_ref` - Whether the value should be passed by reference.
|
||||||
|
/// * `is_variadic` - Whether this type represents a variadic argument.
|
||||||
|
pub fn empty(pass_by_ref: bool, is_variadic: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
ptr: ptr::null::<c_void>() as *mut c_void,
|
||||||
|
type_mask: Self::arg_info_flags(pass_by_ref, is_variadic),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty_from_type(
|
||||||
|
type_: DataType,
|
||||||
|
pass_by_ref: bool,
|
||||||
|
is_variadic: bool,
|
||||||
|
allow_null: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ptr: ptr::null::<c_void>() as *mut c_void,
|
||||||
|
type_mask: Self::type_init_code(type_, pass_by_ref, is_variadic, allow_null),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the internal flags of the type.
|
||||||
|
/// Translation of of the `_ZEND_ARG_INFO_FLAGS` macro from zend_API.h:110.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `pass_by_ref` - Whether the value should be passed by reference.
|
||||||
|
/// * `is_variadic` - Whether this type represents a variadic argument.
|
||||||
|
pub(crate) fn arg_info_flags(pass_by_ref: bool, is_variadic: bool) -> u32 {
|
||||||
|
((pass_by_ref as u32) << _ZEND_SEND_MODE_SHIFT)
|
||||||
|
| (if is_variadic {
|
||||||
|
_ZEND_IS_VARIADIC_BIT
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the internal flags of the type.
|
||||||
|
/// Translation of the `ZEND_TYPE_INIT_CODE` macro from zend_API.h:163.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// * `type_` - The type to initialize the Zend type with.
|
||||||
|
/// * `pass_by_ref` - Whether the value should be passed by reference.
|
||||||
|
/// * `is_variadic` - Whether this type represents a variadic argument.
|
||||||
|
/// * `allow_null` - Whether the value can be null.
|
||||||
|
pub(crate) fn type_init_code(
|
||||||
|
type_: DataType,
|
||||||
|
pass_by_ref: bool,
|
||||||
|
is_variadic: bool,
|
||||||
|
allow_null: bool,
|
||||||
|
) -> u32 {
|
||||||
|
let type_ = type_ as u32;
|
||||||
|
|
||||||
|
(if type_ == _IS_BOOL {
|
||||||
|
MAY_BE_BOOL
|
||||||
|
} else {
|
||||||
|
if type_ == IS_MIXED {
|
||||||
|
MAY_BE_ANY
|
||||||
|
} else {
|
||||||
|
1 << type_
|
||||||
|
}
|
||||||
|
}) | (if allow_null {
|
||||||
|
_ZEND_TYPE_NULLABLE_BIT
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}) | Self::arg_info_flags(pass_by_ref, is_variadic)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user