
PHP 8.1 中使用 FFI 加载 .so 动态库时,若出现 “Failed resolving C function” 错误,通常源于头文件中误用了 FFI_SCOPE 和 FFI_LIB 宏定义——这些宏仅在特定声明模式下生效,不应与 FFI::cdef() 的显式路径参数混用。
php 8.1 中使用 ffi 加载 `.so` 动态库时,若出现 “failed resolving c function” 错误,通常源于头文件中误用了 `ffi_scope` 和 `ffi_lib` 宏定义——这些宏仅在特定声明模式下生效,不应与 `ffi::cdef()` 的显式路径参数混用。
在使用 PHP FFI 时,有两种等效但互斥的 API 模式:宏驱动模式(declarative) 和 显式声明模式(explicit)。你当前的代码混合了二者,导致 FFI 解析器行为异常。
❌ 错误写法分析
你的 get_int.h 中包含:
#define FFI_SCOPE "GETINT" #define FFI_LIB "./getint.so" int getint(int val);
同时在 PHP 中又调用:
$ffi = \FFI::cdef(
file_get_contents('/srv/www/myapp/libs/get_int.h'),
"/srv/www/myapp/libs/getint.so"
);这会产生冲突:
立即学习“PHP免费学习笔记(深入)”;
-
FFI_LIB宏仅在 未传入第二参数(library path) 时才被 FFI 解析并生效; - 一旦你在
FFI::cdef()中显式传入.so路径,FFI 就会忽略头文件中的FFI_LIB,但仍会尝试解析FFI_SCOPE并查找对应符号表——而你并未定义该 scope 的绑定逻辑,最终导致getint符号无法解析。
✅ 正确做法(推荐显式模式)
移除头文件中所有 FFI_* 宏,仅保留纯 C 函数声明:
get_int.h(修正后):
// 仅声明函数,不含任何 FFI 特定宏 int getint(int val);
get_int.c(保持不变):
#include "get_int.h" // 注意路径一致性
int getint(int val) {
return val;
}编译命令(确保导出符号可见):
gcc -shared -fPIC -o getint.so get_int.c # ✅ 关键:无需额外链接选项,但需确保函数未被隐藏(默认可见)
index.php(路径需绝对且可读):
<?php
declare(strict_types=1);
// 确保路径正确、SO 文件存在且有执行权限
$soPath = '/srv/www/myapp/libs/getint.so';
if (!is_readable($soPath)) {
throw new RuntimeException("SO library not readable: $soPath");
}
$ffi = \FFI::cdef(
file_get_contents('/srv/www/myapp/libs/get_int.h'),
$soPath
);
$result = $ffi->getint(123);
var_dump($result); // int(123)⚠️ 注意事项
-
符号可见性:Linux 下默认函数为
global可见,但若使用-fvisibility=hidden编译,需加__attribute__((visibility("default")))显式导出; -
路径权限:Web 服务器用户(如
www-data)必须对.so文件具有读取和执行权限; -
架构匹配:
.so必须与 PHP 运行架构一致(如均为x86_64); -
调试技巧:用
nm -D getint.so | grep getint验证符号是否导出(应显示T getint)。
遵循“声明归声明、加载归加载”的分离原则,即可避免绝大多数 FFI 符号解析失败问题。



















