您好,登錄后才能下訂單哦!
本篇內容主要講解“Rust如何開發PHP擴展”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Rust如何開發PHP擴展”吧!
優點:
1、php擴展是C開發的,那速度沒得說。
2、耦合性高,它的出現就是用來增強php的。
3、安全性高,畢竟擴展是編譯后的程序,代碼不開源。
缺點:
1、需針對php版本及系統環境進行開發,那么就比較麻煩了。也就是說7.4版本的php,liunx環境下開發的擴展,只支持該php版本及系統。
2、需要會C、C++,當然本文是以rust進行開發,對C的數據類型進行了解,對rust FFI的操作及數據類型轉換需精通。
3、調試相對麻煩。
原因很簡單,這還要說起rust的語言特性。
1、因“所有權”的特性使你的程序更安全,不會像C那樣出現各種“玄學BUG”。
2、擁有C一樣的性能。
3、畢竟是最受歡迎的語言,我很看好它的發展。
當然,rust目前是沒有專門開發php擴展的骨架。所以我的邏輯也很簡單,利用rust開發靜態庫暴露給C【涉及FFI的了解】。我們在php官方骨架中直接引入rust靜態庫調用其方法即可。
開發環境
寶塔【CentOS 7.6】、GCC【涉及php擴展骨架的編譯,我這里系統內置就有,如果編譯擴展報相關錯了自行安裝】、對應php版本源碼、web環境【寶塔中安裝對應php版本、nginx、mysql等等】
開發整體流程:
1、準備寶塔
這里我們以開發php7.4擴展為例。
2、下載php7.4 liunx版源碼
php官網:PHP: Hypertext Preprocessor
注意!該源碼版本必須與你環境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-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文件
到此,相信大家對“Rust如何開發PHP擴展”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。