PHP

FFI扩展调用C的so动态库

Royal
2023-07-07 / 0 评论 / 243 阅读 / 正在检测是否收录...

什么是FFI?
此扩展允许在纯PHP中加载共享库(.DLL或.so),调用C函数以及访问C数据结构,而无需深入了解Zend扩展API。
这个扩展需要安装 libffi libffi-devel库,编译安装步骤具体步骤就不说了(phpize、configure、make、make install四部曲),另外此扩展要求PHP版本必须是(PHP 7 >= 7.4.0, PHP 8),安装完通过php -m就可以看到下面的模块。
ljs00sn3.png

FFI的API都有哪些?

  • FFI::addr— 创建指向C数据的非托管指针
  • FFI::alignof— 获取对齐方式
  • FFI::arrayType— 动态构造一个新的C数组类型
  • FFI::cast— 执行C类型转换
  • FFI::cdef— 创建一个新的FFI对象
  • FFI::free— 释放非托管数据结构
  • FFI::isNull— 检查FFI \ CData是否为空指针
  • FFI::load— 从C头文件加载C声明
  • FFI::memcmp— 比较内存区域
  • FFI::memcpy— 将一个存储区复制到另一个
  • FFI::memset— 填充内存区域
  • FFI::new— 创建一个C数据结构
  • FFI::scope— 在预加载期间使用解析的C声明实例化FFI对象
  • FFI::sizeof— 获取C数据或类型的大小
  • FFI::string— 从内存区域创建一个PHP字符串
  • FFI::type— 从C声明创建FFI \ CType对象
  • FFI::typeof— 获取FFI \ CData的FFI \ CType

FFI怎么用?
直接上例子,下面就是通过一个实例来说明FFI怎么调用C的动态库函数。
首先通过FFI::cdef创建一个FFI对象,定义我们动态库中的常量、结构体、函数、以及通过地址加载我们的动态库。

          define('ECCref_MAX_BITS', 512);
        define('ECCref_MAX_LEN', (ECCref_MAX_BITS + 7) / 8);
        define("SGD_SM3", 0x00000001);

        $this->ffi = FFI::cdef(<<<EOH
        // 定义结构体
        typedef struct ECCrefPublicKey_st{
            unsigned int bits;
            unsigned char *x;
            unsigned char *y;
        } ECCrefPublicKey;

        // 定义打开设备函数
        int SDF_OpenDevice(void **phDeviceHandle);

        // 定义创建会话句柄函数
        int SDF_OpenSession(void *hDeviceHandle, void **phSessionHandle);

        // 定义生成随机数函数
        int SDF_GenerateRandom(void *hSessionHandle, unsigned int uiLength, unsigned char *pucRandom);

        // 定义杂凑运算初始化函数
        int SDF_HashInit(void *hSessionHandle, unsigned int uiAlgID, ECCrefPublicKey *pucPublicKey, unsigned char *pucID, unsigned int uiIDLength);
EOH
            , '/home/sangshuaidong/sdk/libcsapi.so');#加载动态库

然后,我们在方法中进行调用c函数。

public function openDevice()
    {
        $deviceHandlePtr = FFI::new("void*[1]");
        $result          = $this->ffi->SDF_OpenDevice($deviceHandlePtr);

        if ($result == 0) {
            $this->deviceHandle = $deviceHandlePtr[0];

            return true;
        } else {
            return false;
        }
    }

    public function openSession()
    {
        $sessionHandlePtr = FFI::new("void*[1]");
        $result           = $this->ffi->SDF_OpenSession($this->deviceHandle, FFI::addr($sessionHandlePtr));

        if ($result == 0) {
            $this->sessionHandle = $sessionHandlePtr[0];

            return true;
        } else {
            return false;
        }
    }

    // 获取16位随机数
    public function generateRandom($length)
    {
        $randomBuffer = FFI::new("unsigned char[$length]");
        $result       = $this->ffi->SDF_GenerateRandom($this->sessionHandle, $length, $randomBuffer);

        if ($result == 0) {
            $randomHex = bin2hex(FFI::string($randomBuffer, $length));

            return $randomHex;
        } else {
            return false;
        }
    }

    // hash杂凑运算初始化
    public function hashInit($algID = "SGD_SM3", $length = 0)
    {
        $result = 0;
        if ($algID === 'SGD_SM3') {
            $publicKey =$this->ffi->new("ECCrefPublicKey");
            $publicKey->bits = 0;
            $unsignedCharType = FFI::arrayType(FFI::type('unsigned char'), [ECCref_MAX_LEN]);

            $publicKey->x = FFI::cast('unsigned char*', FFI::new($unsignedCharType));
            $publicKey->y = FFI::cast('unsigned char*', FFI::new($unsignedCharType));


            $result = $this->ffi->SDF_HashInit(
                $this->sessionHandle,
                SGD_SM3,
                FFI::addr($publicKey),
                FFI::addr(FFI::new("unsigned char")),
                $length
            );
        } else {
            // 其他算法的处理,根据实际情况进行设置
        }

        if ($result == 0) {
            return true;
        } else {
            return false;
        }
    }

最后,我们通过执行可执行程序可以看到成功输出!
ljs0hz7s.png

1

评论

博主关闭了当前页面的评论