Backend Development 9 min read

Using PHP FFI to Call C Libraries: libbloom, Linux Namespaces, and raylib Examples

The article introduces PHP's FFI extension, explains how to call C libraries such as libbloom, manipulate Linux namespaces, and use raylib for graphics, provides complete code examples, and discusses performance considerations, offering a practical guide for backend developers.

Laravel Tech Community
Laravel Tech Community
Laravel Tech Community
Using PHP FFI to Call C Libraries: libbloom, Linux Namespaces, and raylib Examples

FFI (Foreign Function Interface) is a PHP core extension introduced in PHP 7.4 that enables calling C code directly from PHP.

The extension is simple to use: declare the C structures and functions with FFI::cdef , then invoke them through the returned $ffi object.

Libbloom Example

libbloom is a C implementation of a Bloom filter. The following code shows how to declare the required structures and functions and use them from PHP.

$ffi = FFI::cdef(
    "    struct bloom {
        int entries;
        double error;
        int bits;
        int bytes;
        int hashes;
        double bpe;
        unsigned char * bf;
        int ready;
    };

    int bloom_init(struct bloom * bloom, int entries, double error);
    int bloom_check(struct bloom * bloom, const void * buffer, int len);
    int bloom_add(struct bloom * bloom, const void * buffer, int len);
    void bloom_free(struct bloom * bloom);
    ", "libbloom.so.1.5");

After the declaration, create a bloom structure and call the library functions:

// Create a bloom struct and get its address
$bloom = FFI::addr($ffi->new("struct bloom"));

// Initialize the bloom filter
$ffi->bloom_init($bloom, 10000, 0.01);

// Add some data
$ffi->bloom_add($bloom, "PHP", 3);
$ffi->bloom_add($bloom, "C", 1);

// Check membership
var_dump($ffi->bloom_check($bloom, "PHP", 3));   // 1
var_dump($ffi->bloom_check($bloom, "Laravel", 7)); // 0

// Free resources
$ffi->bloom_free($bloom);
$bloom = null;

Linux Namespace Example

Linux namespaces are a foundation of container technology. Using FFI, PHP can invoke the corresponding glibc system calls to create an isolated environment.

const CLONE_NEWNS     = 0x00020000; // mount namespace
const CLONE_NEWCGROUP = 0x02000000; // cgroup namespace
const CLONE_NEWUTS    = 0x04000000; // utsname namespace
const CLONE_NEWIPC    = 0x08000000; // ipc namespace
const CLONE_NEWUSER   = 0x10000000; // user namespace
const CLONE_NEWPID    = 0x20000000; // pid namespace
const CLONE_NEWNET    = 0x40000000; // network namespace

const MS_NOSUID  = 2;
const MS_NODEV   = 4;
const MS_NOEXEC  = 8;
const MS_PRIVATE = 1 << 18;
const MS_REC     = 16384;

Declare the needed libc functions:

$cdef = "
    int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
    int mount(const char *source, const char *target, const char *filesystemtype,
        unsigned long mountflags, const void *data);
    int setgid(int gid);
    int setuid(int uid);
    int sethostname(char *name, unsigned int len);
";
$libc = FFI::cdef($cdef, "libc.so.6");

Define the child process that sets up the container environment:

$containerId = sha1(random_bytes(8));

$childfn = function() use ($libc, $containerId) {
    usleep(1000); // wait for uid/gid map
    $libc->mount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, null);
    $libc->setuid(0);
    $libc->setgid(0);
    $libc->sethostname($containerId, strlen($containerId));
    pcntl_exec("/bin/sh");
};

Spawn the child process with the appropriate namespace flags and set up UID/GID mappings:

$child_stack  = FFI::new("char[1024 * 4]");
$child_stack = FFI::cast('void *', FFI::addr($child_stack)) + 1024 * 4;

$pid = $libc->clone($childfn, $child_stack,
    CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS |
    CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWCGROUP | SIGCHLD, null);

$uid = getmyuid();
$gid = getmyuid();
file_put_contents("/proc/$pid/uid_map", "0 $uid 1");
file_put_contents("/proc/$pid/setgroups", "deny");
file_put_contents("/proc/$pid/gid_map", "0 $gid 1");

pcntl_wait($pid);

The child process runs a shell inside its own PID, network, and mount namespaces, demonstrating a minimal container built entirely from PHP.

raylib Example

raylib is a feature‑rich game library. The following PHP code, wrapped with FFI, creates a window and draws a circle that follows the mouse cursor.

// Initialize
RayLib::init(); // set up FFI and constants
RayLib::InitWindow(400, 300, "raylib example");

// Ball position state
$ballPosition = RayLib::Vector2(-100.0, 100.0);

// Main loop
while (!RayLib::WindowShouldClose()) {
    // Update state
    $ballPosition = RayLib::GetMousePosition();
    // Render
    RayLib::BeginDrawing();
    RayLib::ClearBackground(RayLib::$RAYWHITE);
    RayLib::DrawCircleV($ballPosition, 40, RayLib::$RED);
    RayLib::DrawFPS(10, 10);
    RayLib::EndDrawing();
}

// Cleanup
RayLib::CloseWindow();

Limitations

Performance: although C libraries are fast, the overhead of FFI calls can be significant—FFI access is roughly twice as slow as native PHP array/object access, and even with JIT it does not match pure PHP performance, as shown by the benchmark in the original article.

FFI does not support the C pre‑processor (except for FFI_LIB and FFI_SCOPE ), so macros must be expanded manually.

Overall, PHP FFI opens powerful possibilities for rapid prototyping with existing C libraries, but developers should weigh the convenience against the runtime cost.

backend developmentFFIPHPLinux NamespacesC# Integrationraylib
Laravel Tech Community
Written by

Laravel Tech Community

Specializing in Laravel development, we continuously publish fresh content and grow alongside the elegant, stable Laravel framework.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.