klitsche/ffigen

FFI绑定生成器

v0.8.1 2023-01-21 00:42 UTC

This package is auto-updated.

Last update: 2024-09-22 02:16:47 UTC


README

Build Status Test Coverage Maintainability Packagist Version

ffigen是一个简单的命令行助手,用于快速生成和更新用于C库的低级PHP FFI绑定。

它根据提供的C头文件生成两个PHP文件

  • constants.php - 存储常量值
  • Methods.php - 存储函数绑定作为静态方法,并在trait中包含phpdoc

它深受FFIMe的启发,并依赖于ircmaxell的PHPCParser

进行中:所有0.*预发布版本都可能发生破坏性更改。

要求

  • PHP ^7.4 || ^8.0
  • 例如:必须启用FFI扩展

快速入门

在你的项目中安装

composer require --dev klitsche/ffigen

安装一个c库(例如uuid)。

将配置文件添加到项目根目录

.ffigen.yml

修改此配置文件(示例)

headerFiles:
  - uuid/uuid.h
libraryFile: libuuid.so.1
parserClass: Klitsche\FFIGen\Examples\UUID\FFIGen\Parser
outputPath: ./
excludeConstants:
  - /^(?!(FFI|UUID)_).*/
excludeMethods:
namespace: Klitsche\FFIGen\Examples\UUID

可选:添加自己的Parser类来自定义预处理或后处理逻辑(示例)

<?php

declare(strict_types=1);

namespace Klitsche\FFIGen\Examples\UUID\FFIGen;

use Klitsche\FFIGen\Config;

class Parser extends \Klitsche\FFIGen\Adapter\PHPCParser\Parser
{
    public function __construct(Config $config)
    {
        parent::__construct($config);

        $this->context->defineInt('_SYS_TYPES_H', 1);
        $this->context->defineInt('_SYS_TIME_H', 1);
        $this->context->defineInt('_TIME_H', 1);
    }

    protected function parseHeaderFile(string $file): array
    {
        $file = $this->searchHeaderFilePath($file);

        $prependHeaderFile = '
            typedef long time_t;
        ';
        $tmpfile = tempnam(sys_get_temp_dir(), 'ffigen');
        file_put_contents($tmpfile, $prependHeaderFile . file_get_contents($file));

        $declarations = parent::parseHeaderFile($tmpfile);

        unlink($tmpfile);

        return $declarations;
    }

    private function searchHeaderFilePath(string $file): string
    {
        if (file_exists($file)) {
            return $file;
        }
        foreach ($this->context->headerSearchPaths as $headerSearchPath) {
            if (file_exists($headerSearchPath . '/' . $file)) {
                return $headerSearchPath . '/' . $file;
            }
        }

        throw new \RuntimeException(sprintf('File not found: %s', $file));
    }
}

别忘了在composer.json中注册Parser命名空间以进行自动加载(开发环境可以)

    "autoload-dev": {
        "psr-4": {
            "Klitsche\\FFIGen\\Examples\\": "examples"
        }
    },

使用以下命令进行自动加载

composer dump-autoload

运行ffigen生成绑定文件

vendor/bin/ffigen

这将生成输出路径中的两个文件

  • constants.php - 将此添加到你的自动加载中
  • Methods.php - 将此添加到你的自己的类上下文中,并在你的高级PHP库中使用它

别忘了将constants.php添加到composer.json中以进行自动加载

    "autoload": {
        "files": [
          "tweak-path-to/constants.php",
        ]
    },

尝试示例

构建带有预安装c库的docker镜像(uuid,snappy & librdkafka)

 docker-compose build php74

运行uuid示例

docker-compose run --rm php74 php bin/ffigen generate -c examples/UUID/.ffigen.yml
docker-compose run --rm php74 php examples/UUID/test.php

运行snappy示例(请参阅Snappy类以获取一个简单的示例)

docker-compose run --rm php74 bin/ffigen generate -c examples/Snappy/.ffigen.yml
docker-compose run --rm php74 php examples/Snappy/test.php

运行rdkafka示例(librdkafka 1.5.2 & 模拟集群)

docker-compose run --rm php74 bin/ffigen generate -c examples/RdKafka/.ffigen.yml
docker-compose run --rm php74 php examples/RdKafka/test.php

待办事项

  • 添加travis
  • 添加更多测试
  • 添加文档
  • 添加Windows,macOS的支持
  • 添加更多示例(并从中学习)
  • 考虑多版本支持
  • 考虑为类型生成自定义接口/类
  • 考虑添加clang / cpp / readelf适配器(cpp定义仅包含 & 清洁文件,clang -c11 ast-dump=json,readelf --dyn-syms)