← All docs

FFI & Extern

Foreign Function Interface: calling C libraries, extern functions, globals, classes, and callbacks.

FFI lets elephc programs call C library functions directly, with automatic type marshalling.

Declaring extern functions

<?php
extern function abs(int $n): int;
extern function getpid(): int;

// With explicit library
extern "curl" function curl_easy_init(): ptr;

// Block syntax
extern "SDL2" {
    function SDL_Init(int $flags): int;
    function SDL_Quit(): void;
}

Supported C types

elephc typeC equivalentPassed in
intint64_t / longinteger argument register
floatdoublefloating-point argument register
boolint (0/1)integer argument register
stringchar* (auto null-terminated)integer argument register
ptrvoid*integer argument register
ptr<T>T*integer argument register
voidvoid (return only)
callablefunction pointerinteger argument register

Argument registers follow the selected target’s C ABI: AArch64 uses x0-x7 for integers/pointers and d0-d7 for doubles, while Linux x86_64 uses the System V registers (rdi, rsi, rdx, rcx, r8, r9 for integers/pointers and xmm0-xmm7 for doubles).

callable in an extern function signature is a special FFI-only path. The call site can provide either a string-literal elephc function name (resolved case-insensitively like PHP function names) or a callable descriptor. Descriptor callbacks are bound into generated C-ABI trampoline symbols, including values with closure captures, object receivers, late-static-binding context, or branch-selected descriptor state. The trampoline receives the C callback arguments, reloads the retained descriptor from global storage, invokes the descriptor’s runtime invoker, and casts the boxed result back to the C-compatible callback return type.

FFI callback descriptors are limited to fixed scalar/pointer signatures: int, float, bool, ptr, and void return values, with parameters drawn from int, float, bool, and ptr. string, arrays, objects, variadics, defaults, and by-reference callback parameters are rejected because ownership and temporary lifetime across a C callback boundary are not modeled safely yet. For C APIs without a userdata/context parameter, each generated trampoline owns one mutable descriptor slot for that callsite; registering a new descriptor at the same callsite replaces the previous slot owner.

String conversion

  • Calling C: elephc creates temporary null-terminated copy, frees after call
  • C returns string: elephc scans for \0, copies to owned storage

Extern calls support the declared parameter names:

<?php
extern function strcmp(string $left, string $right): int;

$left = ["a"];
echo strcmp(...$left, right: "b");

Argument expressions are evaluated in PHP source order, then elephc loads the resulting values into the target C ABI registers. This matters when positional, named, or spread arguments have side effects.

Declared extern functions can also be selected by dynamic PHP string callbacks such as call_user_func($cb, ...) and call_user_func_array($cb, $args). The runtime descriptor uses an extern invocation shape and a generated PHP-ABI wrapper; the wrapper performs the C ABI call after PHP argument normalization.

Callbacks

<?php
extern function signal(int $sig, callable $handler): ptr;

function on_signal($sig) {
    echo "caught signal " . $sig . "\n";
}

signal(15, "on_signal");

Descriptor-backed callbacks can carry state:

<?php
extern function signal(int $sig, callable $handler): ptr;

$delta = 3;
$handler = function (int $sig) use ($delta): void {
    echo $sig + $delta;
};

signal(15, $handler);

Callbacks must use C-compatible types only. No strings, arrays, variadic, defaults, or pass-by-reference.

Descriptor-backed callbacks are accepted when no environment is needed:

<?php
extern function signal(int $sig, callable $handler): ptr;

function on_signal(int $sig): void {
    echo $sig;
}

$handler = on_signal(...);
signal(15, $handler);

Extern globals

<?php
extern global ptr $environ;

Uses GOT-relative addressing. String globals auto-converted.

Extern classes (C structs)

<?php
extern class Point {
    public int $x;
    public int $y;
}

$p = ptr_cast<Point>(malloc(16));
$p->x = 10;
echo $p->x;   // 10

Flat sequential layout, no class_id/vtable. 8-byte alignment.

CLI linker flags

FlagDescription
--link LIB / -lLIBLink additional library
--link-path DIR / -LDIRAdd library search path
--framework NAMELink macOS framework

Libraries in extern "lib" {} blocks are linked automatically.