本文使用 Solder
库来实现PHP扩展程序,它是基于 php-rs
库的试验性项目,实现了php扩展的函数处理。 满足了字符串和数字类型基本要求。
soder 源库地址为 github , 这里使用的是 fork 的库, github.com/erasin/solder。
2020-09-17 : 项目 XX/php-rust 中提供了更多的类型处理。简化函数注册流程。
创建项目
使用 cargo 来创建项目,然后创建编译配置文件.cargo/config
cargo new --lib rs-tool
cd rs-tool
mkdir .cargo
touch config
编辑 .cargo/config
,遵循toml格式。
[build]
rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup"]
引入库
编辑 Cargo.toml
,追加引用
[dependencies]
solder = {git = "https://github.com/erasin/solder"}
lazy_static = "1.4"
md5 = "0.7.0"
[lib]
crate-type = ["dylib"]
name = "rstool"
- lib 该节点定义输出类型.
- name 定义输出文件名称,实际名称头部追加
lib
,比如当前项目为librstool.dylib
. - dylib 类型在 osx 下为
.dylib
, linux下为.so
文件.
- name 定义输出文件名称,实际名称头部追加
这里 github 速度太慢的化可以用
https://gitee.com/era/solder
或者自己镜像一个即可。
- 引入
lazy_static
来制作计数器. md5
来创建rs_md5
函数.
创建扩展函数
编辑项目文件 src/lig.rs
.
use solder::info::*;
use solder::zend::*;
use solder::*;
// 扩展简介
#[no_mangle]
pub extern "C" fn php_module_info() {
print_table_start();
print_table_row("tool written by dyuit", "enabled");
print_table_end();
}
/// 函数封装
#[no_mangle]
pub extern "C" fn get_module() -> *mut Module {
// 封装函数
let fn_md5 = FunctionBuilder::new(c_str!("rs_md5"), rs_md5)
.with_arg(ArgInfo::new(c_str!("str"), 0, 0, 0))
.build();
// 将函数追加到扩展中
ModuleBuilder::new(c_str!("dyuit_tool"), c_str!("0.1.0-dev"))
.with_info_function(php_module_info)
.with_function(fn_md5)
.build()
.into_raw()
}
// 引入md5
use md5;
// rs md5 函数
#[no_mangle]
pub extern "C" fn rs_md5(_data: &ExecuteData, retval: &mut Zval) {
// 参数处理,如果多个参数就创建多个 zval 实例。
let mut param_zval = Zval::new_as_null();
// 解析参数,这里可以解析多个参数,不可以超过5个
php_parse_parameters!(&mut param_zval);
// 将参数转换为rust类型
let s = String::try_from(param_zval).ok().unwrap();
// md5 处理
let digest = md5::compute(s);
let re = format!("{:x}", digest);
// 将数据封包返回,查看 solder 源码,检查支持的类型,这里多数为string和&str。
php_return!(retval, re);
}
文件中
get_module
用来输出扩展php_module_info
定义扩展注释- FunctionBuilder 来封装函数,多个参数用
with_arg
来定义参数,参数最大数量支持5个。 - ModuleBuilder 中
with_function
来关联函数。
编译和测试
回到项目目录
cargo build
# 加载扩展饼进入php命令
php -d extension=target/debug/librstool.dylib -a
# php 验证
php > echo rs_md5('123456');
e10adc3949ba59abbe56e057f20f883e
php > echo md5('123456');
e10adc3949ba59abbe56e057f20f883e
php > print_r(get_extension_funcs('dyuit_tool'));
Array
(
[0] => rs_md5
)
如果成功后就可以生产 release 版本 ,加载到 php.ini
中就可以使用了。
! libc 在交叉编译中会报错,生产对应平台文件,需要多个环境编译。
再创建函数 计数器加减
创建函数
use lazy_static::lazy_static;
use std::sync::Mutex;
lazy_static! {
static ref NUM: Mutex<u32> = Mutex::new(0);
}
#[no_mangle]
pub extern "C" fn rs_add(_data: &ExecuteData, retval: &mut Zval) {
let mut n = NUM.lock().unwrap();
*n += 1;
php_return!(retval, *n);
}
#[no_mangle]
pub extern "C" fn rs_less(_data: &ExecuteData, retval: &mut Zval) {
let mut n = NUM.lock().unwrap();
*n -= 1;
php_return!(retval, *n);
}
加载函数到 get_module()
let fn_add = FunctionBuilder::new(c_str!("rs_add"), rs_lock).build();
let fn_less = FunctionBuilder::new(c_str!("rs_less"), rs_unlock).build();
ModuleBuilder::new(c_str!("dyuit_tool"), c_str!("0.1.0-dev"))
.with_info_function(php_module_info)
.with_function(fn_md5)
.with_function(fn_add)
.with_function(fn_less)
.build()
.into_raw()
结束语
PHP 7.4 的 FFI 改进 , 或许比扩展更好,有兴趣的同学可以去对比下。