우리 모두 알고 있듯이 저는 PHP 사용자로서 ffmpeg 비디오 처리 도구를 호출하는 등 PHP의 제한된 기능에 당황스럽습니다. 그것을 운영하기 위한 특별한 확장은 없습니다. 무엇입니까? PHP 시스템 함수 호출을 사용하시나요? 오픈 소스 PHP 스크립트의 경우 이는 너무 안전하지 않습니다!
이번에는 PHP 선배로서 PHP 확장 개발을 고려하고 확장에서 ffmpeg의 동작을 구현하게 됩니다.
현재 c 사이트에는 Rust에서 PHP 확장을 개발하는 방법에 대한 기사가 거의 없습니다. 심지어 PHP 확장을 개발하는 과정에 대한 기사도 다루기가 매우 어렵습니다. 이 글을 쓰고 있어요! ! !
장점:
1. PHP 확장은 C로 개발되었으므로 속도가 놀랍습니다.
2. 높은 결합도, 외관은 PHP를 향상시키는 데 사용됩니다.
3. 높은 보안. 결국 확장 프로그램은 컴파일된 프로그램이고 코드는 오픈 소스가 아닙니다.
단점:
1. PHP 버전과 시스템 환경에 맞춰 개발해야 하는데 그게 더 번거롭습니다. 즉, PHP 버전 7.4와 Linux 환경에서 개발된 Extension은 이 PHP 버전과 시스템만을 지원합니다.
2. C와 C++를 알아야 합니다. 물론 이 글은 Rust로 작성되었습니다. C의 데이터 유형을 이해하고 Rust FFI의 연산 및 데이터 유형 변환에 능숙해야 합니다.
3. 디버깅은 상대적으로 번거롭습니다.
이유는 아주 간단하며, Rust의 언어적 특징에 대해서도 이야기하고 있습니다.
1. "소유권" 기능으로 인해 프로그램이 더 안전하고 C와 같은 다양한 "형이상학적 버그"가 발생하지 않습니다.
2.C와 동일한 성능을 가지고 있습니다.
3. 결국 가장 인기 있는 언어이고 저는 이 언어의 발전에 대해 매우 낙관적입니다.
물론 Rust에는 현재 PHP 확장 개발을 위한 특별히 제작된 뼈대가 없습니다. 그래서 내 논리도 매우 간단합니다. 나는 Rust를 사용하여 정적 라이브러리를 개발하고 이를 C[FFI 관련 지식]에 노출합니다. 공식 PHP 뼈대에 Rust 정적 라이브러리를 직접 도입하고 해당 메서드를 호출할 수 있습니다.
개발 환경
Pagoda [CentOS 7.6], GCC [여기 시스템에 내장된 PHP 확장 뼈대 컴파일 관련, 컴파일 확장에서 오류가 보고되면 직접 설치] , 해당 PHP 버전 소스 코드 , 웹 환경 [해당 PHP 버전, nginx, mysql 등을 파고다에 설치]
전체 개발 과정:
1. 파고다 준비
Pagoda 설치 프로세스: CentOS pagoda 구축(매우 상세함)_One Code Superman's Blog-CSDN Blog_centos Pagoda
여기에서는 php7.4 확장 개발을 예로 들어보겠습니다.
2.php7.4 liunx 버전 소스 코드를 다운로드하세요
php 공식 웹사이트: PHP: Hypertext Preprocessor
주의! 소스 코드 버전은 환경의 PHP 버전과 정확히 동일해야 합니다! ! !
다운로드 완료:
3. pagoda
/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-config make
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-musl cargo build --target x86_64-unknown-linux-musl --release
编译32位静态库
rustup target add i686-unknown-linux-musl cargo build --target i686-unknown-linux-musl --release
这里我们是64位系统。
会生成一个.a文件,该文件便是liunx支持的静态库文件。
生成支持C语言的胶水头文件【用于C调用该库需要写的函数声明,很方便】
创建cbindgen.toml文件
内容:
language = "C"
安装cbindgen,创建头文件。
cargo install --force cbindgen cbindgen --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_OPTIONAL Z_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 ZTS ZEND_TSRMLS_CACHE_DEFINE() # endif ZEND_GET_MODULE(hello) #endif
删除之前生成的扩展文件
重新生成扩展
phpize ./configure --with-php-config=/www/server/php/74/bin/php-config make
大小都变了,说明我们的静态库在里面了哈哈。
按上述使用扩展流程替换扩展
注意!替换扩展文件后要重启PHP哦,不然不生效!
7、测试rust开发的php扩展
网页测试
命令行测试
也可以通过php扩展骨架直接进行测试
编写要执行测试的扩展函数
--TEST-- hello_test2() Basic test --SKIPIF-- <?php if (!extension_loaded('hello')) { echo 'skip'; } ?> --FILE-- <?php hello_test1(); var_dump(hello_test2('5LiA56CB6LaF5Lq6')); ?> --EXPECT-- string(11) "Hello World" string(9) "Hello PHP"
扩展目录下直接输入:
make test
执行后 tests目录下输出了一个.out文件
是不是这样更方便了呢?
以上就是整体的开发流程,需要经通过的话还是多少要了解C语言、php扩展骨架、rust精通。
推荐学习:《PHP视频教程》
위 내용은 Rust(Liunx 버전)에서 PHP 확장을 개발하는 방법을 자세히 설명하는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!