phper如何用Rust开发PHP扩展Liunx版


orchid
电梯物联网专家 2024-01-23 10:15:03 59457 赞同 0 反对 0
分类: 资源 标签: 后端
phper如何用Rust开发PHP扩展Liunx版
众所周知,作为一名phper,对php有些功能的缺失有时会感到尴尬,比如:调用ffmpeg视频处理工具。没有专门的扩展来操作的,什么?利用php system函数调用?对于开源的php脚本,这太不安全了!这个时候作为深资的phper会考虑开发php扩展,在扩展中实现对ffmpeg的操作。
为什么要使用php扩展?
优点:
  • php扩展是C开发的,那速度没得说。
  • 耦合性高,它的出现就是用来增强php的。
  • 安全性高,毕竟扩展是编译后的程序,代码不开源。
缺点:
  • 需针对php版本及系统环境进行开发,那么就比较麻烦了。也就是说7.4版本的php,liunx环境下开发的扩展,关注PHP大神微信公众号只支持该php版本及系统。
  • 需要会C、C++,当然本文是以rust进行开发,对C的数据类型进行了解,对rust FFI的操作及数据类型转换需精通。
  • 调试相对麻烦。
为什么要用rust开发php扩展?
原因很简单,这还要说起rust的语言特性
因“所有权”的特性使你的程序更安全,不会像C那样出现各种“玄学BUG”。拥有C一样的性能,我还是很看好它的发展。
rust开发php扩展流程:
当然,rust目前是没有专门开发php扩展的骨架。所以我的逻辑也很简单,利用rust开发静态库暴露给C【涉及FFI的了解】。我们在php官方骨架中直接引入rust静态库调用其方法即可。
二、开发环境与流程
开发环境
宝塔【CentOS 7.6】、GCC【涉及php扩展骨架的编译,我这里系统内置就有,如果编译扩展报相关错了自行安装】、关注“PHP大神”微信公众号对应php版本源码、web环境【宝塔中安装对应php版本、nginx、mysql等等】
开发整体流程:
1、准备宝塔,安装这里就不说了
图片
这里我们以开发php7.4扩展为例。
图片
2、下载php7.4 liunx版源码
php官网:https://www.php.net/
图片
图片
注意!该源码版本必须与你环境php版本完全一致!!!
图片
3、上传php源码到宝塔
图片
  •  
/usr/phper
在usr下创建一个phper文件夹,然后将源码压缩包上传到此处。
图片
解压该压缩包
图片
4、创建一个我们自己的扩展
图片
/usr/phper/php-7.4.30/ext目录下有这么一个php文件,它可以创建扩展!
图片
注意设置命令行版本,因为接下来利用php命令必须是版本一致的!
图片
在刚刚的目录下,点击终端,输入创建扩展命令。
  •  
php ext_skel.php --ext 扩展名称
图片
图片
这里就多出了一个新的扩展源码文件。
图片
图片
在该目录下点击终端,输入:
  •  
phpize
图片
图片
接着输入:
  •  
./configure --with-php-config=/www/server/php/74/bin/php-config
注意这个参数php路径,如果是别的版本,请自行在宝塔里安装找到对应版本路径,它们都是放一起的。
图片
回车开始进行检查了
图片
最后输入:
  •  
make
进行编译。
图片
图片
这个目录下便是编译出来的so扩展最终文件了!
图片
让我们看下默认生成的扩展有哪些功能
图片
查看主文件【需了解php扩展骨架,这里以它默认给的为例】
图片
也就是说,刚刚编译出来的扩展,是有这两个函数的,咱们测试一下玩玩。
注意!每次修改主文件,都需要重新按上述命令跑一遍,否则不生效,很奇怪!
  •  
  •  
  •  
phpize./configure --with-php-config=/www/server/php/74/bin/php-configmake
5、使用扩展
图片
复制刚刚生成的扩展文件到我们php环境的扩展里
图片
图片
配置php.ini加载hello.so扩展
图片
  •  
extension = hello.so
保存后记得重新启动下php,否则不生效的!
图片
图片
在文件管理中点击终端,输入:
  •  
php -m
可以看到我们的扩展在列表中了。
创建一个站点,测试下扩展中两个函数。
图片
看好,php版本是7.4
图片
图片
访问站点,发现没有问题。
当然也可以通过命令行运行php脚本查看结果【前提是网站那里php命令行版本设置的7.4】
  •  
