ffi 的坑不在调用,而在定义和生命周期管理:符号不匹配、内存归属不清、abi约定不明、作用域错位及结构体对齐错误均会导致段错误或未定义行为。

FFI 不是“学完就能用”的语法糖,而是把 PHP 和 C 的边界直接撕开——没搞清内存归属、作用域和 ABI 约定前,segfault 和 undefined symbol 是常态。真要上手,得先接受一个事实:**FFI 的坑不在调用,而在定义和生命周期管理**。
FFI::cdef() 为什么总报 undefined symbol?
这不是 PHP 找不到函数,而是你写的 C 声明和实际符号不匹配。比如写 "int printf(const char *format, );",看着像头文件里的原型,但 FFI 不解析 #include 或宏,也不懂 __attribute__;它只认纯 C 语法 + 完整类型。
- 系统头文件(如
/usr/include/stdio.h)里复制粘贴最稳,删掉所有#define、#include、注释,只留函数签名和 typedef - 可变参数函数(如
printf)必须确保目标库 ABI 支持;libc.so.6可以,你自己编的.so很可能不支持 - 字符串字面量传入时,不能直接传 PHP 字符串变量;要用
FFI::string()或FFI::new('char[]', ...)分配内存再拷贝
Web 场景下 FFI 性能断崖式下跌?
FFI::cdef() 和 FFI::load() 是重操作,每次 HTTP 请求都执行一次,等于每秒重复解析 C 头文件 + 加载 so/dll —— 开销全在初始化阶段,不是调用慢。
- 必须预加载:在
opcache.preload中调用FFI::load("mylib.h") - 头文件开头加
#define FFI_SCOPE "mylib",否则FFI::scope("mylib")拿不到绑定 - 多个头文件可共用同一 scope,但不能有重复符号(比如两个头都定义了
struct point),否则预加载直接失败
传递结构体指针后程序崩溃?
根本原因是 PHP 默认创建的 FFI\CData 是 “owned”,即请求结束自动 free();而 C 函数可能长期持有该指针,下次访问就是野指针。
- 用
FFI::new('struct my_s', false, true):第二个参数false表示不 owned,第三个参数true表示 persistent(跨请求存活) - 结构体字段必须用
->field访问,数组字段才用[$i];别用clone或unset(),FFI\CData不支持 - 若需手动释放,用
FFI::free($ptr),但仅限owned=false且你确认 C 侧已不再使用该内存时
FFI::scope() 找不到函数或类型?
这是作用域错位最典型的症状。预加载时没声明 scope,或运行时用了错误的 name,都会导致绑定丢失。
- 检查头文件是否以
#define FFI_SCOPE "xxx"开头,且没有拼写错误(大小写敏感) - 确认
opcache.preload确实执行了FFI::load("xxx.h"),可通过php -r "var_dump(FFI::scope('xxx'));"在 CLI 下验证 - 不要混用
FFI::cdef()和FFI::scope():前者每次新建实例,后者复用预加载绑定;两者作用域互不影响
free,什么时候必须 persistent。写错一行声明,就可能让整个 worker 进程崩掉。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











