PHP 的 FFI(Foreign Function Interface)扩展让我们可以直接在 PHP 中调用 C 语言的函数和数据结构,在处理性能敏感或者底层交互的场景非常有用。但是一旦你深入使用它,就会发现一些隐藏的“地雷”,比如——在 FFI::cdef() 里声明结构体时,如果你忘了写 struct 会发生什么?
假设我们有一个简单的 C 结构体:
typedef struct {
int x;
int y;
} Point;
我们用 PHP 的 FFI::cdef() 声明:
$ffi = FFI::cdef("
typedef struct {
int x;
int y;
} Point;
");
然后可以这样使用:
$point = $ffi->new("Point");
$point->x = 10;
$point->y = 20;
echo "Point: ({$point->x}, {$point->y})\n";
一切运行正常。但现在问题来了:如果你忘了写 struct,会发生什么?
C 语言中,struct Point 和 Point 是两个不同的名字空间。除非你用了 typedef,否则 Point 并不是一个有效的类型名。而在 PHP 的 FFI::cdef() 中,情况略有不同——它的解析规则和 C 编译器略有差异,容错性低。
比如你写了这样一段声明:
$ffi = FFI::cdef("
typedef struct Point {
int x;
int y;
} Point;
");
$pt = $ffi->new("Point");
这段代码是没问题的。但如果你粗心把 typedef 部分写漏了,比如这样:
$ffi = FFI::cdef("
struct Point {
int x;
int y;
};
");
$pt = $ffi->new("Point"); // 出错
这里 $ffi->new("Point") 会抛出异常,提示找不到类型“Point”。因为此时“Point”是 struct Point,而不是一个单独的类型别名。
正确的调用应该是:
$pt = $ffi->new("struct Point");
这就是很多人掉进去的坑:以为定义了结构体名字“Point”,其实你只定义了“struct Point”,没做 typedef 的话不能直接用 Point。
如果你看到类似下面的错误:
FFI\Exception: undefined C type 'Point'
那你首先要检查你在 cdef() 中到底是如何声明的。是不是只写了 struct Point 而没有 typedef?如果是这样,就只能用 struct Point 来引用。
一个调试技巧是,把你的 cdef() 声明打印出来检查结构体有没有 typedef,例如:
echo $ffi->cdef;
虽然这个方法不总是适用(尤其在某些版本的 PHP 中),但能帮助你确认类型是否正确注册。
为了避免混乱,建议你在所有结构体声明中都带上 typedef。例如:
typedef struct {
int x;
int y;
} Point;
这样你在 PHP 中就能愉快地写 $ffi->new("Point"),不用再纠结是否该加 struct 前缀。
假设你定义了一个结构体里嵌套另一个结构体:
typedef struct {
int x;
int y;
} Point;
typedef struct {
Point start;
Point end;
} Line;
PHP 中的 FFI::cdef() 应该这样写:
$ffi = FFI::cdef("
typedef struct {
int x;
int y;
} Point;
typedef struct {
Point start;
Point end;
} Line;
");
然后你可以像这样使用:
$line = $ffi->new("Line");
$line->start->x = 1;
$line->start->y = 2;
$line->end->x = 10;
$line->end->y = 20;
echo "Start: ({$line->start->x}, {$line->start->y})\n";
echo "End: ({$line->end->x}, {$line->end->y})\n";
如果你在声明中漏了 typedef,比如 Point 没 typedef,那么 Line 的定义就会报错,因为它找不到 Point 类型。
在使用 PHP FFI 调用 C 的结构体时,以下几点务必注意:
结构体未 typedef 的话,类型名必须加 struct 前缀
推荐所有结构体都使用 typedef 声明,避免命名混淆
调试时遇到 undefined type 错误,先查 cdef 中的声明是否一致
通过这些规范使用方式,可以减少大量莫名其妙的类型错误,让你在用 PHP 玩 C 接口的时候少踩坑、少掉发。
更多关于 PHP FFI 的示例可以参考 https://gitbox.net/docs/php-ffi-examples。