php index.php
OK!从创建到生成到使用扩展的流程结束,接下来才进入正题,开始用rust开发扩展。
6、rust与php扩展的整合开发
开发工具:CLion
需要rust环境与CLion中rust插件的安装与配置,这个自行去百度,比我想象中的全!
图片
创建一个hello命名的库项目
图片
我们写两个导出函数,分别是加法功能和base64字符串解析功能。
图片
lib.rs
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
#![crate_type = "staticlib"]
extern crate libc;
//使用C类型约束use std::ffi::{CStr, CString};use libc::{c_char, c_int};
//add_int【参数:两个c语言的int类型】:对两个int类型数值进行相加#[no_mangle]pub extern "C" fn add_int(x:c_int, y:c_int) -> c_int{ //两个数相加 return x + y;}
//base64_decode函数【参数:c语言的*char类型】:对字符串进行base64解码#[no_mangle]pub extern "C" fn base64_decode(s:*const c_char) -> *mut c_char { //c char类型转&str let h = unsafe{CStr::from_ptr(s).to_str().unwrap()}; //base64 解码 let s = base64::decode(h.to_string()); if let Err(_s) = s { panic!("类型错误!"); } let n = String::from_utf8(s.unwrap().clone()).unwrap(); //String 转 C CString let a = CString::new(n.as_str()).unwrap(); //C CString 转 C char //这里实属无奈,因为rust ffi中阐述,对字符串返回只能是该字符串地址,所以需要该方法进行返回C才能接收到! let r = a.into_raw(); return r;}
图片
Cargo.toml
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
[package]name = "hello"version = "0.1.0"edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[lib]name = "hello"crate-type = ["staticlib"]
[dependencies]libc = "*"base64 = "0.12.1"
注意在编译过程中涉及系统类型,不然在引入该静态库编译扩展可能报错,提示不支持。
编译64位静态库
  •  
  •  
rustup target add x86_64-unknown-linux-muslcargo build --target x86_64-unknown-linux-musl --release
编译32位静态库
  •  
  •  
rustup target add i686-unknown-linux-muslcargo build --target i686-unknown-linux-musl --release
这里我们是64位系统。
图片
会生成一个.a文件,该文件便是liunx支持的静态库文件。
生成支持C语言的胶水头文件【用于C调用该库需要写的函数声明,很方便】
创建cbindgen.toml文件
内容:
  •  
language = "C"
图片
安装cbindgen,创建头文件。
  •  
  •  
cargo install --force cbindgencbindgen --config cbindgen.toml --crate 项目名称 --output 头文件名称.h
图片
图片
自动生成了C语言的函数声明hello.h文件,用于调用。
图片
回到之前我们创建的hello扩展
创建lib文件夹
图片
将刚刚编译出来的静态库.a文件上传到lib目录下
图片
将刚刚创建的.h头文件上传到扩展目录下
图片
配置.m4预编译文件【关键】
图片
设置引入lib文件夹中的静态库文件
图片
  •  
  •  
