You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 19:58:21