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 。