Introduction
PHP FFI (Foreign Function Interface) is a PHP extension for loading shared libraries, calling C functions, and accessing C data structures. You can use existing C APIs directly from PHP without building your own PHP extension. The official PHP documentation describes FFI as a low-level and potentially dangerous feature. Only developers who understand C and the target library API should use it. FFI is also not a feature for speed. The official PHP documentation explains that FFI data structure access is slower than native PHP arrays or objects. You choose FFI for interoperability with existing C libraries, not performance.Supported Environments and Restrictions
The official PHP documentation explains FFI enablement as follows.- Build PHP with
--with-ffi - Enable
php_ffi.dllinphp.inion Windows - Control availability with
ffi.enableinphp.ini
ffi.enable accepts these three values.
| Value | Meaning |
|---|---|
true | Enable the FFI API |
false | Disable the FFI API |
preload | Allow the FFI API only in CLI SAPI and preloaded files |
In web server environments,
ffi.enable=false or ffi.enable=preload is common. In hosting environments, including Laravel Cloud, FFI may not be available for regular web applications.mod_php.
Basic Usage
The core PHP FFI methods areFFI::cdef(), FFI::new(), FFI::addr(), and FFI::string().
FFI::cdef()creates an FFI object from a C declaration string and a shared library name$ffi->new('struct timeval')allocates a C data structureFFI::addr($tv)creates a pointer argument likestruct timeval *- You can access struct fields like
$tv->tv_sec
FFI::string().
Loading Header Files
WithFFI::load(), you can load declarations from a C header file. Header files can include FFI_SCOPE and FFI_LIB.
FFI::cdef() or FFI::load(). You cannot pass #include, #define, or conditional compilation directives as-is.
Because of this, production code usually chooses one of these approaches.
Use `FFI::cdef()` directly for simple APIs
If dependencies are small, embed only the declarations you need on the PHP side.
Real-world Use Case: VOICEVOX Core for PHP
VOICEVOX Core for PHP is a practical example of using the VOICEVOX CORE C dynamic library from pure PHP. It is a concrete reference for practical FFI patterns.1. Maintain an FFI-oriented header
Instead of using the original VOICEVOX Core header directly, declarations for FFI are extracted intoheaders/voicevox_core_ffi.h. This defines opaque pointers, structs, and free functions explicitly.
2. Centralize FFI::cdef() in one place
src/VoicevoxFFI.php centralizes header loading and library path resolution.
FFI::cdef() directly. Library path differences are also absorbed through environment variables and OS detection.
3. Wrap out parameters into objects
The constructor insrc/Synthesizer.php allocates struct VoicevoxSynthesizer* and passes its address into voicevox_synthesizer_new().
Type **out_value in PHP with a C API.
- Create a pointer variable with
new('struct VoicevoxSynthesizer*') - Pass
Type **viaFFI::addr($ptr) - Store the acquired handle in a PHP object property
4. Copy C-returned memory into PHP strings and free immediately
VOICEVOX Core for PHP receives JSON strings and WAV binary data, copies them into PHP strings withFFI::string(), and then calls the C-side free functions.
Cautions and Best Practices
| Perspective | Practical Point |
|---|---|
| Runtime environment | Design for CLI-first usage |
| Header management | Do not pass original headers directly; maintain FFI-shaped declarations separately |
| Library path | Make switching possible with absolute paths or environment variables |
| Memory management | Copy to PHP with FFI::string() and always call the library-specific free function |
| Wrapper design | Do not spread raw CData across the app; isolate it in dedicated classes |
| Compatibility | Check both PHP version changes and target library ABI changes |
Summary
With PHP FFI, you can call existing C libraries from pure PHP. However, FFI is a low-level and dangerous mechanism, and it may not always be available in web server environments. The VOICEVOX Core for PHP implementation highlights four practical priorities.- Prepare a dedicated header for FFI
- Centralize
FFI::cdef()and library path resolution - Wrap opaque pointers with dedicated classes
- Copy C-returned memory into PHP and free it with dedicated functions