PHP调用交叉编译Go的c-shared库时遇_Fcomplex未定义问题
问题:PHP FFI加载Go生成的完整头文件时出现
_Fcomplex未定义错误 我正在开发一个同时使用PHP和Go的项目,需要在部署于Ubuntu 22.04 Docker环境的PHP应用中调用Go编写的函数。通过Docker从macOS交叉编译Go代码为c-shared库后,使用PHP FFI引入完整生成的头文件时,收到“第48行存在未定义C类型‘_Fcomplex’”的错误;但手动只定义导出函数时可以正常调用。
相关代码与编译命令
Go源码(main.go)
package main /** #cgo linux LDFLAGS: -Wl,-unresolved-symbols=ignore-all */import "C" import ( "fmt" ) //export print func print(out *C.char) { fmt.Println("[GO print] " + C.GoString(out)) } //export sum func sum(a C.int, b C.int) C.int { return a + b } func main() {}
生成的头文件(libutil.h)
/* Code generated by cmd/cgo; DO NOT EDIT. */ /* package Go */ #line 1 "cgo-builtin-export-prolog" #include <stddef.h> #ifndef GO_CGO_EXPORT_PROLOGUE_H #define GO_CGO_EXPORT_PROLOGUE_H #ifndef GO_CGO_GOSTRING_TYPEDEF typedef struct { const char *p; ptrdiff_t n; } _GoString_; #endif #endif /* Start of preamble from import "C" comments. */ /* End of preamble from import "C" comments. */ /* Start of boilerplate cgo prologue. */ #line 1 "cgo-gcc-export-header-prolog" #ifndef GO_CGO_PROLOGUE_H #define GO_CGO_PROLOGUE_H typedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; typedef unsigned short GoUint16; typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; typedef GoInt64 GoInt; typedef GoUint64 GoUint; typedef size_t GoUintptr; typedef float GoFloat32; typedef double GoFloat64; #ifdef _MSC_VER #include <complex.h> typedef _Fcomplex GoComplex64; typedef _Dcomplex GoComplex128; #else typedef float _Complex GoComplex64; typedef double _Complex GoComplex128; #endif /* static assertion to make sure the file is being used on architecture at least with matching size of GoInt. */ typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; #ifndef GO_CGO_GOSTRING_TYPEDEF typedef _GoString_ GoString; #endif typedef void *GoMap; typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; #endif /* End of boilerplate cgo prologue. */ #ifdef __cplusplus extern "C" { #endif extern void print(char* out); extern int sum(int a, int b); #ifdef __cplusplus } #endif
PHP报错调用方式
\FFI::cdef( file_get_contents($libPath . "libutil.h"), $libPath . "libutil.so" );
PHP正常调用方式
\FFI::cdef( "void print(char* p0); int sum(int p0, int p1);", $libPath . "libutil.so" );
交叉编译命令
docker run --rm -v "$PWD":/Go -w /Go -e GOOS=linux -e GOARCH=amd64 golang:1.20 env CGO_ENABLED=1 go build -v --buildmode=c-shared -o libutil.so
解决方案
原因分析
生成的头文件中,针对_MSC_VER(Windows MSVC编译器)定义了_Fcomplex和_Dcomplex类型,但PHP FFI的预处理器不会识别_MSC_VER宏,同时也没有自动引入<complex.h>,导致这些类型未定义。而实际运行环境是Ubuntu(GCC环境),这部分针对MSVC的代码完全不需要。
解决方法
方法1:修改生成的头文件
直接编辑libutil.h,删除MSVC相关条件编译代码:
#ifdef _MSC_VER #include <complex.h> typedef _Fcomplex GoComplex64; typedef _Dcomplex GoComplex128; #else typedef float _Complex GoComplex64; typedef double _Complex GoComplex128; #endif
替换为GCC环境的直接定义:
typedef float _Complex GoComplex64; typedef double _Complex GoComplex128;
方法2:使用自定义极简头文件
手动创建go_export.h,仅保留需要调用的函数声明:
extern void print(char* out); extern int sum(int a, int b);
PHP调用时加载该文件:
\FFI::cdef( file_get_contents($libPath . "go_export.h"), $libPath . "libutil.so" );
方法3:编译时优化(辅助)
Go的c-shared模式默认生成包含所有Go类型的头文件,无法直接避免,但可以确保代码中只导出必要函数,减少冗余内容。
验证
修改后重新调用PHP代码,测试print和sum函数是否正常执行,确认无类型错误。
内容的提问来源于stack exchange,提问作者Martin Bende
相关产品推荐
相关产品推荐

