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

PHP FFI技术问题:如何传递PHP类对象给C++ DLL并反向转换

PHP FFI 与 C++ DLL 交互:对象指针传递与回调处理

核心解决方案

直接利用PHP FFI的类型转换能力,实现PHP对象与C++ void*的双向映射,同时避免垃圾回收风险,确保操作原对象的属性。


1. 将PHP对象$this转为void*传递给C++

PHP对象底层对应zend_object结构体,FFI可以直接将对象实例转换为void*,无需序列化或额外映射。

PHP代码片段

// 将$this转换为void*指针
$userData = FFI::cast('void*', $this);
// 传递给C++的创建函数
$this->myClassInstance = $this->ffi->MyClass_Create($callback, $userData);

2. 回调函数中将void*转回PHP对象并调用方法

使用FFI的FFI::object()函数,将C++传回的void*(实际是zend_object*)还原为原PHP对象,直接操作其成员属性和方法。

PHP回调代码片段

$callback = function($userData) {
    // 将void*转回原PHP对象
    $obj = FFI::object($userData);
    // 直接调用对象成员方法或修改属性
    $obj->count1 += 1;
    $obj->count2 += 1;
};

完整示例代码

C++ DLL 代码(编译为MyDll.dll)

#include <iostream>
using namespace std;

// 定义回调函数类型
typedef void (*CallbackFunc)(void*);

// C++端存储回调与用户数据的结构体
struct MyClass {
    CallbackFunc callback;
    void* userData;
};

// 创建实例并保存回调和用户指针
extern "C" __declspec(dllexport) MyClass* MyClass_Create(CallbackFunc callback, void* userData) {
    MyClass* obj = new MyClass();
    obj->callback = callback;
    obj->userData = userData;
    return obj;
}

// 触发回调函数
extern "C" __declspec(dllexport) void MyClass_TriggerCallback(MyClass* obj) {
    if (obj && obj->callback) {
        obj->callback(obj->userData);
    }
}

// 释放C++实例
extern "C" __declspec(dllexport) void MyClass_Destroy(MyClass* obj) {
    if (obj) {
        delete obj;
    }
}

PHP 测试代码

<?php
class Test {
    public $count1 = 1;
    public $count2 = 0;
    private $ffi;
    private $myClassInstance;

    public function __construct() {
        // 加载C++ DLL并声明函数结构
        $this->ffi = FFI::cdef("
            typedef void (*CallbackFunc)(void*);
            struct MyClass;
            struct MyClass* MyClass_Create(CallbackFunc callback, void* userData);
            void MyClass_TriggerCallback(struct MyClass* obj);
            void MyClass_Destroy(struct MyClass* obj);
        ", "MyDll.dll");

        // 定义回调函数
        $callback = function($userData) {
            $obj = FFI::object($userData);
            $obj->count1 += 1;
            $obj->count2 += 1;
        };

        // 传递$this指针给C++
        $userData = FFI::cast('void*', $this);
        $this->myClassInstance = $this->ffi->MyClass_Create($callback, $userData);
    }

    // 触发C++端回调
    public function trigger() {
        $this->ffi->MyClass_TriggerCallback($this->myClassInstance);
    }

    // 销毁C++实例
    public function __destruct() {
        if ($this->myClassInstance) {
            $this->ffi->MyClass_Destroy($this->myClassInstance);
        }
    }
}

// 运行测试
$obj = new Test();
$obj->trigger();
echo "count1: {$obj->count1}, count2: {$obj->count2}\n";
// 输出结果:count1: 2, count2: 1
?>

关键注意事项

  1. 垃圾回收防护:确保PHP对象在C持有指针期间不会被回收。示例中通过类成员变量持有$this和C实例,保证对象生命周期与C++实例一致。如果需要长期持有多个对象,推荐使用SplObjectStorage(比普通数组更高效的对象存储容器)来维护引用。
  2. 类型安全:确保传递的void*确实指向有效的PHP对象,避免野指针访问导致崩溃。
  3. 性能优势:此方案无需序列化/反序列化或额外映射表,直接操作底层指针,性能远优于之前尝试的两种方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 01:35:29