PHP_ADD_LIBRARY_WITH_PATH(hello, /usr/phper/php-7.4.30/ext/hello/lib, HELLO_SHARED_LIBADD) PHP_SUBST(HELLO_SHARED_LIBADD)
图片
保存.m4
图片
编写主文件
图片
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
/* hello extension for PHP */
#ifdef HAVE_CONFIG_H# include "config.h"#endif
#include "php.h"#include "ext/standard/info.h"#include "php_hello.h"#include "hello.h"//引入头文件/* For compatibility with older PHP versions */#ifndef ZEND_PARSE_PARAMETERS_NONE#define ZEND_PARSE_PARAMETERS_NONE() \ ZEND_PARSE_PARAMETERS_START(0, 0) \ ZEND_PARSE_PARAMETERS_END()#endif
/* {{{ void hello_test1() */PHP_FUNCTION(hello_test1){ ZEND_PARSE_PARAMETERS_NONE();
int num = add_int(1,2);//rust中两个数相加函数并返回。
php_printf("The extension %d is loaded and working!\r\n", num);}/* }}} */
/* {{{ string hello_test2( [ string $var ] ) */PHP_FUNCTION(hello_test2){char *var = "World";size_t var_len = sizeof("World") - 1; zend_string *retval;
ZEND_PARSE_PARAMETERS_START(0, 1)Z_PARAM_OPTIONALZ_PARAM_STRING(var, var_len)ZEND_PARSE_PARAMETERS_END();
char *newstr = base64_decode(var);//rust中解析base64字符串并返回。
retval = strpprintf(0, "Hello %s", newstr);
RETURN_STR(retval);}/* }}}*/
/* {{{ PHP_RINIT_FUNCTION */PHP_RINIT_FUNCTION(hello){#if defined(ZTS) && defined(COMPILE_DL_HELLO) ZEND_TSRMLS_CACHE_UPDATE();#endif
return SUCCESS;}/* }}} */
/* {{{ PHP_MINFO_FUNCTION */PHP_MINFO_FUNCTION(hello){ php_info_print_table_start(); php_info_print_table_header(2, "hello support", "enabled"); php_info_print_table_end();}/* }}} */
/* {{{ arginfo */ZEND_BEGIN_ARG_INFO(arginfo_hello_test1, 0)ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_hello_test2, 0) ZEND_ARG_INFO(0, str)ZEND_END_ARG_INFO()/* }}} */
/* {{{ hello_functions[] */static const zend_function_entry hello_functions[] = { PHP_FE(hello_test1, arginfo_hello_test1) PHP_FE(hello_test2, arginfo_hello_test2) PHP_FE_END};/* }}} */
/* {{{ hello_module_entry */zend_module_entry hello_module_entry = { STANDARD_MODULE_HEADER,"hello", /* Extension name */ hello_functions, /* zend_function_entry */NULL, /* PHP_MINIT - Module initialization */NULL, /* PHP_MSHUTDOWN - Module shutdown */ PHP_RINIT(hello), /* PHP_RINIT - Request initialization */NULL, /* PHP_RSHUTDOWN - Request shutdown */ PHP_MINFO(hello), /* PHP_MINFO - Module info */ PHP_HELLO_VERSION, /* Version */ STANDARD_MODULE_PROPERTIES};/* }}} */
#ifdef COMPILE_DL_HELLO# ifdef ZTSZEND_TSRMLS_CACHE_DEFINE()# endifZEND_GET_MODULE(hello)#endif
删除之前生成的扩展文件
图片
重新生成扩展
图片
  •  
  •  
  •  
phpize./configure --with-php-config=/www/server/php/74/bin/php-configmake
图片
大小都变了,说明我们的静态库在里面了哈哈。
按上述使用扩展流程替换扩展
图片
注意!替换扩展文件后要重启PHP哦,不然不生效!
7、测试rust开发的php扩展
图片
网页测试和命令行测试
图片
也可以通过php扩展骨架直接进行测试
图片
图片
图片
编写要执行测试的扩展函数
  •  
--TEST--hello_test2() Basic test--SKIPIF--<?phpif (!extension_loaded('hello')) {echo 'skip';}?>--FILE--<?phphello_test1();var_dump(hello_test2('5LiA56CB6LaF5Lq6'));?>--EXPECT--string(11) "Hello World"string(9) "Hello PHP"
图片
 扩展目录下直接输入:
  •  
make test
图片
执行后 tests目录下输出了一个.out文件
图片
是不是这样更方便了呢?

如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
电梯物联网专家L2
粉丝 1 资源 185 + 关注 私信
最近热门资源
分享如何统信UOS系统在屏蔽mysql显性的用户名称以及密码  616
分享免费开源高速下载器  577
分享如何在银河麒麟高级服务器操作系统V10SP3中需要启用内核审计功能。  572
通过shell脚本在统信UOS/麒麟系统中安装nginx  504
分享如何查看网卡中断的数量  423
分享查询网卡所在PCI插槽链路能力及当前链路状态  420
麒麟系统进行内存清理  414
麒麟系统资源下载合集(适配各类cpu)  413
统信UOS常见问题小总结  411
winrar绿色无广告版分享  394
最近下载排行榜
分享如何统信UOS系统在屏蔽mysql显性的用户名称以及密码 0
分享免费开源高速下载器 0
分享如何在银河麒麟高级服务器操作系统V10SP3中需要启用内核审计功能。 0
通过shell脚本在统信UOS/麒麟系统中安装nginx 0
分享如何查看网卡中断的数量 0
分享查询网卡所在PCI插槽链路能力及当前链路状态 0
麒麟系统进行内存清理 0
麒麟系统资源下载合集(适配各类cpu) 0
统信UOS常见问题小总结 0
winrar绿色无广告版分享 0
作者收入月榜
1

prtyaa 收益395.97元

2

zlj141319 收益228.92元

3

IT-feng 收益215.07元

4

1843880570 收益214.2元

5

风晓 收益208.24元

6

777 收益173.02元

7

哆啦漫漫喵 收益131.6元

8

Fhawking 收益106.6元

9

信创来了 收益105.97元

10

克里斯蒂亚诺诺 收益91.08元

请使用微信扫码

加入交流群

请使用微信扫一扫!