使用 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
posted @ 2023-12-20 09:20  Scott_pb  阅读(363)  评论(0编辑  收藏  举报