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.
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.
Laravel Tech Community
Specializing in Laravel development, we continuously publish fresh content and grow alongside the elegant, stable Laravel framework.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.