Search by

ffi / c-parser

Serafim

PHP FFI-compatible ISO/IEC 9899:2024 (С23) parser implementation

dev-master / 1.0.x-dev 2026-10-19 00:19 UTC

This package is auto-updated.

Last update: 2026-10-19 00:30:36 UTC


README

PHP 8.1+ Latest Stable Version Latest Unstable Version Total Downloads License MIT

Tests

Parses C headers (the ones you pass to FFI::cdef()) into an AST. Useful for generating wrappers and stubs or checking a header before loading it.

Based on C23 specification: ISO/IEC 9899:2024 (N3220)

Installation

$ composer require ffi/c-parser

Usage

$parser = new FFI\Parser\Parser();

$declarations = $parser->parse('void move(int dx, int dy);');
// or $parser->parse(new SplFileInfo('header.h'));

echo $declarations[0]->name->value; // "move"

An invalid header throws FFI\Parser\Exception\ParserException, which points to the error in the source.

AST Transformers

The parser contains a set of built-in AST transformations. To use your own, pass it to the constructor:

// override transformers
$parser = new FFI\Parser\Parser(transformers: [
    new MyAstTransformer(),
]);

// or extend existing ones
$parser = new FFI\Parser\Parser(
    transformers: FFI\Parser\Transformer::getDefaultTransformers([
        new MyAstTransformer(),
    ]),
);

AST Validators

The parser checks the AST with validators after the transformations. To use your own, pass it to the constructor:

// override rules
$parser = new FFI\Parser\Parser(rules: [
    new MyValidatorRule(),
]);

// or extend existing ones
$parser = new FFI\Parser\Parser(
    rules: FFI\Parser\Validator::getDefaultRules([
        new MyValidatorRule(),
    ]),
);

Benchmarks

Name Speed AST Nodes Header Size
bzip2.h 0.007s 3869 24.1 KiB
cjson.h 0.003s 1556 6.7 KiB
egl.h 0.004s 2694 16.4 KiB
lua.h 0.013s 6916 39.3 KiB
sqlite3.h 0.017s 8944 41.6 KiB
zlib-no-markers.h 0.011s 6367 33.5 KiB
zlib.h 0.012s 6367 50.4 KiB
zstd.h 0.004s 2033 18.6 KiB
glfw.h 0.185s 81865 344.4 KiB
vulkan.h 0.311s 117375 1.05 MiB
windows.h 0.396s 140673 1.05 MiB

The small headers for tests are in tests/resources/headers.

Please note that these should not be considered benchmarks; results may vary depending on the environment and the PC being used. The measurements were taken in the following environment:

  • PHP 8.5 (with OPCache, with JIT 1205)
  • OS: Windows 11
  • CPU: AMD Ryzen 9 5900X
  • SSD: SanDisk Ultra II (240GB)

Extensions

List of supported keywords and instructions defined by various compilers (outside the ISO/IEC 9899:2024 standard).

Extension Example From
Attributes __attribute__((packed)) GCC, Clang
Declspec __declspec(dllimport) MSVC
Calling conventions __cdecl, __stdcall, __fastcall MSVC
Pointer modifiers __ptr64, __unaligned, __based(b) MSVC
Pointer authentication __ptrauth(k, a, d) Clang
Nullability _Nonnull, _Nullable Clang
Block pointers void (^cb)(int) Clang
Assembler labels int f(void) __asm__("_f"); GCC, Clang
Keyword spellings __const__, __inline__, __typeof__ GCC, Clang
__forceinline __forceinline int f(void); MSVC
__extension__ __extension__ typedef long long i64; GCC, Clang
Built-in calls __builtin_offsetof(struct S, x) GCC, Clang
Statement expressions ({ int y = 1; y; }) GCC, Clang
Range designators { [0 ... 3] = 1 } GCC, Clang
__alignof__ expressions __alignof__ x GCC, Clang
Literal suffixes 1i64, 1.0q, 1.0w MSVC, GCC
Empty struct and ; struct S {};, ; GCC, Clang

Limitations

The meaning of some code depends on whether a name is a typedef: (T)-x is a cast or a subtraction. The parser knows only the names declared in the header itself, a name from a header that is not included keeps the default reading: (size_t)-1 is a cast.