Skip to main content

Inleiding

PHP FFI (Foreign Function Interface) is een PHP-extensie waarmee je shared libraries laadt, C-functies aanroept en toegang krijgt tot C-datastructuren. Zonder zelf een PHP-extensie te bouwen, kun je bestaande C-API’s direct vanuit PHP gebruiken. De officiële PHP-documentatie beschrijft FFI als een low-level en gevaarlijke functie. Alleen ontwikkelaars die C en de API van de doelbibliotheek begrijpen, zouden het moeten gebruiken.
FFI is geen veilig geabstraheerde, gewone PHP-API. Fouten met pointers, ownership of vrijgavefuncties leiden tot crashes en geheugencorruptie.
FFI is ook geen “functie om dingen sneller te maken”. Ook de officiële PHP-documentatie legt uit dat toegang tot datastructuren via FFI trager is dan native PHP-arrays en -objecten. De reden om FFI te kiezen is niet snelheid, maar dat je een bestaande C-bibliotheek vanuit PHP wilt gebruiken.

Omgevingen en beperkingen van FFI

De officiële PHP-documentatie beschrijft het inschakelen van de FFI-extensie als volgt:
  • PHP bouwen met --with-ffi
  • Op Windows php_ffi.dll inschakelen in php.ini
  • Met ffi.enable in php.ini bepalen of FFI gebruikt mag worden
ffi.enable kent drie waarden:
In webserveromgevingen wordt vaak ffi.enable=false of ffi.enable=preload gebruikt. Ook in hostingomgevingen, inclusief Laravel Cloud, is FFI niet altijd bruikbaar in een gewone webapp.
Ook als je FFI in een Laravel-app wilt gebruiken, bekijk daarom eerst of het haalbaar is als CLI-tool of batchproces. Uitgaan van permanent ingeschakeld FFI onder FPM of Apache mod_php kun je beter vermijden.

Basisgebruik

De basis van PHP FFI bestaat uit vier onderdelen: FFI::cdef(), FFI::new(), FFI::addr() en FFI::string().
De belangrijkste punten in dit voorbeeld:
  • FFI::cdef() maakt een FFI-object van een string met C-declaraties en de naam van de shared library
  • $ffi->new('struct timeval') alloceert een C-datastructuur
  • FFI::addr($tv) maakt een pointerargument zoals struct timeval *
  • Je benadert structvelden als $tv->tv_sec
Bij API’s die strings of binaire data teruggeven, gebruik je FFI::string().
Geheugen dat je alloceert met FFI::new() wordt normaal vrijgegeven via de referentietelling van PHP. Pointers die de C-bibliotheek teruggeeft, moeten daarentegen soms worden vrijgegeven met een bibliotheekspecifieke free-functie. Controleer per API bij wie het ownership ligt.

Headerbestanden inladen

Met FFI::load() laad je declaraties uit een C-headerbestand. In het headerbestand kun je FFI_SCOPE en FFI_LIB opnemen.
De officiële PHP-documentatie legt echter uit dat zowel FFI::cdef() als FFI::load() geen gewone C-preprocessorinstructies ondersteunt. #include, #define en conditionele compilatie kun je niet zomaar doorgeven. In de praktijk kies je daarom meestal een van deze twee opties:
1

Bij een simpele API: direct in FFI::cdef() schrijven

Zijn er weinig afhankelijke types en functies, dan neem je alleen de benodigde declaraties op aan de PHP-kant.
2

Bij een complexe API: een voorbewerkte header maken

Beheer een aparte, FFI-specifieke header waaruit macro’s en conditionele compilatie uit de oorspronkelijke header zijn verwijderd.

Een echte use case: VOICEVOX Core for PHP

VOICEVOX Core for PHP is een praktijkvoorbeeld waarin de dynamische C-bibliotheek van VOICEVOX CORE vanuit pure PHP wordt gebruikt. Het bundelt de praktische FFI-patronen en is daardoor begrijpelijker dan abstracte uitleg.

1. Een voor FFI geschikt gemaakte header bijhouden

De oorspronkelijke header van VOICEVOX Core wordt niet direct gebruikt; in headers/voicevox_core_ffi.h zijn de declaraties voor FFI apart gezet. Daarin worden opaque pointers, structs en free-functies expliciet gemaakt.
Belangrijk in dit ontwerp is dat de gedetailleerde interne C-structuur niet aan de PHP-kant wordt getoond, maar wordt opgesloten in opaque handles en functieaanroepen. Het oppervlak dat je vanuit PHP moet beheren wordt kleiner en je wrapperklassen blijven overzichtelijk.

2. FFI::cdef() op één plek concentreren

src/VoicevoxFFI.php concentreert het inladen van de header en het bepalen van het bibliotheekpad op één plek.
In deze vorm hoeft je applicatie zelf FFI::cdef() niet aan te roepen. Ook verschillen in bibliotheekpaden vang je op met omgevingsvariabelen en OS-detectie.

3. Out parameters wrappen tot objecten

De constructor van src/Synthesizer.php alloceert een struct VoicevoxSynthesizer* en geeft het adres daarvan door aan voicevox_synthesizer_new().
Dit patroon is de basisvorm om een Type **out_value uit een C-API in PHP op te vangen:
  • Maak een pointervariabele met new('struct VoicevoxSynthesizer*')
  • Geef Type ** door met FFI::addr($ptr)
  • Bewaar de verkregen handle als property van een PHP-object

4. Door C teruggegeven geheugen naar een PHP-string kopiëren en direct vrijgeven

VOICEVOX Core for PHP kopieert JSON-strings en WAV-binaries na ontvangst eerst met FFI::string() naar een PHP-string en roept daarna de free-functie aan de C-kant aan.
Deze flow van “kopiëren en meteen vrijgeven” is een van de belangrijkste implementatiepatronen bij FFI. Door de buffer aan de C-kant niet te vermengen met de PHP-levenscyclus, houd je het ownership eenvoudig.

Aandachtspunten en best practices

Meng je FFI direct in de request/response-cyclus van een Laravel-app, dan worden omgevingsverschillen en het isoleren van storingen ineens veel lastiger. Laat het eerst werken in een CLI-commando of een los eenmalig proces buiten de queue.
FFI is geschikt wanneer er al een stabiele C-API bestaat en het bouwen van een eigen PHP-extensie te ver gaat. Het is juist ongeschikt voor functionaliteit die op gewone webhosting moet draaien of voor verwerking die volledig in PHP kan.

Samenvatting

Met PHP FFI kun je bestaande C-bibliotheken vanuit pure PHP aanroepen. Maar FFI is een low-level en gevaarlijk mechanisme, en het is niet altijd beschikbaar op een webserver. Kijk je naar de implementatie van VOICEVOX Core for PHP, dan blijken in de praktijk deze vier punten belangrijk:
  1. Houd een aparte header voor FFI bij
  2. Concentreer FFI::cdef() en het bepalen van het bibliotheekpad op één plek
  3. Wrap opaque pointers in speciale klassen
  4. Kopieer door C teruggegeven geheugen naar PHP en geef het daarna vrij met de speciale functie
Lees ook de pagina over VOICEVOX Core for PHP; zo kun je heen en weer tussen de FFI-concepten en het echte packageontwerp.
Laatst gewijzigd op 6 september 2026