使用 golang 开发 PHP 扩展
使用 golang 开发 PHP 扩展
环境
- golang go1.19.9 darwin/arm64
- Macos/Linux
- PHP8.1.11 编译安装
实战
PHP脚手架生成
进入PHP源码,使用命令
php ext/ext_skel.php --ext go2php
会在ext目录下生成go2php的文件夹
GO静态库生成
在go2php 文件下新建目录gomodule,使用命令
cd gomodule
go mod init gomodule
编写go源码
package main
import "C"
//export addNum
func addNum(a, b int) int {
return a + b
}
func main() {}
生成静态库文件
至于为什么不生成动态库,因为如果是动态库编译后这个文件不能删除,比较局限
go build -x -buildmode=c-archive -o libgomodule.a
此时会在gomodule文件夹生成libgomodule.h 和libgomodule.a 文件
config.m4文件修改
在PHP_NEW_EXTENSION上面添加
PHP_ADD_LIBRARY(stdc++, 1, GOLANG2PHP_SHARED_LIBADD)
PHP_ADD_LIBRARY_WITH_PATH(gomodule, ./lib, GOLANG2PHP_SHARED_LIBADD)
PHP_REQUIRE_CXX()
C扩展编写
自定义函数声明文件phpAddNum golang2php_arginfo.h
/*自定义方法phpAddNum的参数*/
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_phpAddNum, 0, 2, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, a, IS_LONG, 1, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, b, IS_LONG, 1, 0)
ZEND_END_ARG_INFO()
/*自定义方法phpAddNum*/
ZEND_FUNCTION(phpAddNum);
/*自定义方法phpAddNum加入zend_function_entry*/
static const zend_function_entry ext_functions[] = {
ZEND_FE(phpAddNum, arginfo_phpAddNum)
ZEND_FE_END
};
c文件修改 golang2php.c
/* golang2php extension for PHP */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"
#include "php_golang2php.h"
#include "golang2php_arginfo.h"
#include "lib/libgomodule.h" /* golang编译生成的头文件 */
/* 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
/* {{{ 自定义方法phpAddNum */
PHP_FUNCTION(phpAddNum)
{
zend_long a;
zend_long b;
ZEND_PARSE_PARAMETERS_START(0, 2)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(a)
Z_PARAM_LONG(b)
ZEND_PARSE_PARAMETERS_END();
// 调用golang中的函数
int res = addNum(a, b);
RETURN_LONG(res);
}
/* }}}*/
/* {{{ PHP_RINIT_FUNCTION */
PHP_RINIT_FUNCTION(golang2php)
{
#if defined(ZTS) && defined(COMPILE_DL_GOLANG2PHP)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
return SUCCESS;
}
/* }}} */
/* {{{ PHP_RINIT_FUNCTION */
PHP_MINIT_FUNCTION(golang2php)
{
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(golang2php)
{
php_info_print_table_start();
php_info_print_table_header(2, "golang2php support", "enabled");
php_info_print_table_end();
}
/* }}} */
/* {{{ golang2php_module_entry */
zend_module_entry golang2php_module_entry = {
STANDARD_MODULE_HEADER,
"golang2php", /* Extension name */
ext_functions, /* zend_function_entry */
PHP_MINIT(golang2php), /* PHP_MINIT - Module initialization */
NULL, /* PHP_MSHUTDOWN - Module shutdown */
PHP_RINIT(golang2php), /* PHP_RINIT - Request initialization */
NULL, /* PHP_RSHUTDOWN - Request shutdown */
PHP_MINFO(golang2php), /* PHP_MINFO - Module info */
PHP_GOLANG2PHP_VERSION, /* Version */
STANDARD_MODULE_PROPERTIES};
/* }}} */
#ifdef COMPILE_DL_GOLANG2PHP
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_GET_MODULE(golang2php)
#endif
编译
1. /usr/local/php8.1.11/bin/phpize #找到phpize的路径
2 ./configure --with-php-config=/usr/local/php8.1.11/bin/php-config #php-config的路径
3. make && make install #执行make操作
ini配置
extension=golang2php
测试
php -r "var_dump(phpAddNum(100,200));"
#看到返回300即为成功
int(300)
扩展
当golang中返回数据为引用时,需要在golang释放
decodeStr := string(body)
cString := C.CString(decodeStr)
encryptFileString[fileName] = cString
defer C.free(unsafe.Pointer(cString)) //释放
return cString
c与go它们的对应关系表
C 语言类型 | CGO 类型 | Go 语言类型 |
---|---|---|
char | C.char | byte |
singed char | C.schar | int8 |
unsigned char | C.uchar | uint8 |
short | C.short | int16 |
unsigned short | C.ushort | uint16 |
int | C.int | int32 |
unsigned int | C.uint | uint32 |
long | C.long | int32 |
unsigned long | C.ulong | uint32 |
long long int | C.longlong | int64 |
unsigned long long int | C.ulonglong | uint64 |
float | C.float | float32 |
double | C.double | float64 |
size_t | C.size_t | uint |