ffi / c-parser
PHP FFI-compatible ISO/IEC 9899:2024 (С23) parser implementation
Requires
- php: ^8.1
- phplrt/exception: ^4.2
- phplrt/lexer: ^4.2
- phplrt/parser: ^4.2.3
- symfony/polyfill-php85: ^1.43
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpat/phpat: ^0.12
- phplrt/compiler: ^4.2.4
- phpstan/phpstan: ^2.3
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^10.5|^11.0|^12.0|^13.0
- symfony/var-dumper: ^6.4|^7.4|^8.0
Suggests
- ext-bcmath: Speeds up parsing of integer constants that do not fit in a PHP integer
- ext-gmp: Speeds up parsing of integer constants that do not fit in a PHP integer
- ext-mbstring: Speeds up decoding of universal character names in string literals
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-19 00:30:36 UTC
README
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.