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

PHP 8.3通过FFI调用Pascal DLL时如何持久化全局句柄?

问题解答

首先明确:PHP传统请求模式(比如PHP-FPM、CGI)下,全局变量没法跨请求保留FFI实例。

原因很简单:PHP的每个请求都是独立的执行上下文,就算用PHP-FPM的进程池,每个进程的内存空间也是完全隔离的——你在A请求里初始化的FFI实例,只存在于处理A请求的进程内存中,B请求大概率会落到另一个进程;就算凑巧落到同一个进程,之前的全局变量也可能因为进程复用的清理机制被重置,根本没法稳定复用。

你遇到的Session问题本质也是一样:FFI实例和指针是绑定当前进程内存空间的,序列化后存到Session里,另一个请求加载时,指针指向的内存地址在新进程里是完全无效的,自然会触发访问冲突。

给你几个可行的解决思路:

1. 用常驻进程框架(推荐)

如果业务场景允许,改用Swoole、Workerman这类常驻进程框架。这类框架会在进程启动时初始化一次FFI实例,之后所有请求都在同一个进程(或固定的进程池)里处理,FFI实例可以直接存在全局变量或类的静态属性里,全程复用,不会有跨进程的问题。

示例伪代码:

// 进程启动时初始化
$ffi = FFI::cdef("
    void init_dll();
    void fill_data(char* data);
    void save_data();
", "your_dll.dll");
$ffi->init_dll();

// 请求处理时直接复用
$app->on('request', function($request, $response) use ($ffi) {
    if ($request->action === 'fill') {
        $ffi->fill_data($request->input);
    } elseif ($request->action === 'save') {
        $ffi->save_data();
    }
});

2. 把DLL状态序列化到外部存储

如果没法改框架,那你需要避免直接存FFI指针,而是把DLL中需要保留的业务状态数据(比如填充的输入内容、配置参数)提取出来,存到Redis、文件或者数据库里。下次请求时,重新加载DLL,再把这些数据传入DLL恢复状态,而不是复用之前的指针。

比如:

// 填充数据的请求
$ffi = FFI::cdef(/* 函数定义 */, "your_dll.dll");
$ffi->init_dll();
$ffi->fill_data($input);
// 提取DLL里的状态数据(需要DLL提供导出函数来获取)
$state = $ffi->get_dll_state();
// 存到Redis
$redis->set('dll_state', serialize($state));

// 保存数据的请求
$ffi = FFI::cdef(/* 函数定义 */, "your_dll.dll");
$ffi->init_dll();
// 从Redis取出状态并恢复
$state = unserialize($redis->get('dll_state'));
$ffi->set_dll_state($state);
$ffi->save_data();

当然这要求你的Pascal DLL能提供获取/设置状态的导出函数,如果DLL本身不支持,这个方案没法用。

3. 调整DLL为无状态或自管理状态

如果能修改DLL的代码,最好把它改成无状态调用——每次调用fill_data和save_data时,都传入唯一的上下文标识(比如用户ID、请求ID),让DLL自己把状态存在文件或者内存映射里,PHP只需要在调用时传递这个标识,不需要自己保留任何指针或实例。

这样不管PHP怎么重启进程,只要传入正确的标识,DLL就能找到对应的状态继续处理。


内容的提问来源于stack exchange,提问作者Renato Rubinho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 10:15:04